KVO 深入學(xué)習(xí)


KVO 的基本調(diào)用

1. 在需要監(jiān)聽的類中,加入以下代碼
[car addObserver:self 
      forKeyPath:@"要監(jiān)聽的屬性" 
         options:NSKeyValueObservingOptionNew context:nil];
(_本例中 car 是需要被監(jiān)聽的一個(gè)類的實(shí)例_)

2. 添加一個(gè)屬性監(jiān)聽的回調(diào)函數(shù)
- (void)observeValueForKeyPath:(NSString *)keyPath 
                      ofObject:(id)object 
                        change:(NSDictionary<NSKeyValueChangeKey,id> *)change 
                       context:(void *)context {
    // 如果屬性值有改動(dòng),執(zhí)行該函數(shù)
    if([keyPath isEqualToString:@"要監(jiān)聽的屬性"]) {
    }
}

3. 當(dāng)類被銷毀時(shí),需要取消監(jiān)聽
- (void)dealloc {
    [self removeObserver:self forKeyPath:@"要監(jiān)聽的屬性"];
}

  • NSKeyValueObservingOptions說明:
    • NSKeyValueObservingOptionNew:在回調(diào)函數(shù)中的change對象中會(huì)返回該屬性修改后的值。
    • NSKeyValueObservingOptionOld:在回調(diào)函數(shù)中的change對象中會(huì)返回該屬性修改前的值。
    • NSKeyValueObservingOptionInitial:在剛開始設(shè)置addObserver:監(jiān)聽的時(shí)候,就默認(rèn)觸發(fā)一次回調(diào)函數(shù)。
    • NSKeyValueObservingOptionPrior:當(dāng)被監(jiān)聽的屬性修改的前后都會(huì)觸發(fā)一次回調(diào)函數(shù)。
  • (NSDictionary<NSKeyValueChangeKey,id> *)change說明:
    • 被監(jiān)聽的屬性名為 Key,以 NSKeyValueObservingOptions 配置而返回的屬性值為 Value。
    • kind的 Value 有以下4種意思
      • NSKeyValueChange = 1:值改變
      • NSKeyValueChangeInsertion = 2:插入新值(集合類)
      • NSKeyValueChangeRemoval = 3:移除值(集合類)
      • NSKeyValueChangeReplacement = 4:替換集合類中的值

如果自定義 KVO?

一般情況下,只要我們按上面的步驟設(shè)置好,則默認(rèn)情況下只要有值改變則會(huì)自動(dòng)觸發(fā)回調(diào)函數(shù),如果我們想自定義的觸發(fā)的條件,可按以下步驟進(jìn)行設(shè)置:

1. 在被監(jiān)聽的類中,將需要自定義的屬性設(shè)置為不自動(dòng)觸發(fā)監(jiān)聽(在本例中就是上面提到的 Car 類)。
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
    if([key isEqualToString:@"自定義的屬性"]) {
        return NO;
    }
    // 默認(rèn)為自動(dòng)觸發(fā)
    return YES;
}

2. 在需要手動(dòng)觸發(fā)的函數(shù)中,寫入以下邏輯:
- (void)buttonAction:(UIButton *)sender {
    [self.car willChangeValueForKey:@"name"];
    self.car.name = @"要更新的值";
    [self.car didChangeValueForKey:@"name"];
}

按該步驟就能自定義觸發(fā)值的監(jiān)聽。


KVO 監(jiān)聽多個(gè)屬性的優(yōu)化


* 當(dāng)我們想監(jiān)聽多個(gè)屬性的時(shí)候,一般情況下會(huì)寫成以下方式:
[car addObserver:self forKeyPath:@"屬性 1" 
                         options:NSKeyValueObservingOptionNew context:nil];
[car addObserver:self forKeyPath:@"屬性 2" 
                          options:NSKeyValueObservingOptionNew context:nil];
[car addObserver:self forKeyPath:@"屬性 3" 
                          options:NSKeyValueObservingOptionNew context:nil];

* 為了減少代碼的冗余,我們可以在被監(jiān)聽的類里面,使用以下方式替換上面的代碼:
 - 監(jiān)聽類中:
  [car addObserver:self forKeyPath:@"屬性" 
                           options:NSKeyValueObservingOptionNew context:nil];

 - 被監(jiān)聽的類里面:
  + (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key  {
    NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
    if([key isEqualToString:@"屬性名"]) {
        NSArray *arr = @[@"屬性 1",@"屬性 2",@"屬性 3"];
        keyPaths = [keyPaths setByAddingObjectsFromArray:arr];
    }
    return keyPaths
}

KVO 實(shí)現(xiàn)原理

  1. 其實(shí)當(dāng)我們在執(zhí)行 addObserver 監(jiān)聽方法的時(shí)候,就是將被監(jiān)聽的實(shí)例(car)的屬性 set 方法進(jìn)行了重寫。
  2. 具體的實(shí)現(xiàn)方式就是通過 runtime 機(jī)制,創(chuàng)建了一個(gè)中間類,該類是原類的子類;并且修改 isa指針,指向這個(gè)中間類,這樣通過 OC 的消息轉(zhuǎn)發(fā)機(jī)制,首先就會(huì)執(zhí)行中間類的 set 方法,通過這種方式,就可以在中間類的 set 方法中去執(zhí)行 willChangeValueForKeydidChangeValueForKey方法,同時(shí)修改屬性的值。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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