KVC/KVO原理
KVC
KVC訪問屬性時儘可能嘗試使用存取方法,當KVC訪問屬性時,它內部其實做了很多事:
以一個屬性icon爲例
- 首先查找模型中有沒有
setIcon
方法,如果有有,直接調用[self setIcon:dict[@"icon"]];
- 如果找不到
set
方法,直接尋找有沒有icon
屬性,如果有,就直接訪問模型中icon = dict[@"icon"];
- 如果找不到
icon
屬性,就尋找_icon
屬性,如果有,直接_icon = dict[@"icon"]
- 如果都找不到,就會報錯
[<Flag 0x7xxxxx> setValue:forUndefinedKey:]
當然,KVC提供了鍵值檢測機制(KVV)
- (BOOL)validateValue:(inout id *)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
當然,這個方法是需要手動調用的,並不會自動調用,KVC的實現原理還是比較簡單的,這裏重點是要說明一下KVO
KVO
當某個類的對象第一次被觀察時,系統就會在運行期動態地創建該類的一個派生類,在這個派生類中重寫基類中被觀察屬性的 setter 方法,在setter方法裏使其具有通知機制。因此,要想KVO生效,必須直接或間接的通過setter方法訪問屬性(KVC的setValue就是間接方式)。直接訪問成員變量KVO是不生效的。
同時派生類還重寫了 class 方法以“欺騙”外部調用者它就是起初的那個類。然後系統將這個對象的 isa 指針指向這個新誕生的派生類,因此這個對象就成爲該派生類的對象了,因而在該對象上對 setter 的調用就會調用重寫的 setter,從而激活鍵值通知機制。此外,派生類還重寫了 dealloc 方法來釋放資源。
舉個?
比如,我們要對一個Dog
做一個監測,那麼,runtime就會創建一個名爲NSKVONotifying_Dog
新的NSKVONotifying_Dog
類會重寫一下方法:
增加了監聽屬性對應的set
,class
,dealloc
,_isKVOA
class
重寫class方法是爲了我們調用它的時候返回跟重寫繼承類之前同樣的內容。
打印如下內容:
NSLog(@"self->isa:%@",self->isa); NSLog(@"self class:%@",[self class]);
在建立KVO監聽之前,打印的結果爲:
self->isa:Dog self class:Dog
在建立監聽之後,打印的結果爲:
objective-c
self->isa:NSKVONotifying_Dog
self class:Dog
set
重寫set方法,是爲了在set方法中增加另外兩個方法的調用:
- (void)willChangeValueForKey:(NSString *)key - (void)didChangeValueForKey:(NSString *)key
其中,
didChangeValueForKey
負責調用:- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
這個就是你在監聽中寫的回調函數,這樣就實現了KVO,這裏有幾點需要注意的東西:
如果沒有任何的訪問器方法,-setValue:forKey
方法會直接調用:
- (void)willChangeValueForKey:(NSString *)key
- (void)didChangeValueForKey:(NSString *)key
比如說我們沒有自動生成setter 和 getter 方法,我們只是通過下劃線命名了 _icon
,那麼,我們只要手動調用這兩個方法,也會實現鍵值監聽
_isKVOA
這個私有方法應該是用來標識該類是一個 KVO 機制聲稱的類,不去關心
到此,一個KVO/KVC的基本實現就是這麼多了