先给答案,我们再探具体:
利用RuntimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的子类
当修改instance对象的属性时,会调用Foundation的_NSSetXXXValueAndNotify函数
willChangeValueForKey:
父类原来的setter
didChangeValueForKey:
内部会触发监听器(Oberser)的监听方法( observeValueForKeyPath:ofObject:change:context:)
我们要手动触发kvo的话,就调用willChangeValueForKey: 和didChangeValueForKey: 方法
我们创建一个person对象,并含有age属性,赋值1,添加监听方法,在touchsbegin方法里面改变age的值,
实现监听方法,我们在添加kvo监听前,分别打印person1的类对象的值,如下
我们发现打印不同,添加监听后,新生成一个类NSKVONotifying_Peerson类对象,根据我们之前学的isa指针,实例对象的isa指针指向他的类对象,我们可以用下图表示,
在这个新生成的类对象里面实现的就是
- (void)setAge:(int)age
{
_NSSetIntValueAndNotify();
}
// 伪代码
void _NSSetIntValueAndNotify()
{
[self willChangeValueForKey:@"age"];
[super setAge:age];
[self didChangeValueForKey:@"age"];
}
- (void)didChangeValueForKey:(NSString *)key
{
// 通知监听器,某某属性值发生了改变
[oberser observeValueForKeyPath:key ofObject:self change:nil context:nil];
}
有人会问我们为什么知道是调用了_NSSetIntValueAndNotify()这个函数,接下来就来验证,
// [self.person1 methodForSelector:@selector(setAge:)] 这个为获得IMP
NSLog(@"person1添加KVO监听之前 - %p %p",
[self.person1 methodForSelector:@selector(setAge:)],
[self.person2 methodForSelector:@selector(setAge:)]);
//打印为 person1添加KVO监听之前 - 0x10935f630 0x10935f630
// 给person1对象添加KVO监听
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
[self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
NSLog(@"person1添加KVO监听之后 - %p %p",
[self.person1 methodForSelector:@selector(setAge:)],
[self.person2 methodForSelector:@selector(setAge:)]);
// 打印为person1添加KVO监听之后 - 0x1096ba3d2 0x10935f630
在这里打断点,在lldb里面打印 p (IMP)0x1096ba3d2 这样就会打印对应的方法 :(IMP) $0 = 0x00000001096ba3d2 (Foundation`_NSSetIntValueAndNotify),从这里我们就看到了是调用_NSSetIntValueAndNotify函数。在这里在附上kvc赋值和取值的原理: