美文网首页Mac·iOS开发
KVO为什么不会循环引用

KVO为什么不会循环引用

作者: iOS_Coder | 来源:发表于2021-03-27 10:30 被阅读0次

    由于Foundation况下中的NSKeyValueObserving并未开源,而且通过C++代码和汇编代码也看不到KVO内部具体的逻辑,所以我们无法研究KVO的target到底会不会造成循环引用,所以有了我们下面的新探索→GNUStep base

    • 函数调用anObserver最终都会通过GKSKVOInfo 的addObserver 方法加入到 *info中
    • info 通过 setObservationInfo 被添加到了NSMapTable
    • 注意 GSKVOInfo中的instanceNot retained.
    @implementation NSObject (NSKeyValueObserverRegistration)
    
    - (void) addObserver: (NSObject*)anObserver
          forKeyPath: (NSString*)aPath
             options: (NSKeyValueObservingOptions)options
             context: (void*)aContext
    {
      GSKVOInfo             *info;
      GSKVOReplacement      *r;
      NSKeyValueObservationForwarder *forwarder;
      NSRange               dot;
    
      setup();
    
      [kvoLock lock];
    
      // Use the original class
      r = replacementForClass([self class]);
    
      /*
       * Get the existing observation information, creating it (and changing
       * the receiver to start key-value-observing by switching its class)
       * if necessary.
       */
      info = (GSKVOInfo*)[self observationInfo];
      if (info == nil)
        {
          info = [[GSKVOInfo alloc] initWithInstance: self];
          [self setObservationInfo: info];
          object_setClass(self, [r replacement]);
        }
    
      /*
       * Now add the observer.
       */
      dot = [aPath rangeOfString:@"."];
      if (dot.location != NSNotFound)
        {
          forwarder = [[NSKeyValueObservationForwarder alloc]
            initWithKeyPath: aPath
               ofObject: self
             withTarget: anObserver
            context: aContext];
          [info addObserver: anObserver
                 forKeyPath: aPath
                    options: options
                    context: forwarder];
        }
      else
        {
          [r overrideSetterFor: aPath];
          [info addObserver: anObserver
                 forKeyPath: aPath
                    options: options
                    context: aContext];
        }
    
      [kvoLock unlock];
    }
    
    @interface  GSKVOInfo : NSObject
    {
      NSObject          *instance;  // Not retained.
      NSRecursiveLock           *iLock;
      NSMapTable            *paths;
    }
    
    • 接下来我们研究GKSKVOInfo是如何处理observer
      找到GKSKVOInfoaddObserver: (NSObject*)anObserver forKeyPath: (NSString*)aPath options: (NSKeyValueObservingOptions)options context: (void*)aContext方法
    - (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];
      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];
          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];
    }
    
    

    注意观察 GSAssignZeroingWeakPointer 最终observer是通过该方法赋值给了GKSKVOInfo中的observation

    综上所有逻辑可以推测observer和被观察的对象之间没有强引用关系。
    感谢各位阅读,结论只是本人推论得出,有什么错误希望大家指正!

    相关文章

      网友评论

        本文标题:KVO为什么不会循环引用

        本文链接:https://www.haomeiwen.com/subject/ckbehltx.html