在 Struct 和 Class 中介紹到屬性,屬性是一種數值,可以是任何型別,被放在物件中來存取,但是屬性的設定及種類不單只是前幾章介紹的 Stored Property 可以來儲存資料,還有其他種類的屬性:
Stored Property
Computed Property
Type Property
Property Observer
今天就先來介紹 Stored Property
與 Computed 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
宣告為常數,常數屬性只有在設定初始值時能指派值,但就是不能再透過實體來修改,否則就會出錯
奇怪明明我在 Struct 中 name
屬性是宣告成一個變數,為什麼不能夠過實體來更改值?
如果用 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,這種屬性只有在第一次呼叫時,才會真正初始化,舉個例子:
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 屬性並不會改變,而且這裡要注意的是
有別於 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
中的程式碼,在裡面,我們將面積開根號並且指配給邊長
,所以這時候邊長就有值了。
有沒有發現 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歲
在定義 Setter,我們會透過 Set(自定義參數名稱)
,讓我們在 Setter 中針對外部傳入的值指派到自定義的參數中,來傳遞及使用,但是我們也可以不定義這個參數,如果沒特別定義參數時,在 Setter 中就會自己生成一個變數 newValue
,負責來接收外部傳數的值。
import Foundation
class 正方形 {
var 邊長: Double = 0
var 面積: Double {
get {
邊長 * 邊長
}
set {
邊長 = sqrt(newValue)
}
}
}