理解
1.添加响应者,监听对象属性变化,当对象属性改变时调用代理。
2.动态创建NSKVONotifying_XX类,修改被监听对象isa指针指向,只要调用对象的set方法,就会调用NSKVONotifying_XX的set方法。本质:判断对象的set方法有没有被调用
监听一个Person类底层实现
1)、动态创建NSKVONotifying_Person,NSKVONotifying_Person是Person子类,做KVO;
2)、修改当前对象的isa指针->NSKVONotifying_Person;
3)、只要调用对象的set,就会调用NSKVONotifying_Person的set方法;
4)、重写NSKVONotifying_Person的set方法,1[super set:] 2、通知观察者,告诉属性改变。
每个对象都有 isa 指针,指向该对象的类,它告诉 Runtime 系统这个对象的类是什么。所以对象注册为被观察者时,isa 指针指向新子类,那么这个被观察的对象就神奇地变成新子类的对象了。在新类的set方法中会通知观察者。
为了测试kvo的实现方式,我们可以创建一个Person类,然后使用kvo监测name属性,然后在Person类中重写description方法。然后分别在添加kvo监控前后打印对象。
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
-(NSString *)description{
IMP nameIPM = [self methodForSelector:@selector(setName:)];
NSLog(@"object address:%p\n object setName: %p",self,nameIPM);
Class objectMethodClass = [self class];
Class objectRuntimeClass = object_getClass(self);
Class superClass = class_getSuperclass(objectRuntimeClass);
NSLog(@"objectMethodClass : %@, ObjectRuntimeClass : %@, superClass : %@ \n", objectMethodClass, objectRuntimeClass, superClass);
return @"";
}
kvo中被观察者不会对观察者进行强引用。
nsnotification中从 iOS 9 开始通知中心会对观察者进行弱引用,所以不需要在观察者对象释放之前从通知中心移除。但是,通过-[NSNotificationCenter addObserverForName:object:queue:usingBlock]方法注册的观察者依然需要手动的释放,因为通知中心对它们持有的是强引用。
kvo缺陷
1.不支持block
,代码分散。
2.在调用KVO时需要传入一个keyPath
,由于keyPath
是字符串的形式,所以其对应的属性发生改变后,字符串不易修改(因为不修改也不会有提示)。
推荐Facebook
的一个KVO
开源第三方框架-KVOController。KVOController
本质上是对系统KVO
的封装,具有原生KVO
所有的功能,而且规避了原生KVO
的很多问题,兼容block
和action
两种回调方式。