当一个类对某个对象的某个属性进行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
:
详细文章请参考: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:这时候就能知道监听对象的属性值的改变了。
网友评论