键值观察通知依赖于NSObject两个方法:willChangeValueForKey:和didChangeValueForKey:在一个被观察属性发生改变之前,willChangeValueForKey:一定会被调用,这就会记录旧的值。而改变发生后:observerValueForKey:ofObject:change:context:会被调用,继而didChangeValuueForKey:也会被调用。
在使用KVC命名约定时,当观察一个对象时,一个新的类会被动态创建。这个类继承自该对象的原本的类,并重写了被观察属性的setter方法。重写的setter方法会负责在调用setter方法之前和之后,通知所有观察对象值的更改。最后通过isa混写把这个对象的isa指针指向这个新创建的子类。
- (void)Jay_addObserver:(NSObject *)observer forKey:(NSString *)key withBlock:(JayObservingBlock)block{
//获取set方法的选择器
SEL setterSelector = NSSelectorFromString(setterForGetter(key));
Method setterMetod = class_getInstanceMethod([self class], setterSelector);
if (!setterMetod) {
NSString *reason = [NSString stringWithFormat:@"Object %@ does not have a setter for key %@",self,key];
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:reason userInfo:nil];
return;
}
Class clazz = object_getClass(self);
NSString *clazzName = NSStringFromClass(clazz);
//创建该对象所属类的观察者子类,并将isa指针指向观察者类
if (![clazzName hasPrefix:kJayKVOClassPrefix]) {
clazz = [self makeKVOClassWithOriginalClassName:clazzName];
object_setClass(self, clazz);
}
//往所创建的观察者子类里添加setter方法实现
if (![self hasSelector:setterSelector]) {
const char *types = method_getTypeEncoding(setterMetod);
class_addMethod(clazz, setterSelector, (IMP)kvo_setter, types);
}
//同时保存观察者的信息,观察者注册完成
JayObservationInfo *infoObj = [[JayObservationInfo alloc]initWithObserver:observer key:key block:block];
NSMutableArray *observersArr = objc_getAssociatedObject(self, (__bridge const void *)(kJayKVOAssociatedObservers));
if (!observersArr) {
observersArr = [NSMutableArray array];
objc_setAssociatedObject(self, (__bridge const void *)(kJayKVOAssociatedObservers), observersArr, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
[observersArr addObject:infoObj];
}