一郎科技|iOS底层系列:KVO( 二 )


3. NSKVONotifying_Person中的setAge:的实现个人感觉 , 挖掘setAge:的实现是比较难的 。
我们通过下面的方法打印一下方法IMP的地址
NSLog(@"person1添加KVO之前的两个setAge地址: \n -person1:%p -- person2:%p",[person1 methodForSelector:@selector(setAge:)],[person2 methodForSelector:@selector(setAge:)]);[person1 addObserver:selfforKeyPath:@"age"options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOldcontext:nil];NSLog(@"person1添加KVO之后的两个setAge地址: \n -person1:%p -- person2:%p",[person1 methodForSelector:@selector(setAge:)],[person2 methodForSelector:@selector(setAge:)]);输出:
person1添加KVO之前的两个setAge地址: -person1:0x1005ee850 -- person2:0x1005ee850 person1添加KVO之后的两个setAge地址: -person1:0x7fff25623f0e -- person2:0x1005ee850我们可以看到 , 在添加KVO监听前后 , person2的setAge实现的地址没有发生变化 , 但是person1的变了 , 我们在打印台用lldb命令打印一下0x7fff25623f0e
(lldb) p (IMP)0x7fff25623f0e(IMP) $1 = 0x00007fff25623f0e (Foundation`_NSSetIntValueAndNotify)可以看到setAge的实现其实就是调用了Foundation框架的_NSSetIntValueAndNotify方法 。 那具体的_NSSetIntValueAndNotify内部实现是怎么样的呢?因为Foundation不开源 , 我们只能猜测 , 并对我们的猜测做出相应的验证 。
下面是我看过一些大神的分析之后猜测的_NSSetIntValueAndNotify实现的 伪代码 (特此鸣谢我们的MJ老师)
void _NSSetIntValueAndNotify() {[self willChangeValueForKey:@"age"];[super setAge:age];[self didChangeValueForKey:@"age"];}- (void)didChangeValueForKey:(NSString *)keyPath {// 通知监听者,已经修改完毕[observer observeValueForKeyPath:keyPath ofObject:self change:nil context:nil];}如何验证一下我们的猜测呢?
我们知道NSKVONotifying_Person类中没有实现willChangeValueForKey和didChangeValueForKey这两个方法 , 所以我们可以在NSKVONotifying_Person的父类 , 也就是Person类型重写这两个方法 , 改造完之后的Person类里面应该是下面这样子:
@interface Person : NSObject@property (nonatomic, assign) int age;@end@implementation Person- (void)setAge:(int)age {_age = age;NSLog(@"age:%d",age);}- (void)willChangeValueForKey:(NSString *)key {[super willChangeValueForKey:key];NSLog(@"willChangeValueForKey");}- (void)didChangeValueForKey:(NSString *)key {NSLog(@"didChangeValueForKey - begin");[super didChangeValueForKey:key];NSLog(@"didChangeValueForKey - end");}@endperson1在添加了KVO监听 , 并设置值 person1.age = 10; 之后 , 输出如下:
2020-09-07 23:47:13.787066+0800 KVO[8122:119707] willChangeValueForKey2020-09-07 23:47:13.787229+0800 KVO[8122:119707] age:102020-09-07 23:47:13.787334+0800 KVO[8122:119707] didChangeValueForKey - begin2020-09-07 23:47:13.787592+0800 KVO[8122:119707]-- age -- {kind = 1;new = 10;old = 0;}2020-09-07 23:47:13.787732+0800 KVO[8122:119707] didChangeValueForKey - end输出结果与我们的猜测一致 。
其他小知识点NSKVONotifying_Person也重写了class方法 , 使用[person1 class]的时候返回的是Person , 其实也很容易理解 , 只是为了隐藏NSKVONotifying_Person这个类 , 尽量隐藏KVO的内部实现 。
大家也可以看一下我下面附上的参考文章 , 写的很不错 。


推荐阅读