value type & reference type 的 property observer
透過定義 Swift property observer 的 willSet & didSet,我們可以在 property 內容被設定時執行某段程式。
struct Dog {
var name: String
}
struct Baby {
var dog = Dog(name: "史努比") {
didSet {
print("小狗變了")
}
}
}
var cuteBaby = Baby()
cuteBaby.dog = Dog(name: "布丁狗")
結果
由於 cuteBaby.dog = Dog(name: "布丁狗")
利用 = 修改 property dog,所以毫無疑問地, dog 的 didSet 將被觸發執行。但如果我們修改小狗的名字呢 ? 此時 property 是 value type 還是 reference type 將深深影響 willSet & didSet 是否觸發。
1 當 property 是 value type,以 struct 定義的 Dog 為例
struct Dog {
var name: String
}
struct Baby {
var dog = Dog(name: "史努比") {
didSet {
print("小狗變了")
}
}
}
var cuteBaby = Baby()
cuteBaby.dog.name = "布丁狗"
cuteBaby.dog.name = "布丁狗"
將觸發 didSet,因為 Dog 是 value type,所以設定 Dog 的任何一個 property 都代表 property dog 改變了。
2 當 property 是 reference type,以 class 定義的 Dog 為例
class Dog {
var name: String
init(name: String) {
self.name = name
}
}
struct Baby {
var dog = Dog(name: "史努比") {
didSet {
print("小狗變了")
}
}
}
var cuteBaby = Baby()
cuteBaby.dog.name = "布丁狗"
didSet 不會觸發。因為 Dog 是 reference type,所以 cuteBaby.dog 儲存的是小狗物件的記憶體位置,cuteBaby.dog.name = "布丁狗"
改變的是 小狗物件的 name,而不是寶寶物件的 dog。
在 UIView 的 height 改變時,更新圓角的 cornerRadius。
最後讓我們看一個開發 iOS App 時常見的例子。我們希望做個圓角的 button,當 button 高度改變時,cornerRadius 自動更新為高度的一半。
import UIKit
class RoundButton: UIButton {
override var frame: CGRect {
didSet {
layer.cornerRadius = frame.height / 2
}
}
}
let button = RoundButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
button.setTitle("Swift", for: .normal)
button.backgroundColor = UIColor.orange
button
button.frame.size.height = 100
button
由於 frame 的型別 CGRect 是 struct 定義的 value type,而 size 的型別 CGSize 也是 value type,所以 button.frame.size.height = 100
將改變 frame,觸發 frame 的 didSet 執行。
剛剛的程式在 Auto Layout 時也能發揮效果,因為當 Auto Layout 依據我們設定的條件計算出元件的 frame 後,也會觸發 frame 的 didSet。