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