KVO原理探秘

KVO的全稱是Key-Value Observing谦炬,俗稱“鍵值監(jiān)聽”昔榴,可以用于監(jiān)聽某個對象屬性值的改變逞怨。

對于iOS開發(fā)者而言,相信大家都使用過KVO耍鬓。但是對它的原理可能就不太清楚了阔籽。KVO是Runtime運行時機制的又一重大的應用,下面就對KVO的原理做一個詳盡的闡述牲蜀。

創(chuàng)建一個新的項目笆制,然后在項目中創(chuàng)建一個ZPPerson類,這個類中有一個age屬性涣达。

ZPPerson類
age屬性

然后在ViewController.m文件中創(chuàng)建一個person對象在辆,并給它添加KVO監(jiān)聽。

創(chuàng)建person對象
給person對象添加KVO監(jiān)聽

當用戶點擊屏幕的時候峭判,改變person對象的age屬性的值。

改變age屬性的值

這個時候系統(tǒng)就會調(diào)用監(jiān)聽方法了棕叫。

調(diào)用監(jiān)聽方法

下面闡述這個過程中KVO的實現(xiàn)原理:

  1. 在給這個person實例對象添加KVO監(jiān)聽之后林螃,系統(tǒng)會利用Runtime機制動態(tài)地創(chuàng)建一個ZPPerson類的子類,名字就叫做"NSKVONotifying_ZPPerson"俺泣。然后系統(tǒng)會把這個person實例對象里面的isa指針由原來的指向ZPPerson類的class對象變?yōu)楝F(xiàn)在的指向NSKVONotifying_ZPPerson類的class對象疗认;
  2. 新創(chuàng)建的子類"NSKVONotifying_ZPPerson"的class對象里面存儲著isa指針完残、superclass指針、"setAge:"實例方法横漏、"class"實例方法谨设、"dealloc"實例方法以及"_isKVOA"實例方法等。其中系統(tǒng)會重寫"setAge:"實例方法缎浇,重寫后的該方法與它父類中的該方法的實現(xiàn)是不一樣的扎拣,只不過方法的名稱是一樣的而已;
  3. 添加完KVO監(jiān)聽之后素跺,當開發(fā)者調(diào)用"setAge:"實例方法來修改person對象的age屬性的時候,根據(jù)上面所述,這個instance對象里面的isa指針已經(jīng)由原來的指向ZPPerson類的class對象變?yōu)榱爽F(xiàn)在的指向NSKVONotifying_ZPPerson類的class對象了瓶摆,所以系統(tǒng)根據(jù)這個instance對象里面的isa指針找到的是NSKVONotifying_ZPPerson類的class對象弟头,然后在這個class對象里面找到重寫后的"setAge:"實例方法,最后再進行調(diào)用踩验。這個重寫的"setAge:"實例方法里面會調(diào)用Foundation框架里面的C語言函數(shù)"_NSSetIntValueAndNotify();"鸥诽,這個函數(shù)的實現(xiàn)里面首先會執(zhí)行"[self willChangeValueForKey:@"age"];"代碼,然后再執(zhí)行"[super setAge:age];"代碼箕憾,在執(zhí)行這句代碼的時候就會執(zhí)行它的父類牡借,也就是ZPPerson類里面的"setAge:"方法,從而真正更改這個屬性的值厕九,最后再執(zhí)行"[self didChangeValueForKey:@"age"];"代碼蓖捶。"didChangeValueForKey:"這個方法里面會通知監(jiān)聽器"age"屬性的值被改變了,即調(diào)用"- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context"監(jiān)聽方法扁远,從而實現(xiàn)了對對象的某個屬性進行監(jiān)聽的目的俊鱼。

可以從下面的圖中看出NSKVONotifying_ZPPerson類和ZPPerson類的關(guān)系:

NSKVONotifying_ZPPerson類和ZPPerson類的關(guān)系

除了上述的改變對象的屬性值的時候會自動調(diào)用KVO的監(jiān)聽方法之外,想要在不改變對象屬性值的時候也能自動調(diào)用KVO的監(jiān)聽方法畅买,應該怎么做呢并闲?

手動觸發(fā)KVO:

在點擊屏幕的時候調(diào)用手動觸發(fā)KVO的方法:


調(diào)用手動觸發(fā)KVO的方法

在方法中需要主動調(diào)用"willChangeValueForKey:"和 "didChangeValueForKey:"方法。

手動觸發(fā)KVO方法

這樣的話谷羞,在點擊屏幕的時候就會手動觸發(fā)KVO的監(jiān)聽方法了"- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context"帝火。

image.png

以上就是對KVO原理的闡述。

Github Demo

補充:

修改對象里面的成員變量的值是不能夠觸發(fā)KVO的監(jiān)聽方法的湃缎,代碼如下所示:

在ZPPerson類里添加一個_age成員變量:

_age成員變量

在ViewController.m文件中給age屬性賦值:

給age屬性賦值

點擊屏幕的時候修改_age成員變量的值:

修改_age成員變量的值

運行之后可以知道通過這種方式改變對象的成員變量的值是無法自動觸發(fā)KVO的監(jiān)聽方法的"- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context"犀填。

修改對象的成員變量無法觸發(fā)KVO監(jiān)聽方法的原因:

原因就在于對于已經(jīng)被添加KVO監(jiān)聽的對象而言,instance對象的isa指針已經(jīng)被改變?yōu)榱酥赶蛳到y(tǒng)新創(chuàng)建的子類的class對象了嗓违,然后在這個子類的class對象里面找到了(setAge:)實例方法九巡,最后再進行調(diào)用。由之前的Demo可知蹂季,當調(diào)用新創(chuàng)建的子類的class對象中的屬性的set實例方法的時候其實是在調(diào)用Foundation框架里面的C語言函數(shù)"_NSSetIntValueAndNotify"冕广,這個函數(shù)中會執(zhí)行"didChangeValueForKey:"方法疏日,此方法會觸發(fā)KVO的監(jiān)聽方法。由此可知KVO的本質(zhì)是調(diào)用set實例方法撒汉,即只有通過調(diào)用屬性的set實例方法來修改屬性的值才能觸發(fā)KVO的監(jiān)聽方法沟优,而修改對象的成員變量就沒有調(diào)用屬性的set方法,所以是不能夠觸發(fā)KVO的監(jiān)聽方法的睬辐。

如果想要在修改對象的成員變量的值的時候成功觸發(fā)KVO的監(jiān)聽方法的話就要手動進行觸發(fā):

image.png

在手動觸發(fā)方法的實現(xiàn)里面要調(diào)用"willChangeValueForKey:"和 "didChangeValueForKey:"方法:

image.png

運行之后就可以在控制臺上看到成功調(diào)用了KVO的監(jiān)聽方法了:

控制臺打印結(jié)果

Github Demo

”三人行挠阁,必有我?guī)熝伞埃?歡迎各位批評指正。
如果您還覺得我寫的不錯的話請您點贊加關(guān)注溉委,您的肯定是我前進的最大動力鹃唯!
我是愛學習也愛您的樹懶O(∩_∩)O

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市瓣喊,隨后出現(xiàn)的幾起案子坡慌,更是在濱河造成了極大的恐慌,老刑警劉巖藻三,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洪橘,死亡現(xiàn)場離奇詭異,居然都是意外死亡棵帽,警方通過查閱死者的電腦和手機熄求,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逗概,“玉大人弟晚,你說我怎么就攤上這事∮馍唬” “怎么了卿城?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長铅搓。 經(jīng)常有香客問我瑟押,道長,這世上最難降的妖魔是什么星掰? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任多望,我火速辦了婚禮,結(jié)果婚禮上氢烘,老公的妹妹穿的比我還像新娘怀偷。我一直安慰自己,他們只是感情好播玖,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布椎工。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪晋渺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天脓斩,我揣著相機與錄音木西,去河邊找鬼。 笑死随静,一個胖子當著我的面吹牛八千,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播燎猛,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼恋捆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了重绷?” 一聲冷哼從身側(cè)響起沸停,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎昭卓,沒想到半個月后愤钾,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡候醒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年能颁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片倒淫。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡伙菊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出敌土,到底是詐尸還是另有隱情镜硕,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布纯赎,位于F島的核電站谦疾,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏犬金。R本人自食惡果不足惜念恍,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望晚顷。 院中可真熱鬧峰伙,春花似錦、人聲如沸该默。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽栓袖。三九已至匣摘,卻和暖如春店诗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背音榜。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工庞瘸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赠叼。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓擦囊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親嘴办。 傳聞我的和親對象是個殘疾皇子瞬场,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

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