KVO即Key-Value-Observing,鍵值觀察,是觀察者模式的一種實現。KVO提供了一種機制可以方便的觀察對象的屬性。如:指定一個被觀察對象,當對象的某個屬性發生變化時,對象會得到通知,進而能夠作出相應的處理。在實際的開發中對於model與controller之間進行交流是很是有用的。controller一般觀察Model的屬性,view經過controller觀察Model對象的屬性。 然而,Model對象能夠觀察其餘Model對象(一般用於肯定依賴別人的值什麼時候改變)或甚至自身(再次肯定依賴別人的值什麼時候改變)。bash
一個簡單的例子說明了KVO如何在您的應用程序中發揮做用。 假設Person對象與Account對象交互,表示該人在銀行的儲蓄帳戶。 Person的實例可能須要知道Account實例的某些方面什麼時候發生變化,例如餘額或利率。ide
若是這些屬性是Account的公共屬性,那麼Person能夠按期輪詢賬戶以發現更改,但這固然是低效的,而且一般是不切實際的。 更好的方法是使用KVO,相似於當帳戶的屬性值發生更改時Preson接收到一箇中斷。ui
Automatic key-value observing is implemented using a technique called isa-swizzling.The isa pointer, as the name suggests, points to the object's class which maintains a dispatch table. This dispatch table essentially contains pointers to the methods the class implements, among other data. When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class. As a result the value of the isa pointer does not necessarily reflect the actual class of the instance. You should never rely on the isa pointer to determine class membership. Instead, you should use the class method to determine the class of an object instance.spa
KVO的實現依賴於OC強大的Runtime,上面文檔也說道,KVO是經過使用isa-swizzling技術實現的。debug
基本的流程是:當某個類的屬性對象第一次被觀察時,系統會在運行期動態地建立該類的一個子類,在這個子類中重寫父類中任何被觀察屬性的setter方法,子類在被重寫的setter方法內實現真正的通知機制。3d
例如:當 被觀察對象爲A時,KVO機制動態建立一個新類,名爲NSKVONotifying_A。該類繼承自對象A的類,而且重寫了觀察屬性的setter方法,setter方法會負責在調用原setter方法以前和以後,通知全部觀察者對象屬性值的改變狀況。指針
每一個類對象都有一個isa指針,該指針指向當前類,當一個類對象的屬性第一次被觀察,系統會偷偷將isa指針指向動態生成的派生類,從而在給被監聽屬性賦值時執行的是派生類的setter方法。code
子類setter方法:cdn
KVO的鍵值觀察通知機制依賴於NSObject的兩個方法,willChangeValueForKey:和didChangeValueForKey:,在進行存取數值先後分別調用這2個方法(由於咱們所監聽的對象屬性能夠獲取新值和舊值,因此屬性改變先後都會通知觀察者)。被觀察屬性發生改變以前,willChangeValueForKey:方法被調用,當屬性發生改變以後,didChangeValueForKey:方法會被調用,以後observeValueForKey:ofObject:change:context方法也會被調用。這裏要注意:重寫觀察屬性的setter方法是在運行時由系統自動執行的。並且蘋果還重寫了class方法,讓咱們誤認爲是使用了當前類,從而達到隱藏生成的派生類。爲了幫助理解過程,借用圖片一張: server
let player = AVPlayer(playerItem: playerItem)
// 第一步:監聽AVPlayer的timeControlStatus
player.addObserver(self, forKeyPath: "timeControlStatus", options: .new, context: nil)
// 第二步:重寫observeValue方法
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "timeControlStatus" {
guard let player = player else { return }
switch player.timeControlStatus {
case .playing:
observerDelegate?.playerStatusChangedAction(status: .playing)
case .paused:
observerDelegate?.playerStatusChangedAction(status: .paused)
default:
return
}
}
// 去除監聽者
deinit {
player?.removeObserver(self, forKeyPath: "timeControlStatus")
DocsLogger.debug("-----deinit-----")
}
複製代碼