鍵值觀察通知依賴於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];
}