iOS优秀开发文章

KVO原理探索

2020-08-05  本文已影响0人  Breezes

当一个类对某个对象的某个属性进行KVO监听时,系统为自动为该对象生成一个新的类,并把该对象的isa指针指向该类,以下面的Person类为例,通过代码看一下:

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end

使用object_getClass方法分别获取person对象在KVO监听前后所属的类

Person *person = [Person new];
NSLog(@"observing before = %@",object_getClass(person));
[person addObserver:self forKeyPath:@"name" options:  
NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
NSLog(@"observing after = %@",object_getClass(person));

打印结果⬇️

2020-08-01 15:47:51.387800+0800 KVO原理探索[2812:151048] observing before = Person
2020-08-01 15:47:51.388244+0800 KVO原理探索[2812:151048] observing after = NSKVONotifying_Person

但是通过打印person对象会发现:


image.png

person还是属于Person类,这是因为派生类NSKVONotifying_Person重写了Class方法,以欺骗调用者。

那么接下来如何确定person的isa指针被重新指向了NSKVONotifying_Person?

交换指针需要使用object_setClass方法,通过条件断点检测设置KVO后是否调用object_setClass

image.png
详细文章请参考:https://juejin.im/post/6855129007928082446

生成派生类后是如何发送通知给监听者?

通过断点看一下派生类的setName方法:


image.png

通过上面的输出,我们了解到NSKVONotifying_Person类的setter方法转换为Foundation框架的_NSSetObjectValueAndNotify函数。
继续看堆栈信息,


image.png
通过堆栈信息,我们看出KVO的通知调用顺序:
1. -[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:key:key:usingBlock:] 
2. -[NSObject(NSKeyValueObservingPrivate) _changeValueForKeys:count:maybeOldValuesDict:maybeNewValuesDict:usingBlock:] 
3.  -[Person setName:]
4.  NSKeyValueDidChange ()
5.  NSKeyValueNotifyObserver ()
6.  -[ViewController observeValueForKeyPath:ofObject:change:context:] 

总结一下:

使用KVO监听的person对象的isa指向了NSKVONotifying_Person类对象,那么他的set方法就在此类对象中,而且NSKVONotifying_Person类是Person的子类,当调用NSKVONotifying_Person的setName方法时,就会调用Foundation框架中的_NSSetObjectValueAndNotify方法。
_NSSetObjectValueAndNotify方法内部实现是:
首先调用willChangeValueForKey:
调用父类的setName:方法
调用didChangeValueForKey:当调用这个方法时内部就会触发监听器Oberser的监听方法observeValueForKeyPath:ofObject:change:context:这时候就能知道监听对象的属性值的改变了。

上一篇 下一篇

猜你喜欢

热点阅读