KVO與KVC的實(shí)現(xiàn)原理

一祷肯、KVO的實(shí)現(xiàn)原理

KVO的全稱是Key-ValueObserving(鍵值監(jiān)聽),可以用于監(jiān)聽某個(gè)對象屬性值的改變芙代。

1之拨、KVO的使用API如下


通過addObserver: forKeyPath: options: context:添加觀察者對某個(gè)屬性的監(jiān)聽茉继。

2、大家看下我寫的代碼蚀乔,在設(shè)置age打個(gè)斷點(diǎn)來調(diào)試烁竭。


為什么會這樣?明明我的person實(shí)例是HPPerson類實(shí)例化而來的切發(fā)現(xiàn)他的類對象不再是HPPerson了,變成了NSKVONotifying_HPPerson乙墙。person1的類對象還是HPPerson颖变。

注意:不能用[self.person class]來獲取解析本質(zhì)。[self.person class]听想,[self.person1 class]得到的結(jié)果都是HPPerson類腥刹,這個(gè)可能是蘋果API不想你知道太多內(nèi)層的實(shí)現(xiàn)哈哈。所以要利用isa指針的本質(zhì)來看問題汉买。

說明:在使用[self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];時(shí)衔峰,在內(nèi)部實(shí)現(xiàn)中通過Runtime動(dòng)態(tài)生成了一個(gè)NSKVONotifying_HPPerson類。

3蛙粘、NSKVONotifying_HPPerson類是什么類垫卤?

先看下setAge的實(shí)現(xiàn)。


可以看出出牧,由于person加了KVO監(jiān)聽穴肘,所以setAge的函數(shù)地址變了,實(shí)現(xiàn)也變了舔痕!setAge里面的具體實(shí)現(xiàn)是調(diào)用了_NSSetIntValueAndNotify()這個(gè)函數(shù)评抚。但是我們從self.person.age=10;的設(shè)置結(jié)果可以看出,self.person.age設(shè)值后的age確實(shí)是10伯复;也就是說這個(gè)成員變量_age是正確按照setAge:里面的實(shí)現(xiàn)(_age=age)做了賦值操作慨代。而這個(gè)賦值操作時(shí)在Person類對象的方法列表中的setAge方法里面。說明_NSSetIntValueAndNotify()函數(shù)應(yīng)該實(shí)現(xiàn)了setAge的方法實(shí)現(xiàn)啸如。也就是要達(dá)到這種目的侍匙,只有一種情況,NSKVONotifying_HPPerson是HPPerson的子類叮雳。也就是在addObserver:時(shí)候Runtime動(dòng)態(tài)創(chuàng)建的NSKVONotifying_HPPerson類是HPPerson類的子類想暗。

可以通過LLDB打印他的superclass指針來證明NSKVONotifying_HPPerson確實(shí)是HPPerson子類。


由此可以看出添加KVO后的實(shí)例對象的類對象變成了NSKVONotifying_HPPerson類帘不,而沒有添加KVO的實(shí)例對象的類對象還是之前的HPPerson類江滨。

總結(jié)關(guān)系如下圖:

1)添加KVO的類關(guān)系圖譜


2)沒有添加KVO的類關(guān)系圖譜


3)如果增加一個(gè)stirng類型的name屬性,則會調(diào)用_NSSetCharValueAndNotify(),由此可以知道_NSSet**ValueAndNotify()有非常多個(gè)這個(gè)函數(shù)厌均,根據(jù)不同類型屬性調(diào)用不同函數(shù)名的函數(shù)唬滑,這個(gè)屬于Foundation框架下的函數(shù),可以通過越獄設(shè)備拿到Foundation的二進(jìn)制文件,然后反編譯后去查找這類函數(shù)的實(shí)現(xiàn)晶密。過程太復(fù)雜擒悬,這里不深入寫下去了。 在這里只要知道我們的KVO實(shí)質(zhì)是利用了Runtime生成了一個(gè)子類稻艰,在子類的setAge方法中調(diào)用_NSSetIntValueAndNotify()來實(shí)現(xiàn)的懂牧。

4)_NSSetIntValueAndNotify()這系列函數(shù)內(nèi)部實(shí)現(xiàn)邏輯大概是如下這樣:

通過willChangeValueForKey與didChangeValueForKey的調(diào)用,可以實(shí)現(xiàn)手動(dòng)觸發(fā)KVO監(jiān)聽尊勿。

所以可以大概知道這個(gè)函數(shù)內(nèi)部實(shí)現(xiàn)應(yīng)該是如下:


在didChangeValueForKey:里面實(shí)現(xiàn)了內(nèi)部調(diào)用屬性觀察者observer的observeValueForKeyPath:ofObject:change:context:方法僧凤。

4、通過KVC設(shè)值age會不會觸發(fā)KVO監(jiān)聽嗎元扔?


結(jié)果可以發(fā)現(xiàn)躯保,KVC也會觸發(fā)KVO監(jiān)聽。其內(nèi)部也是實(shí)現(xiàn)了willChangeValueForKey與didChangeValueForKey的調(diào)用澎语。

二途事、KVC設(shè)值取值原理流程圖

常用API使用:

- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;

- (void)setValue:(id)value forKey:(NSString *)key;

- (id)valueForKeyPath:(NSString *)keyPath;

- (id)valueForKey:(NSString *)key;

1、設(shè)值原理

說明:+(BOOL)accessInstanceVariablesDirectly方法默認(rèn)返回值YES;

2)取值原理


對于KVC的設(shè)值取值的順序擅羞,可以自己建一個(gè)類把對應(yīng)方法寫進(jìn)去看下執(zhí)行及順序尸变。

三、總結(jié)

0减俏、利用addObserver:forKeyPath...函數(shù)增加觀察值變化召烂,內(nèi)部實(shí)現(xiàn)會利用Runtime動(dòng)態(tài)生成一個(gè)NSKVONotifying_HPPerson類(HPPerson的子類)。NSKVONotifying_HPPerson類中也有setAge方法娃承,實(shí)現(xiàn)代碼是直接調(diào)用了Foundation下的_NSSet***ValueAndNotify()系里函數(shù)骑晶。

1、_NSSet***ValueAndNotify()系里函數(shù)實(shí)現(xiàn)邏輯為調(diào)用willChangeValueForKey與didChangeValueForKey方法來實(shí)現(xiàn)草慧。

2、-(void)didChangeValueForKey方法內(nèi)部實(shí)現(xiàn)[observer?observeValueForKeyPath: ofObject...]的調(diào)用匙头。

3漫谷、KVO需要通過set方法來實(shí)現(xiàn)鍵值監(jiān)聽,如果直接賦值成員變量是不會觸發(fā)KVO蹂析。

4舔示、KVO可以通過手動(dòng)調(diào)用-(void)willChangeValueForKey與-(void)didChangeValueForKey方法來實(shí)現(xiàn)。

5电抚、KVC設(shè)值會觸發(fā)KVO惕稻。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蝙叛,隨后出現(xiàn)的幾起案子俺祠,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜘渣,死亡現(xiàn)場離奇詭異淌铐,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蔫缸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門腿准,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人拾碌,你說我怎么就攤上這事吐葱。” “怎么了校翔?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵弟跑,是天一觀的道長。 經(jīng)常有香客問我展融,道長窖认,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任告希,我火速辦了婚禮扑浸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘燕偶。我一直安慰自己喝噪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布指么。 她就那樣靜靜地躺著酝惧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪伯诬。 梳的紋絲不亂的頭發(fā)上晚唇,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機(jī)與錄音盗似,去河邊找鬼哩陕。 笑死,一個(gè)胖子當(dāng)著我的面吹牛赫舒,可吹牛的內(nèi)容都是我干的悍及。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼接癌,長吁一口氣:“原來是場噩夢啊……” “哼心赶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起缺猛,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤缨叫,失蹤者是張志新(化名)和其女友劉穎椭符,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弯汰,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡艰山,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了咏闪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片曙搬。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鸽嫂,靈堂內(nèi)的尸體忽然破棺而出纵装,到底是詐尸還是另有隱情,我是刑警寧澤据某,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布橡娄,位于F島的核電站,受9級特大地震影響癣籽,放射性物質(zhì)發(fā)生泄漏挽唉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一筷狼、第九天 我趴在偏房一處隱蔽的房頂上張望瓶籽。 院中可真熱鬧,春花似錦埂材、人聲如沸塑顺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽严拒。三九已至,卻和暖如春竖独,著一層夾襖步出監(jiān)牢的瞬間裤唠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工莹痢, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留种蘸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓格二,卻偏偏與公主長得像,于是被迫代替她去往敵國和親竣蹦。 傳聞我的和親對象是個(gè)殘疾皇子顶猜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容