iOS GNUstep源码分析之 KVO

本文详细介绍了Swift中使用KVO(Key-Value Observing)实现观察者模式的技巧,涉及添加观察者、接收值变化通知、派生类替换及关键方法如willChangeValueForKey和didChangeValueForKey的使用。核心内容包括GSKVOInfo、NSKeyValueObservationForwarder和setter方法的重写。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

思维导图:

在这里插入图片描述

两个主要方法:

// 添加观察者, 监听本函数调用者的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];
}

其他

  1. 设置关联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];
}
  1. 使观察者不收到 height 修改的通知
// 重写该方法
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
    if ([key isEqual:@"height"]) {
        return false;
    }
    return [super automaticallyNotifiesObserversForKey:key];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值