- (void)viewDidLoad {
[super viewDidLoad];
self.person1 = [[MJPerson alloc] init];
self.person1.age = 1;
self.person2 = [[MJPerson alloc] init];
self.person2.age = 2;
// 给person1对象添加KVO监听
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
[self.person1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.person1.age = 10;
self.person2.age = 20;
}
- (void)dealloc {
[self.person1 removeObserver:self forKeyPath:@"age"];
}
// 当监听对象的属性值发生改变时,就会调用
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"监听到%@的%@属性值改变了 - %@ - %@", object, keyPath, change, context);
}
我们通过上述代码和打印结果可以看到,点击touch之后代码中对person1进行了监听,但是并没有对person2进行监听。但是通过代码可以看到,person1和person2都进行了set操作。我们知道,KVO执行监听就是通过修改某个对象的值从而实现监听效果。那为什么person1和person2之间有区别呢?
KVO的底层原理
我们可以打印person1和person2的isa指针
由上图可以看到,person1的isa指针指向了一个叫NSKVONotifiying_Person类,而person2的isa指针指向的还是Person类。
那么这两者底层的区别在于:
1、person1的isa指针不再指向Person类,而是指向了一个叫NSKVONotifiying_Person的类,这个类里面有一个set方法,通过set方法就会去调用foundation框架里的setValueAndNotify方法实现监听动作。
//方法里面大致步骤如下
{
willChangeValueForkey:
super setValue
didChangeValueForKey:
}
-(void )didChangeValueForKey:(NSString *)key {
//通知监听器
[obersevr observerValueForKeyPath:];
}
2、person2的isa指针会向Person类中去寻找set方法,找到之后就调用。
总结:KVO之所以能实现监听,是因为它的底层是runtime机制,利用runtime的API生成一个子类NSKVONotifiying_XXX,并让实例对象的isa指向这个全新的子类,当修改属性的值时,会调用foundation框架里面的setValueAndNotify方法,在内部触发监听器的监听方法。