KVO 這個(gè)坑你肯定會(huì)遇到

一、什么是 KVO

首先讓我們了解一下什么KVO甜熔,全稱(chēng)為Key-Value Observing圆恤,是iOS中的一種設(shè)計(jì)模式,用于檢測(cè)對(duì)象的某些屬性的實(shí)時(shí)變化情況并作出響應(yīng)。鍵值觀(guān)察Key-Value-Observer就是觀(guān)察者模式盆昙。

觀(guān)察者模式的定義:一個(gè)目標(biāo)對(duì)象管理所有依賴(lài)于它的觀(guān)察者對(duì)象羽历,并在它自身的狀態(tài)改變時(shí)主動(dòng)通知觀(guān)察者對(duì)象。這個(gè)主動(dòng)通知通常是通過(guò)調(diào)用各觀(guān)察者對(duì)象所提供的接口方法來(lái)實(shí)現(xiàn)的淡喜。觀(guān)察者模式較完美地將目標(biāo)對(duì)象與觀(guān)察者對(duì)象解耦。

KVO和KVC沒(méi)有什么關(guān)系,要說(shuō)有關(guān)系的話(huà)也就是--KVO同KVC一樣都依賴(lài)于Runtime的動(dòng)態(tài)機(jī)制.

在WPF中有一種雙向綁定機(jī)制澎嚣,如果數(shù)據(jù)模型修改了之后會(huì)立即反映到UI視圖上易桃,類(lèi)似的還有如今比較流行的基于MVVM設(shè)計(jì)模式的前端框架晤郑。其實(shí)在ObjC中原生就支持這種機(jī)制造寝,它叫做Key Value Observing(簡(jiǎn)稱(chēng)KVO)吭练。KVO其實(shí)是一種觀(guān)察者模式线脚,利用它可以很容易實(shí)現(xiàn)視圖組件和數(shù)據(jù)模型的分離浑侥,當(dāng)數(shù)據(jù)模型的屬性值改變之后作為監(jiān)聽(tīng)器的視圖組件就會(huì)被激發(fā)寓落,激發(fā)時(shí)就會(huì)回調(diào)監(jiān)聽(tīng)器自身伶选。在ObjC中要實(shí)現(xiàn)KVO則必須實(shí)現(xiàn)NSKeyValueObServing協(xié)議,不過(guò)幸運(yùn)的是NSObject已經(jīng)實(shí)現(xiàn)了該協(xié)議构资,因此幾乎所有的ObjC對(duì)象都可以使用KVO吐绵。

二己单、怎么實(shí)現(xiàn) KVO

1.注冊(cè)

1 //keyPath就是要觀(guān)察的屬性值 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?2//options給你觀(guān)察鍵值變化的選擇 ? ? ? ? ? ? ? ? ??

3//context方便傳輸你需要的數(shù)據(jù)

4-(void)addObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath5options:(NSKeyValueObservingOptions)options context:(void*)context;

2.實(shí)現(xiàn)

1 //change里存儲(chǔ)了一些變化的數(shù)據(jù)纹份,比如變化前的數(shù)據(jù)廷痘,變化后的數(shù)據(jù);如果注冊(cè)時(shí)context不為空蠢笋,這里context就能接收到昨寞。 ? ?2 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object34change:(NSDictionary *)change context:(void*)context

3 移除

1//移除 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 2- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

三援岩、 KVO底層實(shí)現(xiàn)分析

系統(tǒng)實(shí)現(xiàn)KVO有以下幾個(gè)步驟:

當(dāng)類(lèi)A的對(duì)象第一次被觀(guān)察的時(shí)候享怀,系統(tǒng)會(huì)在運(yùn)行期動(dòng)態(tài)創(chuàng)建類(lèi)A的派生類(lèi)添瓷。我們稱(chēng)為B鳞贷。

在派生類(lèi)B中重寫(xiě)類(lèi)A的setter方法搀愧,B類(lèi)在被重寫(xiě)的setter方法中實(shí)現(xiàn)通知機(jī)制咱筛。

類(lèi)B重寫(xiě)會(huì) class方法杆故,將自己偽裝成類(lèi)A处铛。類(lèi)B還會(huì)重寫(xiě)dealloc方法釋放資源罢缸。

系統(tǒng)將所有指向類(lèi)A對(duì)象的isa指針指向類(lèi)B的對(duì)象。

通俗一點(diǎn)的解釋是:當(dāng)注冊(cè)觀(guān)察者的時(shí)候做的哪些事情:

1.動(dòng)態(tài)的創(chuàng)建一個(gè)叫NSKVONotifying_Person的子類(lèi)

2.更改之前類(lèi)的 isa指針為子類(lèi)

3.傳入一堆參數(shù) 1.監(jiān)聽(tīng)者(將來(lái)調(diào)用observeValueForKeyPath)? 2.keypath(決定了重寫(xiě)哪個(gè)set方法)? 3.枚舉(決定傳哪些給你) 4.攜帶參數(shù)

4.根據(jù)keypath 重寫(xiě)子類(lèi)的set方法

//其實(shí)在子類(lèi)的set方法中是實(shí)現(xiàn)了下面三步

[super setWeight:weight];

//這兩個(gè)方法會(huì)調(diào)用監(jiān)聽(tīng)者的監(jiān)聽(tīng)者方法

[self willChangeValueForKey:@"weight"];

[self didChangeValueForKey:@"weight"];

5.在子類(lèi)的set方法中? 根據(jù)枚舉 保存所有的屬性值? 然后調(diào)用父類(lèi)的set方法 然后調(diào)用監(jiān)聽(tīng)者的observeValueForKeyPath... 把對(duì)應(yīng)的值傳出去通知監(jiān)聽(tīng)者發(fā)生了事情。所以不能依靠isa指針來(lái)確定對(duì)象是否是一個(gè)類(lèi)的成員寝贡。應(yīng)該使用class方法來(lái)確定對(duì)象實(shí)例的類(lèi)圃泡。

四颇蜡、KVO使用陷阱介紹:

首先辆亏,看一下KVO的使用場(chǎng)景,假設(shè)我們的目標(biāo)是在一個(gè)UITableViewController內(nèi)對(duì)tableview的contentOffset進(jìn)行實(shí)時(shí)監(jiān)測(cè)缤弦,很容易地使用KVO來(lái)實(shí)現(xiàn)為[使用場(chǎng)景]。

在初始化方法中加入:

1[_tableView addObserver:self forKeyPath:@"contentOffset"options:NSKeyValueObservingOptionNew context:nil];

在dealloc中移除KVO監(jiān)聽(tīng):

1[_tableView removeObserver:self forKeyPath:@"contentOffset"context:nil];

添加默認(rèn)的響應(yīng)回調(diào)方法:

1- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object3change:(NSDictionary *)change context:(void*)context5{7[self doSomethingWhenContentOffsetChanges];9}

通常的寫(xiě)法已經(jīng)完成,但是當(dāng)你在controller中添加多個(gè)KVO時(shí),所有的回調(diào)都是走同上述函數(shù)累提,那就必須對(duì)觸發(fā)回調(diào)函數(shù)的來(lái)源進(jìn)行判斷恍箭。判斷如下:

1- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object

2change:(NSDictionary *)change context:(void*)context

3{

4if(object== _tableView && [keyPath isEqualToString:@"contentOffset"]) {

5[self doSomethingWhenContentOffsetChanges];

6}

7? }

接著還有其他的陷阱,如 我們假設(shè)當(dāng)前類(lèi)(在例子中為UITableViewController)還有父類(lèi)扯夭,并且父類(lèi)也有自己綁定了一些其他KVO. 我們看到交洗,上述回調(diào)函數(shù)體中只有一個(gè)判斷,如果這個(gè)if不成立咆爽,這次KVO事件的觸發(fā)就會(huì)到此中斷了符糊。但事實(shí)上呛凶,若當(dāng)前類(lèi)無(wú)法捕捉到這個(gè)KVO漾稀,那很有可能是在他的superClass崭捍,或者super-superClass...中殷蛇,上述處理截?cái)嗔诉@個(gè)鏈晾咪。合理的處理方式應(yīng)該是這樣的:

1- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object

2change:(NSDictionary *)change context:(void*)context

3{

4if(object== _tableView && [keyPath isEqualToString:@"contentOffset"]) {

5[self doSomethingWhenContentOffsetChanges];

6}else{

7[super observeValueForKeyPath:keyPath ofObject:objectchange:change context:context];

8}

9}

還有潛在的問(wèn)題有可能出現(xiàn)在dealloc中對(duì)KVO的注銷(xiāo)上塞赂。KVO的一種缺陷(其實(shí)不能稱(chēng)為缺陷宴猾,應(yīng)該稱(chēng)為特性)是仇哆,當(dāng)對(duì)同一個(gè)keypath進(jìn)行兩次removeObserver時(shí)會(huì)導(dǎo)致程序crash讹剔,這種情況常常出現(xiàn)在父類(lèi)有一個(gè)kvo延欠,父類(lèi)在dealloc中remove了一次由捎,子類(lèi)又remove了一次的情況下狞玛。不要以為這種情況很少出現(xiàn)软驰!當(dāng)你封裝framework開(kāi)源給別人用或者多人協(xié)作開(kāi)發(fā)時(shí)是有可能出現(xiàn)的,而且這種crash很難發(fā)現(xiàn)心肪。不知道你發(fā)現(xiàn)沒(méi)锭亏,目前的代碼中context字段都是nil,那能否利用該字段來(lái)標(biāo)識(shí)出到底kvo是superClass注冊(cè)的蒙畴,還是self注冊(cè)的贰镣?

回答是可以的。我們可以分別在父類(lèi)以及本類(lèi)中定義各自的context字符串膳凝,比如在本類(lèi)中定義context為@"ThisIsMyKVOContextNotSuper";然后在dealloc中remove observer時(shí)指定移除的自身添加的observer。這樣iOS就能知道移除的是自己的kvo恭陡,而不是父類(lèi)中的kvo蹬音,避免二次remove造成crash。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末休玩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖组橄,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件遵班,死亡現(xiàn)場(chǎng)離奇詭異愿阐,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)习劫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)谤狡,“玉大人捕仔,你說(shuō)我怎么就攤上這事斜做】馕铮” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵靴姿,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)唁影,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任崔慧,我火速辦了婚禮皇钞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘卵贱。我一直安慰自己,他們只是感情好臭埋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著硼被,像睡著了一般屎媳。 火紅的嫁衣襯著肌膚如雪丹禀。 梳的紋絲不亂的頭發(fā)上葫盼,一...
    開(kāi)封第一講書(shū)人閱讀 51,521評(píng)論 1 304
  • 那天舔哪,我揣著相機(jī)與錄音,去河邊找鬼刹泄。 笑死外里,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的特石。 我是一名探鬼主播盅蝗,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼姆蘸!你這毒婦竟也來(lái)了墩莫?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤逞敷,失蹤者是張志新(化名)和其女友劉穎狂秦,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體推捐,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡裂问,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片堪簿。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡痊乾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出椭更,到底是詐尸還是另有隱情哪审,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布虑瀑,位于F島的核電站湿滓,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏缴川。R本人自食惡果不足惜茉稠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望把夸。 院中可真熱鬧而线,春花似錦、人聲如沸恋日。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)岂膳。三九已至誓竿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谈截,已是汗流浹背筷屡。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留簸喂,地道東北人毙死。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像喻鳄,于是被迫代替她去往敵國(guó)和親扼倘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

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

  • 寫(xiě)在前面 程序設(shè)計(jì)語(yǔ)言中有各種各樣的設(shè)計(jì)模式(pattern)和與此對(duì)應(yīng)的反設(shè)計(jì)模式(anti-pattern)纠拔,...
    Frankxp閱讀 4,925評(píng)論 0 23
  • KVO 作為 iOS 中一種強(qiáng)大并且有效的機(jī)制,為 iOS 開(kāi)發(fā)者們提供了很多的便利泛啸;我們可以使用 KVO 來(lái)檢測(cè)...
    JzRo閱讀 940評(píng)論 0 2
  • 本文結(jié)構(gòu)如下: Why? (為什么要用KVO) What? (KVO是什么) How? ( KVO怎么用) Mo...
    等開(kāi)會(huì)閱讀 1,651評(píng)論 1 21
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉绿语,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,715評(píng)論 0 9
  • 本文由我們團(tuán)隊(duì)的 糾結(jié)倫 童鞋撰寫(xiě)秃症。 文章結(jié)構(gòu)如下: Why? (為什么要用KVO) What? (KVO是什么...
    知識(shí)小集閱讀 7,412評(píng)論 7 105