屬性觀察者,用來監聽和響應屬性值的改變。在每次屬性值被設置新值時都會被調用,即便設置的新值跟屬性的當前值如出一轍。markdown
能夠添加屬性觀察者的屬性類型:ide
屬性觀察者經過下面的兩個函數來監聽:函數
willSet
:在值存儲前被調用。didSet
:在值存儲後調用。class Person {
var name: String = "jack" {
willSet(newName) {
print("newName == \(newName)")
}
didSet {
print("oldName == \(oldValue)")
}
}
}
let p = Person()
p.name = "rose" // name == jack;newName == rose name == rose;oldName == jack
複製代碼
上面的代碼定義了一個 Person 的類,該類包含一個 name 的存儲屬性。name 屬性添加了屬性觀察者,並實現了 willSet/didSet 兩個函數。spa
經過打印能夠看出,在給 name 屬性賦值以後,會先調用 willSet 打印 name == jack;newName == rose
,再調用 didSet 打印 name == rose;oldName == jack
。由此能夠看出 willSet
是在值存儲前被調用,而 didSet
是在值存儲後調用。code
須要注意的是,當調用構造函數的時候,不會觸發屬性觀察者。
詳見下面的代碼:orm
class Person {
var name: String {
willSet(newName) {
print("name == \(name);newName == \(newName)")
}
didSet {
print("name == \(name);oldName == \(oldValue)")
}
}
init(name: String) {
self.name = name
}
}
let p = Person(name: "rose")
複製代碼
上面的代碼中,將 name 屬性的初始值刪掉,並提供了一個構造函數。當調用 let p = Person(name: "rose") 這行代碼時,並無任何打印。說明調用構造函數並不會觸發屬性觀察者。
繼承
class Person {
var name: String
init(name: String) {
self.name = name
}
}
class Student: Person {
override var name: String {
willSet(newName) {
print("name == \(name);newName == \(newName)")
}
didSet {
print("name == \(name);oldName == \(oldValue)")
}
}
}
let stu = Student(name: "jack")
stu.name = "rose" // name == jack;newName == rose name == rose;oldName == jack
複製代碼
上面的代碼聲明瞭一個 Student 的類,繼承自 Person。並對Student 繼承 的 name 屬性添加了屬性觀察者。get
經過打印能夠看出,在給 name 屬性賦值以後,會先調用 willSet 打印 name == jack;newName == rose
,再調用 didSet 打印 name == rose;oldName == jack
。編譯器
雖然,調用父類構造函數不會觸發屬性觀察者,但若是在子類中修改屬性值,是會觸發屬性觀察者的。詳見下面的代碼:it
class Person {
var name: String {
willSet(newName) {
print("name == \(name);newName == \(newName)")
}
didSet {
print("name == \(name);oldName == \(oldValue)")
}
}
init(name: String) {
self.name = name
self.name = "Person, \(name)"
}
}
class Student: Person {
override init(name: String) {
super.init(name: name)
self.name = "Student, \(name)"
}
}
let p = Person(name: "mike") // 沒有任何打印
let stu = Student(name: "robin")
// name == Person, robin;newName == Student, robin
// name == Student, robin;oldName == Person, robin
複製代碼
能夠看到,在 Student 的構造器中添加 self.name = "Student, \(name)"
以後,就會觸發屬性觀察者。
而在 Person 的構造器中,不管添加多少 self.name = "Person, \(name)"
,都不會觸發屬性觀察者。
struct BodyInfo {
var height = 0
var weight = 0
}
class Person {
var name: String
var body = BodyInfo()
var info: BodyInfo {
get {
return BodyInfo(height: body.height + 1, weight: body.weight + 1)
}
set {
body.height = newValue.height + 1
body.weight = newValue.weight + 1
}
}
init(name: String) {
self.name = name
}
}
class Student: Person {
override var info: BodyInfo {
willSet(newInfo) {
print("newInfo = \(newInfo)")
}
didSet {
print("oldInfo = \(oldValue)")
}
}
}
let stu = Student(name: "jack")
stu.info = BodyInfo(height: 15, weight: 20)
// newInfo = BodyInfo(height: 15, weight: 20)
// oldInfo = BodyInfo(height: 1, weight: 1)
複製代碼
須要說明上面的代碼邏輯上狗屁不通,只是當作代碼說明🤣。
上述代碼中,Person 定義了一個 info 的計算屬性,而後 Student 中繼承了 info,並給它添加了屬性觀察者。
經過 stu.info = BodyInfo(height: 15, weight: 20) 的調用,能夠看到輸出的結果是符合預期的。
須要說明的是,不能再 Person 中給 info 添加屬性觀察者,由於 willSet/didSet 是不能和 get 同時出現的,感興趣的同窗能夠本身動手實踐一下。編譯器會報錯:'willSet' cannot be provided together with a getter
。
willSet
:在值存儲前被調用;didSet
:在值存儲後調用。