KVO原理

1.kvo簡介

KVO全稱KeyValueObserving算吩,是蘋果提供的一套事件通知機(jī)制闻妓。允許對象監(jiān)聽另一個對象特定屬性的改變返干,并在改變時接收到事件。由于KVO的實(shí)現(xiàn)機(jī)制席揽,所以對屬性才會發(fā)生作用顽馋,一般繼承自NSObject的對象都默認(rèn)支持KVO。

KVO和NSNotificationCenter都是iOS中觀察者模式的一種實(shí)現(xiàn)幌羞。區(qū)別在于寸谜,相對于被觀察者和觀察者之間的關(guān)系,KVO是一對一的属桦,而不一對多的熊痴。KVO對被監(jiān)聽對象無侵入性,不需要修改其內(nèi)部代碼即可實(shí)現(xiàn)監(jiān)聽聂宾。

KVO可以監(jiān)聽單個屬性的變化果善,也可以監(jiān)聽集合對象的變化。通過KVC的mutableArrayValueForKey:等方法獲得代理對象系谐,當(dāng)代理對象的內(nèi)部對象發(fā)生改變時巾陕,會回調(diào)KVO監(jiān)聽的方法。集合對象包含NSArray和NSSet蔚鸥。

看下官方文檔

KVO是一套通知機(jī)制惜论,這臺機(jī)制讓一個對象可以收到一個通知,這個通知描述的是另一個對象它的屬性發(fā)生的變化止喷。

2.kvo使用

運(yùn)行:


這就是kvo的基本使用 官網(wǎng)上也有介紹

Person對象監(jiān)聽Account對象

1馆类、注冊觀察者:addObserver:forKeyPath:options:context:

2、實(shí)現(xiàn)通知回調(diào):observeValueForKeyPath:ofObject:change:context:

3弹谁、移除觀察者:?removeObserver:forKeyPath:

// observer: 觀察誰乾巧,弱引用添加

// keyPath:觀察什么

// options:觀察什么的變化句喜,枚舉值 - 新值、舊值

// context:上下文 為了進(jìn)行觀察這區(qū)分沟于,解決問題咳胃,可以在監(jiān)聽回調(diào)方法中減少判斷,代碼可讀性會降低

context

官方:

addObserver:forKeyPath:options:context: message中的上下文指針包含將在相應(yīng)的更改通知中傳遞回觀察者的任意數(shù)據(jù)旷太。您可以指定NULL并完全依賴于鍵路徑字符串來確定更改通知的來源展懈,但是這種方法可能會給其超類出于不同的原因也在觀察相同鍵路徑的對象造成問題。

一種更安全供璧、更可擴(kuò)展的方法是使用content來確保接收到的通知是發(fā)送給觀察者的存崖,而不是超類。

類中唯一命名的靜態(tài)變量的地址可以作為一個良好的上下文(content)睡毒。在超類或子類中以類似方式選擇的上下文將不太可能重疊来惧。您可以為整個類選擇一個上下文,并依賴通知消息中的關(guān)鍵路徑字符串來確定更改的內(nèi)容演顾」┎螅或者,您可以為觀察到的每個鍵路徑創(chuàng)建不同的上下文钠至,這樣就完全不必進(jìn)行字符串比較葛虐,從而提高通知解析的效率。

大意就是:你和你的父類可能同時監(jiān)聽了相同的key path棕洋;如果沒有上下文context的支持無法做到區(qū)分挡闰。



這樣就做出了區(qū)分

移除觀察者的純粹性和必要性

1乒融、闡述了純粹性:已除的肯定是之前已經(jīng)添加過的觀察者掰盘,如果未添加就會報(bào)NSRangeException異常,即不能重復(fù)添加

2赞季、闡述了必要性:首先觀察者不會自動的remove;被觀察的對象還會向觀察者發(fā)送通知愧捕,如果此時觀察者已經(jīng)released,那么就會出現(xiàn)內(nèi)存訪問異常

3、給出建議使用:在init或viewDidLoad中注冊觀察者addObserver;dealloc中移除觀察者removeObserver

手動和自動

使用automaticallyNotifiesObserversForKey:方法可以實(shí)現(xiàn)對當(dāng)前對象的某個屬性的自動觀察做開關(guān)處理申钩。

設(shè)置為no時就不會響應(yīng) 也就是通過automaticallyNotifiesObserversForKey類方法屏蔽某個屬性的自動通知邏輯

然后手動實(shí)現(xiàn)下面方法就可以正常響應(yīng)

路徑處理

比如我們監(jiān)聽下載的進(jìn)度時次绘,可知下載的進(jìn)度 = 已下載量 / 總下載量,可我們又不想監(jiān)聽兩個量撒遣,這個時候可以使用keyPathsForValuesAffectingValueForKey方法進(jìn)行觀察處理邮偎。


運(yùn)行后結(jié)果:

數(shù)組觀察


輸出發(fā)現(xiàn)沒有變化 但是數(shù)組的確增加了

官方文檔:

根據(jù)官方文檔做如下處理,將修改數(shù)組的位置修改為


typedef NS_ENUM(NSUInteger, NSKeyValueChange) {

? ? NSKeyValueChangeSetting = 1, // 設(shè)置

? ? NSKeyValueChangeInsertion = 2, // 插入

? ? NSKeyValueChangeRemoval = 3, // 移除

? ? NSKeyValueChangeReplacement = 4, // 替換

};

上方的2對應(yīng)數(shù)組的addObject等操作义黎,3對應(yīng)數(shù)組的removeObject等操作禾进,4對應(yīng)數(shù)組的replaceObjectAtIndex:withObject:的操作

KVO底層原理探索

KVO觀察屬性


** 通過打印結(jié)果可知,KVO只對屬性進(jìn)行監(jiān)聽廉涕,對成員變量不監(jiān)聽. ** 屬性與成員變量的區(qū)別在于屬性存在 setter方法泻云,而成員變量沒有setter

中間類

self.person的isa的指向發(fā)生了變化

通過斷點(diǎn)打印可知艇拍,在添加監(jiān)聽之后,self.person的isa重新指向了NSKVONotifying_LGPerson類宠纯,對比之前的類多了NSKVONotifying_前綴卸夕。

** 派生類 **:即某個類的子類

自定義方法,查看LGPerson的子類情況婆瓜,代碼如下:


并在self.person添加監(jiān)聽的前后進(jìn)行調(diào)用該方法快集,

查看打印結(jié)果。

其中LGStudent是LGPerson的一個子類廉白,在添加監(jiān)聽方法后發(fā)現(xiàn)LGPerson多了一個子類NSKVONotifying_LGPerson碍讨,說明添加監(jiān)聽方法后,self.person的isa指向了LGPerson的子類蒙秒。

中間類中有什么

是否存在什么方法勃黍。

自定義代碼打印類的方法。

調(diào)用方法

打印

從打印結(jié)果可知晕讲,在NSKVONotifying_LGPerson類中添加了四個方法覆获,分別為:setNickName、class瓢省、dealloc弄息、_isKVOA這四個方法。

_isKVOA?判斷當(dāng)前是否為KVO類

dealloc?釋放

setNickName?需要查看是繼承還是重寫的勤婚,答案是重寫的方法

class

那我們繼續(xù)打印下另一個子類student的方法

子類student先不實(shí)現(xiàn)任何方法?

打印沒有

接下來實(shí)現(xiàn)一個setNickName

打印就有了setNickName

這個時候發(fā)現(xiàn)LGStudent中存在setNickName方法摹量,由此可知NSKVONotifying_LGPerson中的setNickName為重寫的方法

isa是否會還原

NSKVONotifying_LGPerson類是否會移除,self.person的isa是否會指回來馒胆。

在ViewController中的dealloc方法中添加斷點(diǎn)缨称,查看移除之后的self.personisa的指向。


通過打印可知祝迂,在移除監(jiān)聽后睦尽,self.person的isa會重新指向LGPerson

在pop的ViewController也做LGPerson的子類的打印,


結(jié)果如下:


發(fā)現(xiàn)當(dāng)監(jiān)聽被移除后型雳,NSKVONotifying_LGPerson類并沒有被移除当凡,而是仍然存在。

結(jié)論是 對象的isa會重新指向 動態(tài)生成的類會一直存在

重寫的setter方法內(nèi)做了什么處理

猜測一下纠俭,首先沿量,在調(diào)用NSKVONotifying_LGPerson重寫setter方法的時候,改變的是其父類LGPerson的nickName的值冤荆,那么在重寫的setter方法中一定有對父類nickName進(jìn)行傳值的操作朴则。

設(shè)置觀察self->_pserson->_nickName,具體命令為:

watchpoint set variable self->_person->_nickName

執(zhí)行命令后的結(jié)果匙赞。進(jìn)入斷點(diǎn)佛掖。

查看左側(cè)堆棧妖碉,可知2、3芥被、4步是隱藏邏輯為匯編語言

堆棧編號1為LGPerson的setter方法

堆棧編號2在斷點(diǎn)前后分別執(zhí)行NSKeyValueWillChange方法以及NSKeyValueDidChange方法欧宜。

結(jié)論:在willChange與didChange之間調(diào)用父類的賦值方法,因此拴魄,父類的值得以改變冗茸。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市匹中,隨后出現(xiàn)的幾起案子夏漱,更是在濱河造成了極大的恐慌,老刑警劉巖顶捷,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挂绰,死亡現(xiàn)場離奇詭異,居然都是意外死亡服赎,警方通過查閱死者的電腦和手機(jī)葵蒂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來重虑,“玉大人践付,你說我怎么就攤上這事∪崩鳎” “怎么了永高?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長提针。 經(jīng)常有香客問我命爬,道長,這世上最難降的妖魔是什么关贵? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任遇骑,我火速辦了婚禮卖毁,結(jié)果婚禮上揖曾,老公的妹妹穿的比我還像新娘。我一直安慰自己亥啦,他們只是感情好炭剪,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著翔脱,像睡著了一般奴拦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上届吁,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天错妖,我揣著相機(jī)與錄音绿鸣,去河邊找鬼。 笑死暂氯,一個胖子當(dāng)著我的面吹牛潮模,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播痴施,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼擎厢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了辣吃?” 一聲冷哼從身側(cè)響起动遭,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎神得,沒想到半個月后厘惦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡哩簿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年绵估,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卡骂。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡国裳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出全跨,到底是詐尸還是另有隱情缝左,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布浓若,位于F島的核電站渺杉,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏挪钓。R本人自食惡果不足惜是越,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望碌上。 院中可真熱鬧倚评,春花似錦、人聲如沸馏予。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽霞丧。三九已至呢岗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背后豫。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工悉尾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人挫酿。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓焕襟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親饭豹。 傳聞我的和親對象是個殘疾皇子鸵赖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345