有關KVO (Key-Value Observing)大家一定不會覺得陌生,常常被用來監聽某個對象屬性值的改變。那麼有關底層實作原理是需要來探讨的,今天就來說說KVO的基本使用以及實作原理。
什麼是KVO
KVO (Key-Value Observing) 是 Objective-C 對觀察者模式(Observer Pattern)的實作。當被觀察者對象的某個被觀察屬性發生變化時,觀察者對象會獲得通知。
蘋果官方文檔中對KVO的定義是
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.
從中可以看到并不想我們想象的那麼簡單,KVO的實作中使用了黑魔法 isa-swizzling,後面将描述其實作過程。
下面先來看KVO的基本使用。
KVO的基本使用
以下使用官方文檔中的Example為例(官方文檔)。
建立兩個類,Person類和Account類。假使一個Person對象擁有一個Account對象,代表一個人在銀行的儲蓄賬号。這樣,當銀行的收益和利率變化時這個人必須時刻注意到。在Account類中添加兩個公共屬性,如圖所示:
既然Account類的屬性是公共的,Person對象可以通過輪詢查找來擷取Account屬性值的變化,但顯然這樣效率低下。
1、為Person對象添加監聽對象,調用方法:addObserver:forKeyPath:options:context:。
2、在Person類中實作observeValueForKeyPath:ofObject:change:context:方法。這樣,當Account屬性值發生變化時就會調用此方法,将其值的變化通知過來。
3、最後,當不再希望收到通知時一定要在Person類銷毀時登出通知,調用removeObserver:forKeyPath:方法。
實作原理
KVO是基于Runtime實作的,其實作過程 ChenYilong (微網誌@iOS程式犭袁)在下面這張圖中畫的很明白。
當觀察一個對象的時候,運作期系統會動态建立一個新的類。
這個類繼承自該對象的原本的類,并重寫了被觀察屬性的 setter 方法。重寫的 setter 方法會負責在調用原 setter 方法之前和之後(被觀察屬性發生改變之前 willChangeValueForKey:方法會被調,被觀察的屬性發生改變之後didChangeValueForKey:方法則會被調用),通知所有觀察對象:值的更改。
最後通過 isa 混寫(isa-swizzling) 把這個對象的 isa 指針 ( isa 指針告訴 Runtime 系統這個對象的類是什麼 ) 指向這個新建立的子類,對象就神奇的變成了新建立的子類的執行個體。
感謝 ChenYilong 在《《招聘一個靠譜的 iOS》—參考答案(下)》中的精彩答案,節省了我很多時間去研究KVO的實作細節。
手寫KVO
這裡推薦Glow 技術團隊部落格上的一篇文章《如何自己動手實作 KVO》,文章裡關于KVO實作細節是我在網上看到最全的,而且附帶源碼。感興趣的同學可以去看看。這裡就不浪費空間貼大量代碼了。
KVO的不足
KVO一度被認為是非常不好的API,比如其 以字元串作為關鍵字、remove observer不當會導緻crash,《Key-Value Observing Done Right》、《KVO Considered Harmful》以及Mattt Thompson在《Key-Value Observing》中都強調了KVO的不足之處,有興趣可以去看下。
參考文章