Zach的博客

FBKVOController源码阅读笔记

FBKVOController 源码阅读笔记

FBKVOContoller是Facebook推出的KVO的库,目的在于简化KVO的代码编写,它提供了干净的block回调,避免了散落到各处的处理逻辑。

结构分析

FBKVOController主要由以下三个部分组成:

  1. FBKVOInfo
  2. FBKVOSharedController
  3. FBKVOControlller

FBKVOInfo

FBKVOInfo用来作为KVO中的Context,它的定义如下:

1
@implementation _FBKVOInfo
{
@public
  __weak FBKVOController *_controller;
  NSString *_keyPath;
  NSKeyValueObservingOptions _options;
  SEL _action;
  void *_context;
  FBKVONotificationBlock _block;
  _FBKVOInfoState _state;
}

_FBKVOInfoState的定义如下:

1
typedef NS_ENUM(uint8_t, _FBKVOInfoState) {
  _FBKVOInfoStateInitial = 0,

  // whether the observer registration in Foundation has completed
  _FBKVOInfoStateObserving,

  // whether `unobserve` was called before observer registration in Foundation has completed
  // this could happen when `NSKeyValueObservingOptionInitial` is one of the NSKeyValueObservingOptions
  _FBKVOInfoStateNotObserving,
};

以上的定义基本可以后名字推断,就不再赘述。

FBKVOSharedController

FBKVOSharedController是一个单例,所有的观察信息以FBKVOInfo的形式交由其处理,FBKVOSharedController内部实现了KVO机制,当FBKVOInfo中的keypath对应的属性发生改变,那么对应的回调就会被执行。

具体的代码如下:

1
- (void)observeValueForKeyPath:(nullable NSString *)keyPath
                      ofObject:(nullable id)object
                        change:(nullable NSDictionary<NSString *, id> *)change
                       context:(nullable void *)context
{
  NSAssert(context, @"missing context keyPath:%@ object:%@ change:%@", keyPath, object, change);
  
  _FBKVOInfo *info;
  
  {
    // lookup context in registered infos, taking out a strong reference only if it exists
    OSSpinLockLock(&_lock);
    info = [_infos member:(__bridge id)context];
    OSSpinLockUnlock(&_lock);
  }
  
  if (nil != info) {
    
    // take strong reference to controller
    FBKVOController *controller = info->_controller;
    if (nil != controller) {
      
      // take strong reference to observer
      id observer = controller.observer;
      if (nil != observer) {
        
        // dispatch custom block or action, fall back to default action
        if (info->_block) {
          info->_block(observer, object, change);
        } else if (info->_action) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
          [observer performSelector:info->_action withObject:change withObject:object];
#pragma clang diagnostic pop
        } else {
          [observer observeValueForKeyPath:keyPath ofObject:object change:change context:info->_context];
        }
      }
    }
  }
}

FBKVOSharedController还定义了以下三个方法,用于添加删除FBKVOInfo到其维护的一个NSHashTabe<_fbkvoinfo *="">中。

代码如下:

1
- (void)observe:(id)object info:(nullable _FBKVOInfo *)info

- (void)unobserve:(id)object info:(nullable _FBKVOInfo *)info

- (void)unobserve:(id)object infos:(nullable NSSet<_FBKVOInfo *> *)infos

FBKVOController

FBKVOController内部维护了一个NSMapTable *>用于建立一个被观察对象和被观察对象的KVOInfos(一个被观察对象可以又许多keypath被观察)的映射。

除此之外,FBKVOController只是简单的地调用FBKVOSharedController提供的方法来添加/删除观察对象。