思维导图:
两个主要方法:
// 添加观察者, 监听本函数调用者的aPath的改变
- (void) addObserver: (NSObject*)anObserver
forKeyPath: (NSString*)aPath
options: (NSKeyValueObservingOptions)options
context: (void*)aContext;
// 接收 keyPath 值修改的通知
- (void)observeValueForKeyPath:(nullable NSString *)keyPath
ofObject:(nullable id)object
change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change
context:(nullable void *)context;
关键代码:
- (void) addObserver: (NSObject*)anObserver
forKeyPath: (NSString*)aPath
options: (NSKeyValueObservingOptions)options
context: (void*)aContext
{
GSKVOInfo *info;
GSKVOReplacement *r;
NSKeyValueObservationForwarder *forwarder;
NSRange dot;
// 1.1 一些全局map和baseClass 的初始化
setup();
[kvoLock lock];
// 1.2 创建派生类, 拷贝 GSKVOBase 的 实例方法/类方法 到派生类
r = replacementForClass([self class]);
// 1.3 获取 info
info = (GSKVOInfo*)[self observationInfo];
if (info == nil)
{
// 获取失败, 新建
info = [[GSKVOInfo alloc] initWithInstance: self];
// 插入全局 map: GSKVOInfo
[self setObservationInfo: info];
// 修改self 的 isa 指针, 指向派生类
object_setClass(self, [r replacement]);
}
dot = [aPath rangeOfString:@"."];
if (dot.location != NSNotFound)
{
// 1.4 aPath 是 keyPath, 需要 NSKeyValueObservationForwarder 作为桥接类
forwarder = [[NSKeyValueObservationForwarder alloc]
initWithKeyPath: aPath
ofObject: self
withTarget: anObserver
context: aContext];
[info addObserver: anObserver
forKeyPath: aPath
options: options
context: forwarder];
}
else
{
// 1.5 aPath 是 key, 直接重写 key 的 setter 方法
[r overrideSetterFor: aPath];
//1.6 info 处理
[info addObserver: anObserver
forKeyPath: aPath
options: options
context: aContext];
}
[kvoLock unlock];
}
1.1 setup();
static NSRecursiveLock *kvoLock = nil;
static NSMapTable *classTable = 0; // <原类:派生类>
static NSMapTable *infoTable = 0; // <被观察实例对象:GSKVOInfo>
//属性key值改变时, 观察者也会收到key 关联的 keys 值的变化
static NSMapTable *dependentKeyTable; <被观察实例对象的class:<key:与key关联的keys>>
static Class baseClass; // GSKVOBase
static id null;
static inline void setup() {
if (nil == kvoLock)
{
[gnustep_global_lock lock];
if (nil == kvoLock)
{
kvoLock = [NSRecursiveLock new];
null = [[NSNull null] retain];
classTable = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
NSNonOwnedPointerMapValueCallBacks, 128);
infoTable = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
NSNonOwnedPointerMapValueCallBacks, 1024);
dependentKeyTable = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks,
NSOwnedPointerMapValueCallBacks, 128);
baseClass = NSClassFromString(@"GSKVOBase");
}
[gnustep_global_lock unlock];
}
}
1.2 r = replacementForClass([self class]);
static GSKVOReplacement * replacementForClass(Class c) {
GSKVOReplacement *r;
// 同1.1
setup();
// 加锁
[kvoLock lock];
// 获取原类对应的派生类
r = (GSKVOReplacement*)NSMapGet(classTable, (void*)c);
// 未获取到, 创建并插入全局map classTable
if (r == nil)
{
// 1.2.1
r = [[GSKVOReplacement alloc] initWithClass: c];
NSMapInsert(classTable, (void*)c, (void*)r);
}
// 解锁
[kvoLock unlock];
// 返回派生类
return r;
}
// 1.2.1
- (id) initWithClass: (Class)aClass
{
NSValue *template;
NSString *superName;
NSString *name;
// 调用父类init方法失败, return nil
if (nil == (self = [super init]))
{
return nil;
}
// 不允许原类重写方法 takeValue:forKey:
if ([aClass instanceMethodForSelector: @selector(takeValue:forKey:)]
!= [NSObject instanceMethodForSelector: @selector(takeValue:forKey:)])
{
NSLog(@"WARNING The class '%@' (or one of its superclasses) overrides"
@" the deprecated takeValue:forKey: method. Using KVO to observe"
@" this class may interfere with this method. Please change the"
@" class to override -setValue:forKey: instead.",
NSStringFromClass(aClass));
}
// 不允许原类重写方法 takeValue:forKeyPath:
if ([aClass instanceMethodForSelector: @selector(takeValue:forKeyPath:)]
!= [NSObject instanceMethodForSelector: @selector(takeValue:forKeyPath:)])
{
NSLog(@"WARNING The class '%@' (or one of its superclasses) overrides"
@" the deprecated takeValue:forKeyPath: method. Using KVO to observe"
@" this class may interfere with this method. Please change the"
@" class to override -setValue:forKeyPath: instead.",
NSStringFromClass(aClass));
}
// 赋值, 表示原类
original = aClass;
/*
* Create subclass of the original, and override some methods
* with implementations from our abstract base class.
*/
// 获取原类的类名
superName = NSStringFromClass(original);
// 原类类名前拼接 GSKVO
name = [@"GSKVO" stringByAppendingString: superName];
// 动态创建派生类, 其父类为原类
template = GSObjCMakeClass(name, superName, nil);
// 注册类
GSObjCAddClasses([NSArray arrayWithObject: template]);
// 派生类
replacement = NSClassFromString(name);
// 1.2.1.1把 GSKVOBase 的 实例方法/类方法 添加到派生类
GSObjCAddClassBehavior(replacement, baseClass);
/* Create the set of setter methods overridden.
*/
keys = [NSMutableSet new];
return self;
}
1.2.1.1 把原类的 实例方法/类方法 添加到派生类
void GSObjCAddClassBehavior(Class receiver, Class behavior) {
unsigned int count;
Method *methods;
Class behavior_super_class = class_getSuperclass(behavior);
if (YES == class_isMetaClass(receiver))
{
fprintf(stderr, "Trying to add behavior (%s) to meta class (%s)\n",
class_getName(behavior), class_getName(receiver));
abort();
}
if (YES == class_isMetaClass(behavior))
{
fprintf(stderr, "Trying to add meta class as behavior (%s) to (%s)\n",
class_getName(behavior), class_getName(receiver));
abort();
}
if (class_getInstanceSize(receiver) < class_getInstanceSize(behavior))
{
const char *b = class_getName(behavior);
const char *r = class_getName(receiver);
#ifdef NeXT_Foundation_LIBRARY
fprintf(stderr, "Trying to add behavior (%s) with instance "
"size larger than class (%s)\n", b, r);
abort();
#else
/* As a special case we allow adding GSString/GSCString to the
* constant string class ... since we know the base library
* takes care not to access non-existent instance variables.
*/
if ((strcmp(b, "GSCString") && strcmp(b, "GSString"))
|| (strcmp(r, "NSConstantString") && strcmp(r, "NXConstantString")))
{
fprintf(stderr, "Trying to add behavior (%s) with instance "
"size larger than class (%s)\n", b, r);
abort();
}
#endif
}
BDBGPrintf("Adding behavior to class %s\n", class_getName(receiver));
/* Add instance methods */
methods = class_copyMethodList(behavior, &count);
BDBGPrintf(" instance methods from %s %u\n", class_getName(behavior), count);
if (methods == NULL)
{
BDBGPrintf(" none.\n");
}
else
{
GSObjCAddMethods (receiver, methods, NO);
free(methods);
}
/* Add class methods */
methods = class_copyMethodList(object_getClass(behavior), &count);
BDBGPrintf(" class methods from %s %u\n", class_getName(behavior), count);
if (methods == NULL)
{
BDBGPrintf(" none.\n");
}
else
{
GSObjCAddMethods (object_getClass(receiver), methods, NO);
free(methods);
}
/* Add behavior's superclass, if not already there. */
if (!GSObjCIsKindOf(receiver, behavior_super_class))
{
GSObjCAddClassBehavior (receiver, behavior_super_class);
}
GSFlushMethodCacheForClass (receiver);
}
// GSKVOBase重写的方法
@implementation GSKVOBase
- (void) dealloc
{
// Turn off KVO for self ... then call the real dealloc implementation.
[self setObservationInfo: nil];
object_setClass(self, [self class]);
[self dealloc];
GSNOSUPERDEALLOC;
}
// 重写 -class 方法, 不然会返回派生类
- (Class) class
{
return class_getSuperclass(object_getClass(self));
}
/*
下面几个方法做了相同的操作: hook KVC 方法,
在原方法调用前后分别调用 -willChangeValueForKey: 和 -didChangeValueForKey:
*/
- (void) setValue: (id)anObject forKey: (NSString*)aKey
{
// 这里拿到的是原类
Class c = [self class];
void (*imp)(id,SEL,id,id);
// 原类的 -setValue:forKey: 方法
imp = (void (*)(id,SEL,id,id))[c instanceMethodForSelector: _cmd];
// 是否允许通知
if ([[self class] automaticallyNotifiesObserversForKey: aKey])
{
[self willChangeValueForKey: aKey];
imp(self,_cmd,anObject,aKey);
[self didChangeValueForKey: aKey];
}
else
{
imp(self,_cmd,anObject,aKey);
}
}
- (void) takeStoredValue: (id)anObject forKey: (NSString*)aKey
{
Class c = [self class];
void (*imp)(id,SEL,id,id);
imp = (void (*)(id,SEL,id,id))[c instanceMethodForSelector: _cmd];
if ([[self class] automaticallyNotifiesObserversForKey: aKey])
{
[self willChangeValueForKey: aKey];
imp(self,_cmd,anObject,aKey);
[self didChangeValueForKey: aKey];
}
else
{
imp(self,_cmd,anObject,aKey);
}
}
- (void) takeValue: (id)anObject forKey: (NSString*)aKey
{
Class c = [self class];
void (*imp)(id,SEL,id,id);
imp = (void (*)(id,SEL,id,id))[c instanceMethodForSelector: _cmd];
if ([[self class] automaticallyNotifiesObserversForKey: aKey])
{
[self willChangeValueForKey: aKey];
imp(self,_cmd,anObject,aKey);
[self didChangeValueForKey: aKey];
}
else
{
imp(self,_cmd,anObject,aKey);
}
}
- (void) takeValue: (id)anObject forKeyPath: (NSString*)aKey
{
Class c = [self class];
void (*imp)(id,SEL,id,id);
imp = (void (*)(id,SEL,id,id))[c instanceMethodForSelector: _cmd];
if ([[self class] automaticallyNotifiesObserversForKey: aKey])
{
[self willChangeValueForKey: aKey];
imp(self,_cmd,anObject,aKey);
[self didChangeValueForKey: aKey];
}
else
{
imp(self,_cmd,anObject,aKey);
}
}
- (Class) superclass
{
return class_getSuperclass(class_getSuperclass(object_getClass(self)));
}
@end
1.3 每个被观察者都有一个GSKVOInfo
与之对应, 保存在全局 map: infoTable
static NSMapTable *infoTable = 0;
在 setup();
初始化
- (void*) observationInfo
{
void *info;
setup();
[kvoLock lock];
info = NSMapGet(infoTable, (void*)self);
IF_NO_GC(AUTORELEASE(RETAIN((id)info));)
[kvoLock unlock];
return info;
}
- (void) setObservationInfo: (void*)observationInfo
{
setup();
[kvoLock lock];
if (observationInfo == 0)
{
NSMapRemove(infoTable, (void*)self);
}
else
{
NSMapInsert(infoTable, (void*)self, observationInfo);
}
[kvoLock unlock];
}
1.4 NSKeyValueObservationForwarder
假设有一个类 Person
, 它有一个实例对象, 我们监听对象的name 和 mother.name. 后者需要NSKeyValueObservationForwarder来转发
其中, person 是被观察者, someObserver 是观察者
当 mother.name 改变时, NSKeyValueObservationForwarder 的
-observeValueForKeyPath: 方法会转发给 someObserver
class Person: NSObject {
var name: String?
var height: CGFloat?
var weight: CGFloat?
var friend: Person?
}
var person = Person()
person.addObserver(someObserver, forKeyPath: "friend.name", options: .new, context: nil)
@interface NSKeyValueObservationForwarder : NSObject
{
id target;
NSKeyValueObservationForwarder *child;
void *contextToForward;
id observedObjectForUpdate;
NSString *keyForUpdate;
id observedObjectForForwarding;
NSString *keyForForwarding;
NSString *keyPathToForward;
}
- (id) initWithKeyPath: (NSString *)keyPath
ofObject: (id)object
withTarget: (id)aTarget
context: (void *)context;
- (void) keyPathChanged: (id)objectToObserve;
@end
@implementation NSKeyValueObservationForwarder
- (id) initWithKeyPath: (NSString *)keyPath
ofObject: (id)object
withTarget: (id)aTarget
context: (void *)context
{
NSString *remainingKeyPath;
NSRange dot;
if (nil == (self = [super init]))
{
return nil;
}
// someObserver
target = aTarget;
// friend.name
keyPathToForward = [keyPath copy];
contextToForward = context;
dot = [keyPath rangeOfString: @"."];
if (dot.location == NSNotFound)
{
[NSException raise: NSInvalidArgumentException
format: @"NSKeyValueObservationForwarder was not given a key path"];
}
// mother
keyForUpdate = [[keyPath substringToIndex: dot.location] copy];
// name
remainingKeyPath = [keyPath substringFromIndex: dot.location + 1];
// person
observedObjectForUpdate = object;
// person 的 mother 属性被 self: NSKeyValueObservationForwarder 观察
[object addObserver: self
forKeyPath: keyForUpdate
options: NSKeyValueObservingOptionNew
| NSKeyValueObservingOptionOld
context: target];
dot = [remainingKeyPath rangeOfString: @"."];
if (dot.location != NSNotFound)
{
child = [[NSKeyValueObservationForwarder alloc]
initWithKeyPath: remainingKeyPath
ofObject: [object valueForKey: keyForUpdate]
withTarget: self
context: NULL];
observedObjectForForwarding = nil;
}
else
{
// name
keyForForwarding = [remainingKeyPath copy];
// friend
observedObjectForForwarding = [object valueForKey: keyForUpdate];
// friend 的 name 属性被 self: NSKeyValueObservationForwarder 观察
[observedObjectForForwarding addObserver: self
forKeyPath: keyForForwarding
options: NSKeyValueObservingOptionNew
| NSKeyValueObservingOptionOld
context: target];
child = nil;
}
return self;
}
- (void) finalize
{
if (child)
{
[child finalize];
}
if (observedObjectForUpdate)
{
[observedObjectForUpdate removeObserver: self forKeyPath: keyForUpdate];
}
if (observedObjectForForwarding)
{
[observedObjectForForwarding removeObserver: self forKeyPath:
keyForForwarding];
}
DESTROY(self);
}
- (void) dealloc
{
[keyForUpdate release];
[keyForForwarding release];
[keyPathToForward release];
[super dealloc];
}
- (void) observeValueForKeyPath: (NSString *)keyPath
ofObject: (id)anObject
change: (NSDictionary *)change
context: (void *)context
{
if (anObject == observedObjectForUpdate)
{
[self keyPathChanged: nil];
}
else
{
[target observeValueForKeyPath: keyPathToForward
ofObject: observedObjectForUpdate
change: change
context: contextToForward];
}
}
- (void) keyPathChanged: (id)objectToObserve
{
if (objectToObserve != nil)
{
[observedObjectForUpdate removeObserver: self forKeyPath: keyForUpdate];
observedObjectForUpdate = objectToObserve;
[objectToObserve addObserver: self
forKeyPath: keyForUpdate
options: NSKeyValueObservingOptionNew
| NSKeyValueObservingOptionOld
context: target];
}
if (child != nil)
{
[child keyPathChanged:
[observedObjectForUpdate valueForKey: keyForUpdate]];
}
else
{
NSMutableDictionary *change;
change = [NSMutableDictionary dictionaryWithObject:
[NSNumber numberWithInt: 1]
forKey: NSKeyValueChangeKindKey];
if (observedObjectForForwarding != nil)
{
id oldValue;
oldValue
= [observedObjectForForwarding valueForKey: keyForForwarding];
[observedObjectForForwarding removeObserver: self forKeyPath:
keyForForwarding];
if (oldValue)
{
[change setObject: oldValue
forKey: NSKeyValueChangeOldKey];
}
}
observedObjectForForwarding = [observedObjectForUpdate
valueForKey:keyForUpdate];
if (observedObjectForForwarding != nil)
{
id newValue;
[observedObjectForForwarding addObserver: self
forKeyPath: keyForForwarding
options: NSKeyValueObservingOptionNew
| NSKeyValueObservingOptionOld
context: target];
//prepare change notification
newValue
= [observedObjectForForwarding valueForKey: keyForForwarding];
if (newValue)
{
[change setObject: newValue forKey: NSKeyValueChangeNewKey];
}
}
[target observeValueForKeyPath: keyPathToForward
ofObject: observedObjectForUpdate
change: change
context: contextToForward];
}
}
@end
1.5 [r overrideSetterFor: aPath];
aPath 不包含".", 说明是被观察者的属性.
根据参数类型, 从GSKVOSetter
获取对应的 setter imp, 添加到派生类.
注意: 添加方法失败会通过dependentKeyTable
曲线救国!!!
- (void) overrideSetterFor: (NSString*)aKey
{
if ([keys member: aKey] == nil)
{
NSMethodSignature *sig;
SEL sel;
IMP imp;
const char *type;
NSString *suffix;
NSString *a[2];
unsigned i;
BOOL found = NO;
NSString *tmp;
unichar u;
// 假设 aKey = @"name";
suffix = [aKey substringFromIndex: 1];
u = uni_toupper([aKey characterAtIndex: 0]);
tmp = [[NSString alloc] initWithCharacters: &u length: 1];
// setName
a[0] = [NSString stringWithFormat: @"set%@%@:", tmp, suffix];
// _setName
a[1] = [NSString stringWithFormat: @"_set%@%@:", tmp, suffix];
[tmp release];
for (i = 0; i < 2; i++) {
/*
* Replace original setter with our own version which does KVO
* notifications.
*/
sel = NSSelectorFromString(a[i]);
if (sel == 0)
{
continue;
}
sig = [original instanceMethodSignatureForSelector: sel];
if (sig == 0)
{
continue;
}
/*
* A setter must take three arguments (self, _cmd, value).
* The return value (if any) is ignored.
*/
if ([sig numberOfArguments] != 3)
{
continue; // Not a valid setter method.
}
/*
根据不同的类型, 从 GSKVOSetter 获取不同的 setter imp,
这些imp 做了相同的操作, hook 原 setter 方法,
在其调用前后分别调用 `-willChangeValueForKey:` 和 `-didChangeValueForKey:`, 通知观察者监听 key 值的变化
* Since the compiler passes different argument types
* differently, we must use a different setter method
* for each argument type.
* FIXME ... support structures
* Unsupported types are quietly ignored ... is that right?
*/
type = [sig getArgumentTypeAtIndex: 2];
switch (*type)
{
case _C_CHR:
case _C_UCHR:
imp = [[GSKVOSetter class]
instanceMethodForSelector: @selector(setterChar:)];
break;
case _C_SHT:
case _C_USHT:
imp = [[GSKVOSetter class]
instanceMethodForSelector: @selector(setterShort:)];
break;
case _C_INT:
case _C_UINT:
imp = [[GSKVOSetter class]
instanceMethodForSelector: @selector(setterInt:)];
break;
case _C_LNG:
case _C_ULNG:
imp = [[GSKVOSetter class]
instanceMethodForSelector: @selector(setterLong:)];
break;
#ifdef _C_LNG_LNG
case _C_LNG_LNG:
case _C_ULNG_LNG:
imp = [[GSKVOSetter class]
instanceMethodForSelector: @selector(setterLongLong:)];
break;
#endif
case _C_FLT:
imp = [[GSKVOSetter class]
instanceMethodForSelector: @selector(setterFloat:)];
break;
case _C_DBL:
imp = [[GSKVOSetter class]
instanceMethodForSelector: @selector(setterDouble:)];
break;
#if __GNUC__ > 2 && defined(_C_BOOL)
case _C_BOOL:
imp = [[GSKVOSetter class]
instanceMethodForSelector: @selector(setterChar:)];
break;
#endif
case _C_ID:
case _C_CLASS:
case _C_PTR:
imp = [[GSKVOSetter class]
instanceMethodForSelector: @selector(setter:)];
break;
case _C_STRUCT_B:
if (GSSelectorTypesMatch(@encode(NSRange), type))
{
imp = [[GSKVOSetter class]
instanceMethodForSelector: @selector(setterRange:)];
}
else if (GSSelectorTypesMatch(@encode(NSPoint), type))
{
imp = [[GSKVOSetter class]
instanceMethodForSelector: @selector(setterPoint:)];
}
else if (GSSelectorTypesMatch(@encode(NSSize), type))
{
imp = [[GSKVOSetter class]
instanceMethodForSelector: @selector(setterSize:)];
}
else if (GSSelectorTypesMatch(@encode(NSRect), type))
{
imp = [[GSKVOSetter class]
instanceMethodForSelector: @selector(setterRect:)];
}
else
{
#if defined(USE_LIBFFI)
GSCodeBuffer *b;
b = cifframe_closure(sig, cifframe_callback);
[b retain];
imp = [b executable];
#else
imp = 0;
#endif
}
break;
default:
imp = 0;
break;
}
if (imp != 0) {
// 把获取到的 imp 和 sel 添加到派生类
if (class_addMethod(replacement, sel, imp, [sig methodType])) {
found = YES;
} else {
NSLog(@"Failed to add setter method for %s to %s", sel_getName(sel), class_getName(original));
}
}
}
// 添加 setter 方法成功
if (found == YES) {
[keys addObject: aKey];
}
// 添加失败, 尝试通过 `dependentKeyTable` 曲线救国
else
{
/**
假设:
1. `aKey = @"name";`
2. Person 类对应的: `depKeys = {"height": {"name""}};`
表示: 希望当 height 的值改变时, 观察者会收到 name 改变的通知
hook "height" 的 setter 方法, 在原`-setHeight:` 前后分别调用`-willChangeValueForKey:` 和 `-didChangeValueForKey:`
这俩方法会查找"height" 关联的 "name", 并分别调用`[self willChangeValueForKey: @"name"];` 和 `[self didChangeValueForKey: @"name"];` 这样观察者就会收到"name" 改变的通知
*/
NSMapTable *depKeys = NSMapGet(dependentKeyTable, original);
if (depKeys)
{
NSMapEnumerator enumerator = NSEnumerateMapTable(depKeys);
NSString *mainKey;
NSHashTable *dependents;
while (NSNextMapEnumeratorPair(&enumerator, (void **)(&mainKey),
(void**)&dependents))
{
NSHashEnumerator dependentKeyEnum;
NSString *dependentKey;
if (!dependents) continue;
dependentKeyEnum = NSEnumerateHashTable(dependents);
while ((dependentKey
= NSNextHashEnumeratorItem(&dependentKeyEnum)))
{
if ([dependentKey isEqual: aKey])
{
[self overrideSetterFor: mainKey];
// Mark the key as used
[keys addObject: aKey];
found = YES;
}
}
NSEndHashTableEnumeration(&dependentKeyEnum);
}
NSEndMapTableEnumeration(&enumerator);
}
if (!found)
{
NSDebugLLog(@"KVC", @"class %@ not KVC compliant for %@",
original, aKey);
/*
[NSException raise: NSInvalidArgumentException
format: @"class not KVC complient for %@", aKey];
*/
}
}
}
}
// hook -setter: 方法, 具体可参考 -setValue:forKey
- (void) setter: (void*)val
{
NSString *key;
Class c = [self class];
void (*imp)(id,SEL,void*);
imp = (void (*)(id,SEL,void*))[c instanceMethodForSelector: _cmd];
key = newKey(_cmd);
if ([c automaticallyNotifiesObserversForKey: key] == YES)
{
// pre setting code here
[self willChangeValueForKey: key];
(*imp)(self, _cmd, val);
// post setting code here
[self didChangeValueForKey: key];
}
else
{
(*imp)(self, _cmd, val);
}
RELEASE(key);
}
1.6 [info addObserver: anObserver forKeyPath: aPath options: optionscontext: aContext];
先了解下数据结构
@interface GSKVOInfo : NSObject {
NSObject *instance; // Not retained.
NSRecursiveLock *iLock;
// <key:GSKVOPathInfo>
NSMapTable *paths;
}
@end
@interface GSKVOPathInfo : NSObject
{
@public
unsigned recursion;
unsigned allOptions;
// [GSKVOObservation]
NSMutableArray *observations;
NSMutableDictionary *change;
}
- (void) notifyForKey: (NSString *)aKey ofInstance: (id)instance prior: (BOOL)f;
@end
enum {
NSKeyValueObservingOptionNew = 1,
NSKeyValueObservingOptionOld = 2
#if OS_API_VERSION(MAC_OS_X_VERSION_10_5,GS_API_LATEST)
, NSKeyValueObservingOptionInitial = 4,
NSKeyValueObservingOptionPrior = 8
#endif
};
关键方法:
- (void) addObserver: (NSObject*)anObserver
forKeyPath: (NSString*)aPath
options: (NSKeyValueObservingOptions)options
context: (void*)aContext
{
GSKVOPathInfo *pathInfo;
GSKVOObservation *observation;
unsigned count;
if ([anObserver respondsToSelector:
@selector(observeValueForKeyPath:ofObject:change:context:)] == NO)
{
return;
}
[iLock lock];
// 以aPath为key 从 paths 获取GSKVOPathInfo
pathInfo = (GSKVOPathInfo*)NSMapGet(paths, (void*)aPath);
if (pathInfo == nil)
{
pathInfo = [GSKVOPathInfo new];
// use immutable object for map key
aPath = [aPath copy];
NSMapInsert(paths, (void*)aPath, (void*)pathInfo);
[pathInfo release];
[aPath release];
}
observation = nil;
pathInfo->allOptions = 0;
count = [pathInfo->observations count];
while (count-- > 0)
{
GSKVOObservation *o;
o = [pathInfo->observations objectAtIndex: count];
// 获取observer 并更新数据
if (o->observer == anObserver)
{
o->context = aContext;
o->options = options;
observation = o;
}
pathInfo->allOptions |= o->options;
}
if (observation == nil)
{
observation = [GSKVOObservation new];
GSAssignZeroingWeakPointer((void**)&observation->observer,
(void*)anObserver);
observation->context = aContext;
observation->options = options;
[pathInfo->observations addObject: observation];
[observation release];
pathInfo->allOptions |= options;
}
if (options & NSKeyValueObservingOptionInitial)
{
// 注释说的很清楚了
/* If the NSKeyValueObservingOptionInitial option is set,
* we must send an immediate notification containing the
* existing value in the NSKeyValueChangeNewKey
*/
[pathInfo->change setObject: [NSNumber numberWithInt: 1]
forKey: NSKeyValueChangeKindKey];
if (options & NSKeyValueObservingOptionNew)
{
id value;
value = [instance valueForKeyPath: aPath];
if (value == nil)
{
value = null;
}
[pathInfo->change setObject: value
forKey: NSKeyValueChangeNewKey];
}
[anObserver observeValueForKeyPath: aPath
ofObject: instance
change: pathInfo->change
context: aContext];
}
[iLock unlock];
}
二. -willChangeValueForKey:
和 -didChangeValueForKey:
通知观察者key值改变了
// 保存旧值, 调用 -notifyForKey:ofInstance:prior:, 获取关联keys并递归调用
- (void) willChangeValueForKey: (NSString*)aKey
{
GSKVOPathInfo *pathInfo;
GSKVOInfo *info;
// 获取 self 对应的 GSKVOInfo
info = (GSKVOInfo *)[self observationInfo];
if (info == nil)
{
return;
}
// 获取 aKey 对应的 pathInfo
pathInfo = [info lockReturningPathInfoForKey: aKey];
if (pathInfo != nil)
{
if (pathInfo->recursion++ == 0)
{
// 获取上次修改后保存的值
id old = [pathInfo->change objectForKey: NSKeyValueChangeNewKey];
if (old != nil)
{
// 设置为旧值并移除 NSKeyValueChangeNewKey 的值
/* We have set a value for this key already, so the value
* we set must now be the old value and we don't need to
* refetch it.
*/
[pathInfo->change setObject: old
forKey: NSKeyValueChangeOldKey];
[pathInfo->change removeObjectForKey: NSKeyValueChangeNewKey];
}
else if (pathInfo->allOptions & NSKeyValueObservingOptionOld)
{
// 没有包存过, 通过 KVC 获取, 并保存为旧值
/* We don't have an old value set, so we must fetch the
* existing value because at least one observation wants it.
*/
old = [self valueForKey: aKey];
if (old == nil)
{
old = null;
}
[pathInfo->change setObject: old
forKey: NSKeyValueChangeOldKey];
}
[pathInfo->change setValue:
[NSNumber numberWithInt: NSKeyValueChangeSetting]
forKey: NSKeyValueChangeKindKey];
/*
调用通知函数. 对否给 observer 回调,
取决于 aKey 对应的 options 是否包含 NSKeyValueObservingOptionPrior)
*/
[pathInfo notifyForKey: aKey ofInstance: [info instance] prior: YES];
}
[info unlock];
}
// 查找与aKey关联的keys, 并调用他们的 -willChangeValueForKey:
[self willChangeValueForDependentsOfKey: aKey];
}
// 保存新值, 调用 -notifyForKey:ofInstance:prior:, 获取关联keys并递归调用
- (void) didChangeValueForKey: (NSString*)aKey {
GSKVOPathInfo *pathInfo;
GSKVOInfo *info;
info = (GSKVOInfo *)[self observationInfo];
if (info == nil) {
return;
}
pathInfo = [info lockReturningPathInfoForKey: aKey];
if (pathInfo != nil) {
if (pathInfo->recursion == 1) {
id value = [self valueForKey: aKey];
if (value == nil) {
value = null;
}
[pathInfo->change setValue: value forKey: NSKeyValueChangeNewKey];
[pathInfo->change setValue: [NSNumber numberWithInt: NSKeyValueChangeSetting] forKey: NSKeyValueChangeKindKey];
[pathInfo notifyForKey: aKey ofInstance: [info instance] prior: NO];
}
if (pathInfo->recursion > 0) {
pathInfo->recursion--;
}
[info unlock];
}
// 查找与aKey关联的keys, 并调用他们的 -didChangeValueForKey:
[self didChangeValueForDependentsOfKey: aKey];
}
- (void) notifyForKey: (NSString *)aKey ofInstance: (id)instance prior: (BOOL)f
{
unsigned count;
id oldValue;
id newValue;
// 从 -willChangeValueForKey: 进来的
if (f == YES) {
/*
如果 allOptions 不包含 NSKeyValueObservingOptionPrior的话, 直接return. 观察者仅在key值修改后收到通知;
否者继续, 这样的话观察者会在key值修改前后各收到一次通知.
*/
if ((allOptions & NSKeyValueObservingOptionPrior) == 0) {
return; // Nothing to do.
}
[change setObject: [NSNumber numberWithBool: YES] forKey: NSKeyValueChangeNotificationIsPriorKey];
} else {
[change removeObjectForKey: NSKeyValueChangeNotificationIsPriorKey];
}
oldValue = [[change objectForKey: NSKeyValueChangeOldKey] retain];
if (oldValue == nil) {
oldValue = null;
}
newValue = [[change objectForKey: NSKeyValueChangeNewKey] retain];
if (newValue == nil) {
newValue = null;
}
/* Retain self so that we won't be deallocated during the
* notification process.
*/
[self retain];
count = [observations count];
// 遍历观察者, 通知他们 key 值改变了
while (count-- > 0)
{
GSKVOObservation *o = [observations objectAtIndex: count];
if (f == YES) {
if ((o->options & NSKeyValueObservingOptionPrior) == 0) {
continue;
}
}
else {
if (o->options & NSKeyValueObservingOptionNew) {
[change setObject: newValue forKey: NSKeyValueChangeNewKey];
}
}
if (o->options & NSKeyValueObservingOptionOld) {
[change setObject: oldValue forKey: NSKeyValueChangeOldKey];
}
[o->observer observeValueForKeyPath: aKey
ofObject: instance
change: change
context: o->context];
}
[change setObject: oldValue forKey: NSKeyValueChangeOldKey];
[oldValue release];
[change setObject: newValue forKey: NSKeyValueChangeNewKey];
[newValue release];
[self release];
}
其他
- 设置关联key: 当 name 的值被修改时 如果height被观察了, 则 height 会收到修改通知
/*
重写该方法.
GNUstep里没有调用的地方, 但是看api注解得知,
内部会监听 "name", 当他的值改变时, 会通知"height"的观察者"height"被改变了
*/
+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
if ([key isEqual: @"height"]) {
return [[NSSet alloc] initWithObjects:@"name", nil];
}
return [super keyPathsForValuesAffectingValueForKey:key];
}
// 或者重写这个方法 +keyPathsForValuesAffecting<Key>
+ (NSSet<NSString *> *)keyPathsForValuesAffectingHeight {
return [[NSSet alloc] initWithObjects:@"name", nil];
}
- 使观察者不收到 height 修改的通知
// 重写该方法
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
if ([key isEqual:@"height"]) {
return false;
}
return [super automaticallyNotifiesObserversForKey:key];
}