(1)KVO是基于runtime机制实现的,
(2)类A监听类B的一个属性的变化,当B被观察时,系统会在运行期动态创建一个B的子类NSKVONotifying_B,
(3)每个类都有一个isa指针指向当前类,B的isa指针会指向派生类NSKVONotifying_B,然后重写被观察的属性的setter方法,
(4)键值观察依赖于NSobject的两个方法willChangeValueForKey和didChangeValueForKey,在属性被改变之前调用willChangeValueForKey记录旧值,发生改变之后调用didChangeValueForKey用来储存新值,然后调用observeValueForKey:ofObject:change:context
下图
自定义KVO
- (void)Mae_addObserve:(NSObject *)observe forKeyPabth:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{
动态添加一个类,模拟子类NSKVONotifying_B的产生
NSString *oldClassName = NSStringFromClass([self class]);
NSString *newClassName = [@"Mae" stringByAppentdingString:oldClassName];
const char *newName = [newClassName UTF8String];
Class myClass = objc_allocateClassPair([self class],newName,0);
添加一个setter方法,比如监听的是name属性,添加name的setter方法
class_addMethod(myClass,@selector(setName:),(IMP)(setName),nil);
注册新添加的这个类
objc_registerClassPair(myClass);
将指向被观察者的isa指针,指向新类
object_setClass(self,myclass);
将观察者的属性,保存到新类中去
objc_setAssociatedObject(self, (__bridge const void *)@"objc", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
//相当于重写父类的方法
void setAge(id self, SEL _cmd, int age) {
//保存当前类
Class myclass = [self class];
//将self的isa指针指向父类
object_setClass(self, class_getSuperclass([self class]));
//调用父类
objc_msgSend(self, @selector(setName:),name);
//拿出观察者
objc_getAssociatedObject(self, (__bridge const void *)@"objc");
//通知观察者
objc_msgSend(objc,@selector(observeValueForKeyPath:ofObject:change:context:),self,name,nil,nil);
//改为子类
object_setClass(self, myclass);
}
这样就可以回调到ViewContoller中的监听方法observeValueForKeyPath:ofObject:change:context:中去,而且相关的值也传递过去了。
如何手动触发KVO
KVO的缺陷
你只能通过重写 -observeValueForKeyPath:ofObject:change:context: 方法来获得通知。
想要提供自定义的 selector ,不行;
想要传一个 block ,门都没有。