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也会跟着相应变化。
期待大佬们能够友情的提出更深一层的问题,哈哈