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ù)。
- NSKeyValueObservingOptionNew:在回調(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)原理
- 其實(shí)當(dāng)我們在執(zhí)行
addObserver監(jiān)聽方法的時(shí)候,就是將被監(jiān)聽的實(shí)例(car)的屬性set方法進(jìn)行了重寫。 - 具體的實(shí)現(xiàn)方式就是通過
runtime機(jī)制,創(chuàng)建了一個(gè)中間類,該類是原類的子類;并且修改isa指針,指向這個(gè)中間類,這樣通過 OC 的消息轉(zhuǎn)發(fā)機(jī)制,首先就會(huì)執(zhí)行中間類的set方法,通過這種方式,就可以在中間類的set方法中去執(zhí)行willChangeValueForKey和didChangeValueForKey方法,同時(shí)修改屬性的值。