KVO&KVC淺談

在談?wù)揔VO和KVC之前望浩,先引出幾個(gè)問題

問題一:iOS用什么方式實(shí)現(xiàn)KVO瀑晒,你可以理解成KVO本質(zhì)是什么?

1.利用Runtime API動(dòng)態(tài)生成一個(gè)子類,并且讓instance對(duì)象的isa指向這個(gè)全新的子類

2.當(dāng)修改instance對(duì)象的屬性時(shí)赚瘦,會(huì)調(diào)用Foundation的_NSSetXXXValueAndNotify 函數(shù),XXX代表更改的類型

3.重寫class方法

4.在willChangeValueForKey:和didChangeValueForKey:之間調(diào)用父類原來的seter方法奏寨,并且內(nèi)部會(huì)觸發(fā)監(jiān)聽器的Observer的監(jiān)聽方法(observeValueForKeyPath:ofObject:change:context:)

添加兩張圖片:


未使用KVO監(jiān)聽的對(duì)象
使用KVO監(jiān)聽的對(duì)象

問題二:如何手動(dòng)觸發(fā)KVO起意?

手動(dòng)調(diào)用willChangeValueForKey和didChangeValueForKey:

問題三:通過指針->修改成員變量會(huì)觸發(fā)KVO么?

不會(huì)觸發(fā)KVO病瞳,因?yàn)闆]有調(diào)用到setter方法

問題四:什么是KVC揽咕?修改KVC的屬性會(huì)觸發(fā)KVO么?

Key-Value Coding套菜,即鍵值編碼心褐。它是一種不通過存取方法,而通過屬性名稱字符串間接訪問屬性的機(jī)制

會(huì)觸發(fā)KVO

問題五:KVC的賦值和取值過程是怎樣的笼踩?或者KVC的實(shí)現(xiàn)原理是什么逗爹?

賦值:

當(dāng)調(diào)用setValue:屬性值 forKey:@"name"的代碼時(shí),底層的執(zhí)行機(jī)制如下:

1.程序優(yōu)先調(diào)用set<Key>:屬性值方法嚎于,代碼通過setter方法完成設(shè)置掘而,注意,這里的<key>是指成員變量名于购,首字母大小要符合KVC的命名規(guī)則袍睡,下面也是這樣的原則

2.如果沒有找到setName:方法,KVC機(jī)制會(huì)檢查一個(gè)類方法+(BOOL)accessInstanceVariableDirectory方法沒有返回YES肋僧,默認(rèn)該方法會(huì)返回YES斑胜,如果你重寫了該方法讓其返回NO的話,那么在這一步KVC會(huì)執(zhí)行setValue:forUndefinedKey:方法嫌吠,不過一般不會(huì)這樣做止潘,因?yàn)檫@樣做相當(dāng)于打破了KVC的規(guī)則,如果返回YES的話辫诅,KVC機(jī)制會(huì)搜索該類里面有沒有名為_<key>的成員變量凭戴,無論該變量是在類接口處定義,還是在類實(shí)現(xiàn)中定義炕矮,也無論用了什么樣的訪問修飾符狮杨,只要存在以_<key>命名的變量袭蝗,KVC都可以對(duì)該成員變量賦值

3.如果該類沒有set<key>:方法日矫,也沒有_<key>成員變量字柠,KVC機(jī)制會(huì)搜索_is<Key>的成員變量

4.如果沒有set<Key>:方法,也沒有_<key>和_is<Key>成員變量邢滑,KVC機(jī)制再會(huì)繼續(xù)搜索<key>和is<Key>的成員變量腐螟,再給他們賦值

5.如果上面列出的方法或者成員變量都不存在,系統(tǒng)將會(huì)執(zhí)行該對(duì)象的setValue:forUndefinedKey:方法,默認(rèn)是拋出異常

如果開發(fā)者想讓這個(gè)類禁用KVC里遭垛,那么重寫+ (BOOL)accessInstanceVariablesDirectly方法讓其返回NO即可尼桶,這樣的話如果KVC沒有找到set<Key>:屬性名時(shí)操灿,會(huì)直接用setValue:forUndefinedKey:方法

取值:

當(dāng)調(diào)用valueForKey:@”name“的代碼時(shí)锯仪,KVC對(duì)key的搜索方式不同于setValue:屬性值 forKey:@”name“,其搜索方式如下:

1.首先按get<Key>,<key>,is<Key>的順序方法查找getter方法趾盐,找到的話會(huì)直接調(diào)用庶喜。如果是BOOL或者Int等值類型, 會(huì)將其包裝成一個(gè)NSNumber對(duì)象救鲤。

2.如果上面的getter沒有找到久窟,KVC則會(huì)查找countOf<Key>,objectIn<Key>AtIndex或<Key>AtIndexes格式的方法。如果countOf<Key>方法和另外兩個(gè)方法中的一個(gè)被找到本缠,那么就會(huì)返回一個(gè)可以響應(yīng)NSArray所有方法的代理集合(它是NSKeyValueArray斥扛,是NSArray的子類),調(diào)用這個(gè)代理集合的方法丹锹,或者說給這個(gè)代理集合發(fā)送屬于NSArray的方法稀颁,就會(huì)以countOf<Key>,objectIn<Key>AtIndex或<Key>AtIndexes這幾個(gè)方法組合的形式調(diào)用。還有一個(gè)可選的get<Key>:range:方法楣黍。所以你想重新定義KVC的一些功能匾灶,你可以添加這些方法,需要注意的是你的方法名要符合KVC的標(biāo)準(zhǔn)命名方法租漂,包括方法簽名阶女。

3.如果上面的方法沒有找到,那么會(huì)同時(shí)查找countOf<Key>哩治,enumeratorOf<Key>,memberOf<Key>格式的方法秃踩。如果這三個(gè)方法都找到,那么就返回一個(gè)可以響應(yīng)NSSet所的方法的代理集合业筏,和上面一樣吞瞪,給這個(gè)代理集合發(fā)NSSet的消息,就會(huì)以countOf<Key>驾孔,enumeratorOf<Key>,memberOf<Key>組合的形式調(diào)用芍秆。

4.如果還沒有找到,再檢查類方法+ (BOOL)accessInstanceVariablesDirectly,如果返回YES(默認(rèn)行為)翠勉,那么和先前的設(shè)值一樣妖啥,會(huì)按_<key>,_is<Key>,<key>,is<Key>的順序搜索成員變量名,這里不推薦這么做对碌,因?yàn)檫@樣直接訪問實(shí)例變量破壞了封裝性荆虱,使代碼更脆弱。如果重寫了類方法+ (BOOL)accessInstanceVariablesDirectly返回NO的話,那么會(huì)直接調(diào)用valueForUndefinedKey:

5.還沒有找到的話怀读,調(diào)用valueForUndefinedKey:

問題六:KVC更改屬性或者成員變量會(huì)觸發(fā)KVO么诉位?

答案是前者會(huì),后者不會(huì)



接下來我會(huì)一一通過實(shí)例證明以上問題菜枷?

Key-Value Obersver苍糠,即鍵值觀察。它是觀察者模式的一種衍生啤誊≡啦t;舅枷胧牵瑢?duì)目標(biāo)對(duì)象的某屬性添加觀察蚊锹,當(dāng)該屬性發(fā)生變化時(shí)瞳筏,會(huì)自動(dòng)的通知觀察者。這里所謂的通知是觸發(fā)觀察者對(duì)象實(shí)現(xiàn)的KVO的接口方法牡昆。

** KVO是解決model和view同步的好法子姚炕。**

另外,KVO的優(yōu)點(diǎn)是當(dāng)被觀察的屬性值改變時(shí)是會(huì)自動(dòng)發(fā)送通知的丢烘,這比通知中心需要post通知來說柱宦,簡(jiǎn)單了許多

KVO 使用方法

1.首先給目標(biāo)對(duì)象的屬性添加觀察:

[selfaddObserver:selfforKeyPath:@"xxx"options:(NSKeyValueObservingOptionNew)context:nil];

2.實(shí)現(xiàn)下面方法來接收通知,需要注意各個(gè)參數(shù)的含義:

- (void)addObserver:(NSObject*)observer forKeyPath:(NSString*)keyPath options:(NSKeyValueObservingOptions)options context:(nullablevoid*)context;

3.最后要移除觀察者:

- (void)removeObserver:(NSObject*)observer forKeyPath:(NSString*)keyPath;



證明1:(問題一:iOS用什么方式實(shí)現(xiàn)KVO铅协,你可以理解成KVO本質(zhì)是什么捷沸?)

利用Runtime API動(dòng)態(tài)生成一個(gè)子類,并且讓instance對(duì)象的isa指向這個(gè)全新的子類?


生成中間類圖

.當(dāng)修改instance對(duì)象的屬性時(shí)狐史,會(huì)調(diào)用Foundation的_NSSetXXXValueAndNotify 函數(shù)痒给,XXX代表更改的類型

監(jiān)聽前后地址對(duì)象圖
打印地址方法

從上面可以看到監(jiān)聽之后,的確是調(diào)用了NSSetIntValueAndNotify,里面的int代表是監(jiān)聽屬性的類型骏全,另外這一點(diǎn)也可以從你逆向中得到苍柏,從你手機(jī)的目錄中/System/Library/Caches/com.apple.dyld獲取動(dòng)態(tài)庫dyld_shared_cache_arm64,然后導(dǎo)出Foundation框架姜贡,具體可以參考這篇文章,然后利用逆向工具Hop來分析试吁,可以看到里面包含這個(gè)方法


Foundation 框架圖

重寫class方法

class打印圖

從源碼中可以看到object_getClass中就是調(diào)用class,既然這樣楼咳,為什么我直接打印class會(huì)和object_getClass不一樣呢熄捍,很明顯被監(jiān)聽的類重寫了class


在willChangeValueForKey:和didChangeValueForKey:之間調(diào)用父類原來的seter方法,并且內(nèi)部會(huì)觸發(fā)監(jiān)聽器的Observer的監(jiān)聽方法(observeValueForKeyPath:ofObject:change:context:)

證明圖如下

另外想補(bǔ)充一下的就是母怜,這個(gè)被監(jiān)聽的類里面含有的方法:

監(jiān)聽類方法內(nèi)部圖

證明2(如何手動(dòng)觸發(fā)KVO余耽?)

手動(dòng)調(diào)用

可以看到如果不直接賦值的話,手動(dòng)調(diào)用這兩個(gè)方法也是可以觸發(fā)KVO

證明3:(問題三:通過指針->修改成員變量會(huì)觸發(fā)KVO么苹熏?)

更改成員變量沒有觸發(fā)KVO


證明4:(修改KVC的屬性會(huì)觸發(fā)KVO么碟贾?)

修改成員變量

從上面可以看出更改成員變量并沒有出發(fā)KVO

證明5:(KVC的實(shí)現(xiàn)原理)

有setAge
無setAge有_setAge
setKey和_setKey無币喧,找_key
setKey和_setKey無,找_isKey
setKey和_setKey無袱耽,找key
setKey和_setKey無杀餐,找isAge

用一個(gè)圖說明上述的賦值過程:

setValue尋找過程圖
有g(shù)etKey圖
沒有g(shù)etKey圖,有key
沒有g(shù)etKey朱巨,key有isKey
沒有g(shù)etKey史翘,key,isKey蔬崩,但是有_key

用一張圖給說明尋找過程:

valueForKey尋找圖

證明6:(問題六:KVC更改屬性或者成員變量會(huì)觸發(fā)KVO么恶座?)

kvc屬性修改
成員變量修改

從打印結(jié)果可以看出搀暑,修改成員變量并不會(huì)觸發(fā)KVO

其他補(bǔ)充

查看_NSSet*AndNotify的存在

nm 命令圖

_NSSet*ValueAndNotify 的內(nèi)部實(shí)現(xiàn)


_NSSet*ValueAndNotify內(nèi)部實(shí)現(xiàn)圖

_NSSet*ValueAndNotify本質(zhì)調(diào)用就是先調(diào)用willChangeValueForKey:然后調(diào)用原來的setter方法的實(shí)現(xiàn)沥阳,之后再調(diào)用didChangeValueForKey:這個(gè)方法內(nèi)部會(huì)調(diào)用observer的observeValueForKeyPath:ofObject:change:context:方法

KVC常見的API有:

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

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

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

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

參考鏈接

iOS窺探KVO底層實(shí)現(xiàn)原理篇


可以添加微信一起交流學(xué)習(xí):fslskz

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市自点,隨后出現(xiàn)的幾起案子桐罕,更是在濱河造成了極大的恐慌,老刑警劉巖桂敛,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件功炮,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡术唬,警方通過查閱死者的電腦和手機(jī)薪伏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來粗仓,“玉大人嫁怀,你說我怎么就攤上這事〗枳牵” “怎么了塘淑?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蚂斤。 經(jīng)常有香客問我存捺,道長(zhǎng),這世上最難降的妖魔是什么曙蒸? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任捌治,我火速辦了婚禮,結(jié)果婚禮上纽窟,老公的妹妹穿的比我還像新娘肖油。我一直安慰自己,他們只是感情好师倔,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布构韵。 她就那樣靜靜地躺著周蹭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪疲恢。 梳的紋絲不亂的頭發(fā)上凶朗,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音显拳,去河邊找鬼棚愤。 笑死,一個(gè)胖子當(dāng)著我的面吹牛杂数,可吹牛的內(nèi)容都是我干的宛畦。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼揍移,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼次和!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起那伐,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤踏施,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后罕邀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體畅形,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年诉探,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了日熬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡肾胯,死狀恐怖竖席,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情阳液,我是刑警寧澤怕敬,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站帘皿,受9級(jí)特大地震影響东跪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鹰溜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一虽填、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧曹动,春花似錦斋日、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽第献。三九已至,卻和暖如春兔港,著一層夾襖步出監(jiān)牢的瞬間庸毫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工衫樊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留飒赃,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓科侈,卻偏偏與公主長(zhǎng)得像载佳,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子臀栈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

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

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,093評(píng)論 1 32
  • KVC(Key-value coding)鍵值編碼蔫慧,單看這個(gè)名字可能不太好理解。其實(shí)翻譯一下就很簡(jiǎn)單了挂脑,就是指iO...
    我的夢(mèng)工廠閱讀 891評(píng)論 1 8
  • KVC(Key-valuecoding)鍵值編碼藕漱,單看這個(gè)名字可能不太好理解欲侮。其實(shí)翻譯一下就很簡(jiǎn)單了崭闲,就是指iOS...
    榕樹頭閱讀 702評(píng)論 0 2
  • KVC/KVO 概念 KVC : 即 Key-Value-Coding,用于鍵值編碼威蕉。作為 cocoa 的一個(gè)標(biāo)準(zhǔn)...
    滿臉胡茬的小碼農(nóng)閱讀 1,953評(píng)論 2 8
  • 驚蟄刁俭,春分過后,春筍冒尖韧涨,漫山遍野長(zhǎng)在閩北的山野牍戚,一不小心就節(jié)節(jié)升高,趁著春筍鬧市時(shí)節(jié)虑粥,對(duì)的時(shí)間和對(duì)的人一起...
    Icy_cf5f閱讀 435評(píng)論 0 0