KVO可能你們都比較熟了,網上的文章也有不少~我第一次把本身對它的理解整理出來~歡迎你們來找茬~bash
KVO是一種機制,當對象(被觀察)的某個屬性發生更改時,對象能夠得到通知,並做出相應處理。那麼他是怎麼監聽的呢?ui
KVO是用了isa-swizzling來實現的。當對象被kvo觀察的時候,此對象的isa指針會改變,指向一箇中間的類,而不是它真正的類。而後重寫setter方法。spa
//一、建立一個Person類,屬性變量有age
self.p2 = [[Person alloc]init];
self.p2.age = 1;
//二、輸出結果:====p2未監聽前,isa指針Person
NSLog(@"====p2未監聽前,isa指針%@", object_getClass(self.p2));
//三、p2的age屬性添加監聽
[self.p2 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew| NSKeyValueObservingOptionOld context:nil];
//四、輸出結果:====p2監聽後,isa指針NSKVONotifying_Person
NSLog(@"====p2監聽後,isa指針%@", object_getClass(self.p2));
//五、移除監聽
[self.p2 removeObserver:self forKeyPath:@"age" context:nil];
//六、輸出結果:====p2移除監聽後,isa指針Person
NSLog(@"====p2移除監聽後,isa指針%@", object_getClass(self.p2));
複製代碼
由上段代碼能夠看出,p2被監聽前,isa指針指向Person類。在添加監聽後,isa指針指向了NSKVONotifying_Person類。指針
所以,不能用isa指針來獲取他真正的類,而是經過class方法來獲取。code
將上面的第4步的輸出。多輸出一個isa指針指向的類的父類。server
//====p2監聽後,isa指針NSKVONotifying_Person====isa指針指向的類的父類指針是Person
NSLog(@"====p2監聽後,isa指針%@====isa指針指向的類的父類指針是%@", object_getClass(self.p2),class_getSuperclass((Class)object_getClass(self.p2)));
複製代碼
由上可看出,生成的中間類NSKVONotifying_Person類是原類Person類的子類。對象
咱們手動實現一個KVO來證實~rem
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
BOOL automatic = NO;
if ([theKey isEqualToString:@"age"]) {
automatic = NO;
}
else {
automatic = [super automaticallyNotifiesObserversForKey:theKey];
}
return automatic;
}
複製代碼
此時,再跑一下程序,會發現當咱們改變age的時候,不能再收到通知。也就是如下方法,不會被調用。get
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"%@的%@改變了%@", object, keyPath, change);
}
複製代碼
在Person類中重寫setter方法string
- (void)setAge: (NSInteger)age {
//在改變值前調用
[self willChangeValueForKey:@"age"];
_age = age;
//在改變值以後調用
[self didChangeValueForKey:@"age"];
}
複製代碼
OK,在重寫setter方法以後,咱們又能收到通知了~若是咱們想只有在年齡值改變的時候,才收到通知。那咱們能夠將setter方法改爲以下:
- (void)setAge: (NSInteger)age {
if (_age != age) {
//在改變值前調用
[self willChangeValueForKey:@"age"];
_age = age;
//在改變值以後調用
[self didChangeValueForKey:@"age"];
}
}
複製代碼
這樣,在咱們給age賦值和上次同樣的時候,咱們不會再收到通知。也就是說下面這種狀況只會收到兩次通知。
self.p2.age = 100;
self.p2.age = 110;
self.p2.age = 110;
複製代碼
咱們再給person加個屬性變量birthYear,當age變的時候,birthYear跟着變化。那麼setter方法能夠寫成下面這樣
- (void)setAge: (NSInteger)age {
if (_age != age) {
//在改變值前調用
[self willChangeValueForKey:@"age"];
[self willChangeValueForKey:@"birthYear"];
NSInteger gap = age - _age;
_age = age;
_birthYear = _birthYear + gap;
//在改變值以後調用
[self didChangeValueForKey:@"age"];
[self didChangeValueForKey:@"birthYear"];
}
}
複製代碼
這樣,當age變的時候,birthYear也會跟着相應變化。
期待大佬們能夠友情的提出更深一層的問題,哈哈