iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 22
0
Software Development

給我 30 天,給你一輩子:Swift 從零開始系列 第 22

Day 22 | Swift Property 一家親:Stored Property 和 Computed Property

  • 分享至 

  • xImage
  •  

Property 屬性

在 Struct 和 Class 中介紹到屬性,屬性是一種數值,可以是任何型別,被放在物件中來存取,但是屬性的設定及種類不單只是前幾章介紹的 Stored Property 可以來儲存資料,還有其他種類的屬性:

  • Stored Property
  • Computed Property
  • Type Property
  • Property Observer

今天就先來介紹 Stored PropertyComputed Property

Stored Property

一般情況下,我們在物件裡宣告的變數或是常數,通常都是 Stored Property,用來儲存特定型別的值,必須在在初始化有值。

class People {
    var name: String
    let sex: String
    
    init(name: String, sex: String) {
        self.name = name
        self.sex = sex
    }
}

let andrew = People(name: "安竹", sex: "男生")
andrew.name = "安竹皮皮"

也因為當初在宣告 name 時,是用 var,所以 name 是一個變數,可以在建立實體後,再進行修改。

反之,sex 是用 let 宣告為常數,常數屬性只有在設定初始值時能指派值,但就是不能再透過實體來修改,否則就會出錯

https://ithelp.ithome.com.tw/upload/images/20200922/20118283Cwl6R55uYO.png

Struct 實體常數

奇怪明明我在 Struct 中 name 屬性是宣告成一個變數,為什麼不能夠過實體來更改值?

https://ithelp.ithome.com.tw/upload/images/20200922/20118283dFFDsARy2z.png

如果用 let 的方式來宣告實體常數,在初始化完成之後,就不能像 class 一樣透過 類別.屬性 來修改 Stored Property,因為 Struct 是 Value Type,那為什麼就 Value Type 不行?

當一個 Value Type 被宣告為常數時,其所有屬性都都會被視為常數,不管當初宣告使用 var 還是 let

除非使用 var 來宣告實體,就可以更改了:

struct People {
    var name: String
    let sex: String
}

var andrew = People(name: "安竹", sex: "男生")
andrew.name = "安竹皮皮"

但是一開始就被宣告為常數,理所當然就不能再更改其值。

Lazy Stored Property

什麼是 Lazy Stored Property,懶惰的儲存變數?

在還沒加上 Lazy 前綴字的 Stored Property,必須要有初始值,否則就是加上 Lazy,這種屬性只有在第一次呼叫時,才會真正初始化,舉個例子:

class People {
    let name = "安竹"
    let age = "23歲"
    let introduce = "姓名: \(self.name), 年齡: \(self.age)"
}

在還沒初始化前,self 都還尚未生成,所以這時候就會跳出 Use of unresolved identifier 'self' 錯誤,所以我們要不就是建立一個建構式,在裡面就可以使用 self:

class People {
    var name = "安竹"
    var age = "23歲"
    var introduce: String
    
    init(name: String, age: String) {
        self.introduce = "姓名: \(self.name), 年齡: \(self.age)"
    }
}

但是你也可以使用 lazy 來延遲儲存資料:

class People {
    var name = "安竹"
    var age = "23歲"
    lazy var introduce = "姓名: \(self.name), 年齡: \(self.age)"
}

也因為 lazy 只會執行一次,之後再用實體更改了其他屬性,lazy 屬性並不會改變,而且這裡要注意的是

  • lazy 不能用在常數上,因為常數不能使 lazy 在後來才更改屬性值
  • lazy 的屬性一定要在宣告的時候就給定初始值

Computed Property

有別於 Stored Property,Computed Property 不會先讀取值,而是透過 getter 來取值,同時又可以透過 setter 來設定其他屬性的值,直接來看個範例:

import Foundation
class 正方形 {
    var 邊長: Double = 0
    var 面積: Double {
        get {
            邊長 * 邊長
        }
        
        set(面積) {
            邊長 = sqrt(面積)
        }
    }
}

還記得 Closure 有說,如果是單行表達式時,就可以省略掉 return,這邊就是單行表達式,所以原本應該是要寫成 return 邊長 * 邊長

let square = 正方形()
square.邊長 = 5
print(square.面積) // 25.0

所以當我們建立一個正方形的實體,並指派邊長為 5,這時候在呼叫面積,就會去找到 getter,並且執行裡面的程式碼,再回傳給屬性本身。

let square = 正方形()
square.面積 = 169
print(square.邊長) // 13.0

假定我們指派直給面積,這時候就會去執行 setter 中的程式碼,在裡面,我們將面積開根號並且指配給邊長,所以這時候邊長就有值了。

冷知識 1:Computed Property 與 Lazy Property

有沒有發現 Computed Property 與 Lazy Property 作用有點相似,一樣都是後來呼叫才執行,但這裡有個小差別就是,當已經呼叫過 Lazy Property 後,再用實體去更改物件屬性時,Lazy 並不會跟著更動,但是 Computed Property 會。

class People {
    var name = "安竹"
    var age = "23歲"
    lazy var introduce = "姓名: \(self.name), 年齡: \(self.age)"
}

let andrew = People()
print(andrew.introduce)
// 姓名: 安竹, 年齡: 23歲

andrew.name = "皮皮"
print(andrew.introduce)
// 姓名: 安竹, 年齡: 23歲
class People {
    var name = "安竹"
    var age = "23歲"
    var introduce: String {
        get {
            "姓名: \(self.name), 年齡: \(self.age)"
        }
    }
}

let andrew = People()
print(andrew.introduce)
// 姓名: 安竹, 年齡: 23歲

andrew.name = "皮皮"
print(andrew.introduce)
// 姓名: 皮皮, 年齡: 23歲

冷知識 2:Setter 濃縮版

在定義 Setter,我們會透過 Set(自定義參數名稱),讓我們在 Setter 中針對外部傳入的值指派到自定義的參數中,來傳遞及使用,但是我們也可以不定義這個參數,如果沒特別定義參數時,在 Setter 中就會自己生成一個變數 newValue,負責來接收外部傳數的值。

import Foundation
class 正方形 {
    var 邊長: Double = 0
    var 面積: Double {
        get {
            邊長 * 邊長
        }
        
        set {
            邊長 = sqrt(newValue)
        }
    }
}

上一篇
Day 21 | Swift Class 與 Struct 快樂二選一:Struct 篇
下一篇
Day 23 | Swift Property 一家親:Type Property 和 Property Observer
系列文
給我 30 天,給你一輩子:Swift 從零開始30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言