KVO鍵值觀察運用及其原理

1、概念

KVO(Key-Value Observing),意爲鍵值觀察,它來源於設計者模式的觀察者模式,它的基本思想是:

一個目標對象管理所有依賴它的觀察者對象,並在它自身的狀態發生改變時主動通知其觀察者對象,這個主動通知通常是通過實現調用各觀察者所提供的方法。觀察者模式可以較完美的將目標對象和觀察者對象解耦,可達到低耦合的理想狀態。

2、原理

KVO在Apple中的API文檔如下:

Automatic key-value observing is implemented using a technique called isa-swizzling… 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 …

KVO 的實現依賴於 Objective-C 強大的 Runtime,從以上Apple 的文檔可以看出蘋果對於KVO機制的實現是一筆帶過,而具體的細節沒有過多的描述,但是我們可以通過Runtime的所提供的方法去探索,關於KVO機制的底層實現原理。

基本的原理:

當觀察某對象A時,KVO機制動態創建一個對象A當前類的子類,併爲這個新的子類重寫了被觀察屬性keyPath的setter 方法。setter 方法隨後負責通知觀察對象屬性的改變狀況。

深入剖析:

Apple 使用了 isa 混寫(isa-swizzling)來實現 KVO 。當觀察對象A時,KVO機制動態創建一個新的名爲: NSKVONotifying_A的新類,該類繼承自對象A的本類,且KVO爲NSKVONotifying_A重寫觀察屬性的setter 方法,setter 方法會負責在調用原 setter 方法之前和之後,通知所有觀察對象屬性值的更改情況。(備註: isa 混寫(isa-swizzling)isa:is a kind of ; swizzling:混合,攪合;)

①NSKVONotifying_A類剖析:在這個過程,被觀察對象的 isa 指針從指向原來的A類,被KVO機制修改爲指向系統新創建的子類 NSKVONotifying_A類,來實現當前類屬性值改變的監聽;

所以當我們從應用層面上看來,完全沒有意識到有新的類出現,這是系統“隱瞞”了對KVO的底層實現過程,讓我們誤以爲還是原來的類。但是此時如果我們創建一個新的名爲“NSKVONotifying_A”的類(),就會發現系統運行到註冊KVO的那段代碼時程序就崩潰,因爲系統在註冊監聽的時候動態創建了名爲NSKVONotifying_A的中間類,並指向這個中間類了。

(isa 指針的作用:每個對象都有isa 指針,指向該對象的類,它告訴 Runtime 系統這個對象的類是什麼。所以對象註冊爲觀察者時,isa指針指向新子類,那麼這個被觀察的對象就神奇地變成新子類的對象(或實例)了。) 因而在該對象上對 setter 的調用就會調用已重寫的 setter,從而激活鍵值通知機制。

—>我猜,這也是KVO回調機制,爲什麼都俗稱KVO技術爲黑魔法的原因之一吧:內部神祕、外觀簡潔。

②子類setter方法剖析:KVO的鍵值觀察通知依賴於 NSObject 的兩個方法:willChangeValueForKey:和 didChangevlueForKey:,在存取數值的前後分別調用2個方法:

被觀察屬性發生改變之前,willChangeValueForKey:被調用,通知系統該 keyPath 的屬性值即將變更;當改變發生後, didChangeValueForKey: 被調用,通知系統該 keyPath 的屬性值已經變更;之後, observeValueForKey:ofObject:change:context: 也會被調用。且重寫觀察屬性的setter 方法這種繼承方式的注入是在運行時而不是編譯時實現的。

3、拓展

1.與KVC的不同?
KVC(鍵值編碼),即Key-Value Coding,一個非正式的Protocol,使用字符串(鍵)訪問一個對象實例變量的機制。而不是通過調用Setter、Getter方法等 顯式的存取方式去訪問。
KVO(鍵值監聽),即Key-Value Observing,它提供一種機制,當指定的對象的屬性被修改後,對象就會接受到通知,前提是執行了setter方法、或者使用了 KVC賦值。

2.和notification(通知)的區別?
notification比KVO多了發送通知的一步。
兩者都是一對多,但是對象之間直接的交互,notification明顯得多,需要notificationCenter來做爲中間交互。而KVO如我們介紹的,設置觀察者->處理屬性變化,至於中間通知這一環,則隱祕多了,只留一句“交由系統通知”,具體的可參照以上實現過程的剖析。

notification的優點是監聽不侷限於屬性的變化,還可以對多種多樣的狀態變化進行監聽,監聽範圍廣,例如鍵盤、前後臺等系統通知的使用也更顯靈活方便。
3.與delegate的不同?
和delegate一樣,KVO和NSNotification的作用都是類與類之間的通信。但是與delegate不同的是:
這兩個都是負責發送接收通知,剩下的事情由系統處理,所以不用返回值;而delegate 則需要通信的對象通過變量(代理)聯繫;
delegate只是一對一,而這兩個可以一對多。

4.涉及技術:
KVC/KVO實現的根本是Objective-C的動態性和runtime,以及訪問器方法的實現;

總結:

對比其他的回調方式,KVO機制的運用的實現,更多的由系統支持,相比notification、delegate等更簡潔些,並且能夠提供觀察屬性的最新值以及原始值;但是相應的在創建子類、重寫方法等等方面的內存消耗是很巨大的。所以對於兩個類之間的通信,我們可以根據實際開發的環境採用不同的方法,使得開發的項目更加簡潔實用。
另外需要注意的是,由於這種繼承方式的注入是在運行時而不是編譯時實現的,如果給定的實例沒有觀察者,那麼KVO不會有任何開銷,因爲此時根本就沒有KVO代碼存在。但是即使沒有觀察者,委託和NSNotification還是得工作,這也是KVO此處零開銷觀察的優勢。

發佈了30 篇原創文章 · 獲贊 8 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章