iOS KVO理解

概述

  1. KVO全稱Key-Value-Observing,也叫鍵值監(jiān)聽(tīng),是一種觀察者設(shè)計(jì)模式.提供了一種機(jī)制,當(dāng)指定的對(duì)象的屬性被修改后,對(duì)象就會(huì)收到一個(gè)通知.也就是說(shuō)每次指定的被觀察的對(duì)象的屬性被修改后,KVO就會(huì)自動(dòng)通知相應(yīng)的觀察者.
  2. 優(yōu)勢(shì):可以降低兩個(gè)類(業(yè)務(wù)邏輯和視圖控制的類)之間的耦合性.也就是說(shuō)可以很容易的實(shí)現(xiàn)視圖組件和數(shù)據(jù)模型的分離.當(dāng)數(shù)據(jù)模型的屬性值改變之后作為監(jiān)聽(tīng)器的視圖組件就會(huì)被激發(fā),激發(fā)時(shí)就會(huì)回調(diào)監(jiān)聽(tīng)器自身.
  3. 在Objective-C中要實(shí)現(xiàn)KVO則必須實(shí)現(xiàn)NSKeyValueObserving協(xié)議.但不用擔(dān)心,因?yàn)镹SObject已經(jīng)實(shí)現(xiàn)了該協(xié)議,因此幾乎所有的Objective-C對(duì)象都可以使用KVO.

KVO的方法

  1. 監(jiān)聽(tīng)方法
    - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;
    某一個(gè)對(duì)象(受虐狂,喜歡被人監(jiān)視),給自己添加一個(gè)監(jiān)聽(tīng)者(一般都是控制器本身self),讓監(jiān)聽(tīng)者監(jiān)聽(tīng)自身的某一個(gè)屬性. options就是要求監(jiān)聽(tīng)者記錄的信息. context就是要監(jiān)聽(tīng)者給自己添加一個(gè)標(biāo)記,以防止和別的對(duì)象的監(jiān)聽(tīng)混淆. 比如: 有兩個(gè)孩子讓家長(zhǎng)監(jiān)聽(tīng)他們做作業(yè).監(jiān)聽(tīng)者是家長(zhǎng),被監(jiān)聽(tīng)的對(duì)象是兩個(gè)孩子.
    參數(shù):
    • observer 觀察者,也就是KVO的訂閱者,訂閱者必須實(shí)現(xiàn)協(xié)議方法(下面有).
    • keyPath 描述將要觀察的對(duì)象的屬性,也就是被觀察者的屬性.
    • options KVO的屬性配置.
      • NSKeyValueObservingOptionNewchange字典包括改變后的值
      • NSKeyValueObservingOptionOldchange字典包括改變前的值
      • NSKeyValueObservingOptionInitial注冊(cè)后立刻觸發(fā)KVO通知
      • NSKeyValueObservingOptionPrior值改變前是否也要通知(這個(gè)key決定了是否在改變前改變后通知兩次)
    • context上下文,這個(gè)會(huì)傳遞到協(xié)議方法中,用來(lái)區(qū)分消息,處理不同的KVO.所以應(yīng)當(dāng)是不同的.
  2. 解除監(jiān)聽(tīng)
    - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath; 刪除指定keyPath的監(jiān)聽(tīng)器.
    - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(nullable void *)context NS_AVAILABLE(10_7, 5_0);刪除特定上下文標(biāo)記的指定keyPath的監(jiān)聽(tīng)器.
  3. 回調(diào)監(jiān)聽(tīng)
    - (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context;
    參數(shù):
    • keyPath 被監(jiān)聽(tīng)的keyPath
    • object 被監(jiān)聽(tīng)的修改后的對(duì)象,可以獲取修改的對(duì)象的屬性
    • change 保存信息改變的字典(可能有舊的值,新的值等
    • context 上下文

使用步驟

  1. 注冊(cè)KVO監(jiān)聽(tīng).
  2. 實(shí)現(xiàn)代理方法.
  3. 移除監(jiān)聽(tīng).在dealloc方法中移除.

KVO使用注意事項(xiàng)

非常重要

  1. 當(dāng)你在同一個(gè)ViewController中添加多個(gè)KVO的時(shí)候,無(wú)論哪個(gè)KVO都是走- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context;回調(diào)方法.所以需要對(duì)想要的監(jiān)聽(tīng)對(duì)象進(jìn)行區(qū)分,以便指定不同的邏輯.
    這里是對(duì)_tableView對(duì)象的contentOffset屬性監(jiān)聽(tīng).
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
     if (object == _tableView && [keyPath isEqualToString:@"contentOffset"]) {
        [self doSomething];
     }
 }
  1. 我們假設(shè)當(dāng)前類(在例子中為UITableViewController)還有父類,并且父類也有自己綁定了一些其他KVO呢?我們看到,上述回調(diào)函數(shù)體中只有一個(gè)判斷,如果這個(gè)if不成立,這次KVO事件的觸發(fā)就會(huì)到此中斷了。但事實(shí)上,若當(dāng)前類無(wú)法捕捉到這個(gè)KVO,那很有可能是在他的superClass,或者super-superClass...中,上述處理砍斷了這個(gè)鏈。合理的處理方式應(yīng)該是這樣的:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (object == _tableView && [keyPath isEqualToString:@"contentOffset"]) {
        [self doSomethingWhenContentOffsetChanges];
} else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; }
}

但是這個(gè)是要自己搞清楚,父類中到底有沒(méi)有注冊(cè)KVO.如果監(jiān)聽(tīng)一個(gè)對(duì)象的兩個(gè)屬性,兩個(gè)屬性的改變時(shí)分開(kāi)執(zhí)行的,就會(huì)觸發(fā)兩次代理方法.如圖:

1.png
  1. KVO的一個(gè)特性,當(dāng)對(duì)同一個(gè)keyPath進(jìn)行多余一次的removeObserver的時(shí)候會(huì)導(dǎo)致程序crash.這種情況常常出現(xiàn)在父類有一個(gè)kvo,父類在dealloc中remove了一次,子類又remove了一次的情況下。不要以為這種情況很少出現(xiàn)!當(dāng)你封裝framework開(kāi)源給別人用或者多人協(xié)作開(kāi)發(fā)時(shí)是有可能出現(xiàn)的,而且這種crash很難發(fā)現(xiàn).解決辦法就是我們可以分別在父類以及本類中定義各自的context字符串,這樣iOS就能知道移除的是自己的kvo,而不是父類中的kvo,避免二次remove造成crash.
  2. 把監(jiān)聽(tīng)到對(duì)象的屬性值改變賦值的時(shí)候,一定要注意監(jiān)聽(tīng)對(duì)象的值的類型.
    把監(jiān)聽(tīng)到對(duì)象的屬性值改變賦值的時(shí)候,一定要注意監(jiān)聽(tīng)對(duì)象的值的類型.
    把監(jiān)聽(tīng)到對(duì)象的屬性值改變賦值的時(shí)候,一定要注意監(jiān)聽(tīng)對(duì)象的值的類型.
    重要的事情說(shuō)三遍
  3. 如果監(jiān)聽(tīng)一個(gè)對(duì)象的多個(gè)屬性,任何一個(gè)屬性的改變都會(huì)走代理方法,也就是說(shuō)對(duì)屬性的監(jiān)聽(tīng),是分開(kāi)執(zhí)行的.

Demo 下載地址

https://github.com/mancongiOS/KVO.git

說(shuō)明

  1. KVO注意事項(xiàng)1,2,3條轉(zhuǎn)載于 編程小翁@博客園
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • KVO 作為 iOS 中一種強(qiáng)大并且有效的機(jī)制,為 iOS 開(kāi)發(fā)者們提供了很多的便利;我們可以使用 KVO 來(lái)檢測(cè)...
    JzRo閱讀 1,082評(píng)論 0 2
  • 寫在前面 程序設(shè)計(jì)語(yǔ)言中有各種各樣的設(shè)計(jì)模式(pattern)和與此對(duì)應(yīng)的反設(shè)計(jì)模式(anti-pattern),...
    Frankxp閱讀 5,012評(píng)論 0 23
  • 本文結(jié)構(gòu)如下: Why? (為什么要用KVO) What? (KVO是什么) How? ( KVO怎么用) Mo...
    等開(kāi)會(huì)閱讀 1,730評(píng)論 1 21
  • 本文由我們團(tuán)隊(duì)的 糾結(jié)倫 童鞋撰寫。 文章結(jié)構(gòu)如下: Why? (為什么要用KVO) What? (KVO是什么...
    知識(shí)小集閱讀 7,485評(píng)論 7 105
  • KCV 其實(shí)由于ObjC的語(yǔ)言特性,你根部不必進(jìn)行任何操作就可以進(jìn)行屬性的動(dòng)態(tài)讀寫,這種方式就是Key Value...
    TYM閱讀 1,144評(píng)論 0 4

友情鏈接更多精彩內(nèi)容