老掉牙的題目了如捅,只是為了總結(jié)下知識(shí)點(diǎn)地淀,還有新進(jìn)門的iOS。不喜勿噴臼寄!
KVO 前言:
我們知道iOS中NSObject對(duì)象可以增加觀察者來(lái)監(jiān)聽(tīng)對(duì)象屬相值得變化霸奕。
1.那么KVO的本質(zhì)是什么呢??
2. KVO可以手動(dòng)觸發(fā)么吉拳?
下面我們就來(lái)看下KVO的實(shí)現(xiàn)
從上圖我們可以看出质帅,當(dāng)實(shí)現(xiàn)KVO之后,p1的isa指針指向的發(fā)生了改變留攒,指向了一個(gè)NSKVONotifying_JWPerson,那么這個(gè)類是從哪來(lái)的呢煤惩?
其實(shí)呢,KVO的本質(zhì)就是:當(dāng)使用KVO之后炼邀,iOS系統(tǒng)會(huì)修改這個(gè)對(duì)象的isa指針魄揉,改為指向一個(gè)全新通過(guò)Runtime動(dòng)態(tài)創(chuàng)建的子類,子類擁有自己的set方法實(shí)現(xiàn)拭宁,內(nèi)部會(huì)調(diào)用willChangeValueForKey 洛退、 原來(lái)的set方法 、 didChangeValueForKey(這個(gè)方法內(nèi)部會(huì)調(diào)用監(jiān)聽(tīng)器的監(jiān)聽(tīng)方法)來(lái)實(shí)現(xiàn)監(jiān)聽(tīng)的目的的杰标。
下面我們就用來(lái)代碼來(lái)大概還原下KVO的過(guò)程:
這就是KVO的本質(zhì)工作原理兵怯。其實(shí)并不是很難,有很容易理解腔剂。下面我們?cè)趤?lái)證實(shí)下我們所說(shuō)的對(duì)不對(duì)媒区。
從上圖中我們可以看出,其實(shí)實(shí)現(xiàn)監(jiān)聽(tīng)之后,setAge方法并不是走向原來(lái)的父類的setAge方法袜漩,而是走了_NSSETIntVAalueNotify這個(gè)方法绪爸。。這個(gè)方法是一個(gè)底層的C語(yǔ)言方法宙攻,里面調(diào)用了
willChangeValueForKey:
[super setAge:]
didChangeValueForKey: 在此處調(diào)用[observerobserveValueForKeyPath:@"age" ofObject:(object change:change context:context];l來(lái)進(jìn)行回調(diào)奠货,達(dá)到監(jiān)聽(tīng)值發(fā)生改變的原理。
還有一個(gè)就是:Runtime動(dòng)態(tài)生成的子類粘优,只是重寫(xiě)了setter方法么仇味?答案顯然并不是呻顽,那他都有哪些方法呢雹顺?
從圖中我們可以看出NSKVONotifying_JWPerson 一共有四個(gè)方法,一個(gè)是set方法廊遍,一個(gè)是class嬉愧、delloc、_isKVOA? 喉前,下面我們來(lái)說(shuō)下這四個(gè)方法:
1.setter方法不多說(shuō)了没酣,上面已經(jīng)介紹了。
2.class方法的作用是什么呢卵迂?答案就是:當(dāng)我們調(diào)用[object class]方法的時(shí)候裕便,如果子類沒(méi)有改方法,就會(huì)從父類中尋找见咒,直至找到NSObject中的class方法偿衰,進(jìn)行調(diào)用。而NSObject的class實(shí)現(xiàn)是返回 objc_getclass(self)? 實(shí)現(xiàn)的改览,也就是說(shuō)會(huì)直接返回自身類下翎,到那時(shí)apple并不想讓NSKVONotifying_JWPerson給暴露出來(lái),所以重寫(xiě)class方法宝当,進(jìn)而返回你添加觀察者的類视事。
3.delloc方法,是釋放一些資源庆揩。
4.isKVOA 方法俐东,我也不是很懂,有知道的同學(xué)订晌,請(qǐng)留言犬性。。我猜想應(yīng)該是標(biāo)識(shí)是否是KVO的一種狀態(tài)使用的腾仅。
如何手動(dòng)執(zhí)行KVO呢乒裆?我們知道當(dāng)值發(fā)生改變會(huì)觸發(fā)觀察者的方法調(diào)用,但是如何在我們不改變值得情況下觸發(fā)改方法呢?
-(void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context ;
答案就是 需要手動(dòng)觸發(fā)的話 你需要將
[obj willChangeValueForKey:@"age"];
[obj didChangeValueForKey:@"age"];
這兩行代碼放置執(zhí)行處鹤耍,就會(huì)觸發(fā)觀察者的方法肉迫。這也說(shuō)明了,觸發(fā)KVO回調(diào)的就是通過(guò)這兩個(gè)方法來(lái)實(shí)現(xiàn)的稿黄。
補(bǔ)充:
1.修改實(shí)例的成員變量會(huì)觸發(fā)KVO么喊衫?
? ? 不會(huì)的,KVO是通過(guò)重寫(xiě)setter方法來(lái)進(jìn)行監(jiān)聽(tīng)值得改變杆怕,成員變量賦值是不會(huì)觸發(fā)setter方法族购,因此也并不會(huì)觸發(fā)KVO。
KVC 前言:
鍵值編碼陵珍,通過(guò)setValue賦值的操作修改對(duì)象屬性的值寝杖。那么KVC的原理又是什么樣子的呢?下面我們就來(lái)揭曉一下。
賦值過(guò)程:???當(dāng)操作setValue 的時(shí)候會(huì)尋找 setKey 和 _setKey 依次尋找兩個(gè)方法的實(shí)現(xiàn)互纯,如果實(shí)現(xiàn)瑟幕,則賦值成功,如果沒(méi)有首先會(huì)判斷類方法accessInstanceVariablesDirectly 返回值(默認(rèn)返回YES)留潦,返回NO只盹,直接拋出異常,返回YES則會(huì)依次訪問(wèn)該類的成員變量里面的 _key 兔院、__isKey殖卑、Key、isKey 是否存在,如果四個(gè)成員變量都不存在坊萝,爆出異常孵稽,存在賦值成功。
取值過(guò)程:當(dāng)操作valueForKey 的時(shí)候會(huì)尋找 getKey 和 key屹堰、isKey肛冶、_key 依次尋找四個(gè)方法的實(shí)現(xiàn),如果實(shí)現(xiàn)扯键,則取值成功睦袖,如果沒(méi)有首先會(huì)判斷類方法accessInstanceVariablesDirectly 返回值(默認(rèn)返回YES),返回NO荣刑,直接拋出異常馅笙,返回YES則會(huì)依次訪問(wèn)該類的成員變量里面的 _key 、__isKey厉亏、Key董习、isKey 是否存在,如果四個(gè)成員變量都不存在,拋出異常爱只,存在取值成功皿淋。
小知識(shí)點(diǎn):
1、[obj setValue:value forKey:@"key"]; 跟?[obj setValue:value forKeyPath:@"key"];的區(qū)別在于:
兩者的區(qū)別在于,forKeyPath更加強(qiáng)大窝趣,可以訪問(wèn)對(duì)象內(nèi)的對(duì)象的屬性疯暑,比如super.son.property,而forKey只能訪問(wèn)當(dāng)前類的屬性。
2哑舒、KVC改變的賦值會(huì)出發(fā)KVO么妇拯?
會(huì)觸發(fā)KVO的,這里觸發(fā)分兩種情況:
當(dāng)key存在setter方法的時(shí)候洗鸵,會(huì)進(jìn)行KVO的監(jiān)聽(tīng)越锈,重寫(xiě)setter方法,進(jìn)行觸發(fā)KVO膘滨。
當(dāng)key不存在setter方法的時(shí)候甘凭,只有成員變量存在的時(shí)候,也會(huì)觸發(fā)KVO監(jiān)聽(tīng)吏祸,是因?yàn)镵VC內(nèi)部調(diào)用了KVO得 willchangeForKey 和didChangeForKey方法來(lái)觸發(fā)KVO对蒲。