一 、 KVO 的概述
KVO 的全稱 " Key-Value Observing "
KVO 是鍵值觀察機(jī)制,使得當(dāng)某個(gè)對象特定的屬性發(fā)生改變時(shí)能夠通知到別的對象。這經(jīng)常用于模型和控制器之間的通信。
KVO 的主要的優(yōu)點(diǎn)是你不需要在每次屬性改變時(shí)手動(dòng)去發(fā)送通知。并且它支持為一個(gè)屬性注冊多個(gè)觀察者。
二 、KVO 的使用條件
被觀察的對象,必須準(zhǔn)守鍵值編碼。
目前 KVO 支持的類型是 NSObject 。
被觀察的類能夠發(fā)出屬性改變的 KVO 的通知。
被監(jiān)控的類的屬性要使用 dynamic 來修飾。否則將不會(huì)調(diào)用下面方法:
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
- 被觀察的類要強(qiáng)引用,不能清除。
三 、 KVO 的注冊觀察者的方法及參數(shù)介紹
open func addObserver(_ observer: NSObject, forKeyPath keyPath: String, options: NSKeyValueObservingOptions = [], context: UnsafeMutableRawPointer?)
1、該方法的參數(shù)介紹
observer : 一個(gè)注冊的KVO 對象,必須滿足 KVC 編碼特性。
keyPath : 一個(gè)注冊的關(guān)鍵路徑,不能為空。
options : 指定包含在觀察是什么通知。
context : 這個(gè)參數(shù)可以是一個(gè) C 指針或者是一個(gè) 對象引用,它可以作為這個(gè)context的唯一標(biāo)識,也可以提供一些數(shù)據(jù)給觀察者。
?
2、NSKeyValueObservingOptions 的取值介紹
initial : 在注冊觀察者的方法 return 的時(shí)候就發(fā)出一次通知。
new :表示Options里面有新的值時(shí),發(fā)送一次通知。
old : 表示Options里面含所有屬性變化前的值。
prior : 會(huì)在值發(fā)生改變前發(fā)出一次通知,當(dāng)然改變后的通知依舊還會(huì)發(fā)出,也就是每次change都會(huì)有兩個(gè)通知。
四 、 KVO 響應(yīng)觀察者的方法及參數(shù)的介紹
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
該方法的參數(shù)介紹
keyPath : 注冊觀察者時(shí)的關(guān)鍵路徑。
object : 被注冊觀察者的對象。
change : change 是一個(gè)字典,它里面包含了的信息由注冊時(shí)的 options 決定。
context : 這個(gè)參數(shù)可以是一個(gè) C 指針或者是一個(gè) 對象引用,它可以作為這個(gè)context的唯一標(biāo)識,也可以提供一些數(shù)據(jù)給觀察者。
五、 KVO 的注銷方法及參數(shù)介紹
open func removeObserver(_ observer: NSObject, forKeyPath keyPath: String, context: UnsafeMutableRawPointer?)
或者
open func removeObserver(_ observer: NSObject, forKeyPath keyPath: String)
參數(shù)的介紹
observer :注冊的觀察者。
keyPath : 注冊觀察者時(shí)的關(guān)鍵路徑。
context :一個(gè)在注冊觀察者時(shí)的一個(gè) C 指針或者是一個(gè) 對象引用,它可以作為這個(gè)context的唯一標(biāo)識,也可以提供一些數(shù)據(jù)給觀察者。
六、 KVO 的使用舉例
1> 我們首先創(chuàng)建一個(gè) Person 類。
/**
創(chuàng)建一個(gè)類
*/
class Person : NSObject{
dynamic var name : String?
var firstName : String?
var lastName : String?
/**
獲取用戶的名字
*/
func getPersonName() -> String {
name = firstName! + lastName!
return name!
}
/**
反初始化
*/
deinit {
print("反初始化完成")
}
}
2> 注冊觀察者
/**
創(chuàng)建一個(gè)類
*/
NewPerson = Person.init()
/**
開始注冊觀察者
*/
NewPerson.addObserver(self, forKeyPath: "name", options: .new, context: &NewContext)
NewPerson.firstName = "周"
NewPerson.lastName = "NetWork小賤"
/**
獲取名字
*/
let NewName = NewPerson.getPersonName()
MyName = NewName
3> 觀察者的響應(yīng)事件
/**
觀察者方法的實(shí)現(xiàn)
*/
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath=="name" {
print("sss")
print(object!)
print(context!)
print(change!)
}else{
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
4> 當(dāng)我們觀察的類不存在該屬性的異常處理
如果Person 類中沒有我們要設(shè)定的這個(gè)屬性,則 KVC 也無法找到這個(gè)屬性值,這時(shí)候 KVC 協(xié)議其實(shí)會(huì)調(diào)用 valueForUndefinedKey 方法,NSObject 對這個(gè)方法的默認(rèn)實(shí)現(xiàn)是拋出一個(gè) NSUndefinedKeyException 異常。
該異常的解決方法
/**
沒有key時(shí)的異常處理
*/
override func value(forUndefinedKey key: String) -> Any? {
return ""
}
則最使用的方法是在觀察類中加入上面的方法,則觀察類的寫法如下:
/**
創(chuàng)建一個(gè)類
*/
class Person : NSObject{
dynamic var name : String?
var firstName : String?
var lastName : String?
/**
獲取用戶的名字
*/
func getPersonName() -> String {
name = firstName! + lastName!
return name!
}
/**
沒有key時(shí)的異常處理
*/
override func value(forUndefinedKey key: String) -> Any? {
return ""
}
/**
反初始化
*/
deinit {
print("反初始化完成")
}
}
七 、屬性觀察器的介紹(再次介紹)
1> 介紹
???????屬性觀察器相當(dāng)于內(nèi)建的KVO,監(jiān)控和響應(yīng)屬性值的變化,每次屬性被設(shè)置值的時(shí)候都會(huì)調(diào)用屬性觀察器,甚至新的值和現(xiàn)在的值相同的時(shí)候也不例外??梢詾槌搜舆t存儲屬性之外的其他存儲屬性添加屬性觀察器,也可以通過重載屬性的方式為繼承的屬性(包括存儲屬性和計(jì)算屬性)添加屬性觀察器。
2> 屬性檢查其的檢測方式
willSet在設(shè)置新的值之前調(diào)用。
didSet在新的值被設(shè)置之后立即調(diào)用。
注釋
willset觀察器會(huì)將新的屬性值作為固定參數(shù)傳入,在willSet的實(shí)現(xiàn)代碼中可以為這個(gè)參數(shù)指定一個(gè)名稱,如果不指定則參數(shù)仍然可用,這時(shí)使用默認(rèn)名稱newValue表示。類似地,didSet觀察器會(huì)將舊的屬性值作為參數(shù)傳入,可以為該參數(shù)命名或者使用默認(rèn)參數(shù)名oldValue。
注意
willSet和didSet觀察器在屬性初始化過程中不會(huì)被調(diào)用,它們只會(huì)當(dāng)屬性的值在初始化之外的地方被設(shè)置時(shí)被調(diào)用。
3> 舉例介紹
/**
屬性觀察器
*/
func attributeViewer() -> Void {
var PersonCount:Int = 0 {
willSet(newValue){
print("新人數(shù)是:" + "\(newValue)" + "個(gè)")
}
didSet(oldValue){
print("老人數(shù)是:" + "\(oldValue)" + "個(gè)")
}
}
/**
測試
*/
PersonCount = 10
}
測試結(jié)果圖
