KVO 的基本調用
1. 在需要監(jiān)聽的類中公你,加入以下代碼
[car addObserver:self
forKeyPath:@"要監(jiān)聽的屬性"
options:NSKeyValueObservingOptionNew context:nil];
(_本例中 car 是需要被監(jiān)聽的一個類的實例_)
2. 添加一個屬性監(jiān)聽的回調函數
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context {
// 如果屬性值有改動踊淳,執(zhí)行該函數
if([keyPath isEqualToString:@"要監(jiān)聽的屬性"]) {
}
}
3. 當類被銷毀時,需要取消監(jiān)聽
- (void)dealloc {
[self removeObserver:self forKeyPath:@"要監(jiān)聽的屬性"];
}
-
NSKeyValueObservingOptions
說明:- NSKeyValueObservingOptionNew:在回調函數中的
change
對象中會返回該屬性修改后的值陕靠。 - NSKeyValueObservingOptionOld:在回調函數中的
change
對象中會返回該屬性修改前的值迂尝。 - NSKeyValueObservingOptionInitial:在剛開始設置
addObserver:
監(jiān)聽的時候,就默認觸發(fā)一次回調函數懦傍。 - NSKeyValueObservingOptionPrior:當被監(jiān)聽的屬性修改的前后都會觸發(fā)一次回調函數雹舀。
- NSKeyValueObservingOptionNew:在回調函數中的
-
(NSDictionary<NSKeyValueChangeKey,id> *)change
說明:- 被監(jiān)聽的屬性名為 Key,以 NSKeyValueObservingOptions 配置而返回的屬性值為 Value粗俱。
-
kind
的 Value 有以下4種意思- NSKeyValueChange = 1:值改變
- NSKeyValueChangeInsertion = 2:插入新值(集合類)
- NSKeyValueChangeRemoval = 3:移除值(集合類)
- NSKeyValueChangeReplacement = 4:替換集合類中的值
如果自定義 KVO说榆?
一般情況下,只要我們按上面的步驟設置好寸认,則默認情況下只要有值改變則會自動觸發(fā)回調函數签财,如果我們想自定義的觸發(fā)的條件,可按以下步驟進行設置:
1. 在被監(jiān)聽的類中偏塞,將需要自定義的屬性設置為不自動觸發(fā)監(jiān)聽(在本例中就是上面提到的 Car 類)唱蒸。
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
if([key isEqualToString:@"自定義的屬性"]) {
return NO;
}
// 默認為自動觸發(fā)
return YES;
}
2. 在需要手動觸發(fā)的函數中,寫入以下邏輯:
- (void)buttonAction:(UIButton *)sender {
[self.car willChangeValueForKey:@"name"];
self.car.name = @"要更新的值";
[self.car didChangeValueForKey:@"name"];
}
按該步驟就能自定義觸發(fā)值的監(jiān)聽灸叼。
KVO 監(jiān)聽多個屬性的優(yōu)化
* 當我們想監(jiān)聽多個屬性的時候神汹,一般情況下會寫成以下方式:
[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 實現原理
- 其實當我們在執(zhí)行
addObserver
監(jiān)聽方法的時候屁魏,就是將被監(jiān)聽的實例(car)的屬性set
方法進行了重寫。 - 具體的實現方式就是通過
runtime
機制捉腥,創(chuàng)建了一個中間類氓拼,該類是原類的子類;并且修改isa
指針抵碟,指向這個中間類桃漾,這樣通過 OC 的消息轉發(fā)機制,首先就會執(zhí)行中間類的set
方法拟逮,通過這種方式撬统,就可以在中間類的set
方法中去執(zhí)行willChangeValueForKey
和didChangeValueForKey
方法,同時修改屬性的值敦迄。