美文网首页iOS/MacOS开发
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为什么不会循环引用

    由于Foundation况下中的NSKeyValueObserving并未开源,而且通过C++代码和汇编代码也看不...

  • block 循环引用,__strong、__weak、__blo

    首先说一下循环引用, 为什么没用__weak 修饰就直接用 self.属性不会造成循环引用.循环引用是指两个或者多...

  • Swift之循环引用

    Swift中的循环引用是怎么样的呢?我们来看看: 调用: 这样是不会引起循环引用的,为什么呢? 要循环引用还要se...

  • Block 循环引用,__weak、__block、__stro

    循环引用的理解 首先说一下循环引用,为什么没用 __weak 修饰就直接用 self. 属性,有时候不会造成循环引...

  • ios内存优化实例

    11.避免导致循环引用,通知,timer,KVO的及时remove 1.UITableViewCells,UICo...

  • Day2

    1 单方向的引用不会产生循环引用。循环引用:闭包引用了self,self引用了闭包。Swift推荐使用解决循环引用...

  • 自定义KVO的一些错误使用方式

    很多时候我们讲KVO,使用的时候需要注意移除监听,循环引用等问题,但是在自定义KVO的时候,却容易忽视几个问题。 ...

  • Swift 单例循环引用的坑

    1、直接循环引用的看例子 这里会结果不会输出: 11111。 为什么?因为造成死循环了。Test2.sharedT...

  • (二十四)[Swift]循环引用

    1.什么是循环引用 两个对象通过属性互相强引用对方,导致无法正常释放 情境代码 不会循环引用 会导致循环引用 2....

  • block的循环引用

    什么是循环引用呢? 就是我引用你,你引用我,就会造成循环引用,双方都不会被销毁,导致内存泄漏。 _block = ...

网友评论

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

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