FBKVOController阅读

项目中有用到FBKVOController,觉得有点神奇,和原生KVO一样的功能,但是用法却不一样。原生KVO是被观察者添加观察者对象,添加了还要移除;FBKVOController则是观察者主动添加被观察者对象,无需开发者手动移除监听。今天我们就来阅读一下FBKVOController的源码,整体上看,FBKVOController的代码很简洁。

NSObject+FBKVOController

这个分类只便捷得给NSObject添加2个属性,分别是KVOControllerKVOControllerNonRetaining,这两者的区别在于会不会对观察的对象造成引用计数器+1

@interface NSObject (FBKVOController)

@property (nonatomic, strong) FBKVOController *KVOController;

@property (nonatomic, strong) FBKVOController *KVOControllerNonRetaining;

@end

FBKVOController

整个框架的核心在于这个文件,内部实现了KVO监听,这个文件内部主要有3个类,FBKVOController_FBKVOSharedController_FBKVOInfo

FBKVOController

- (instancetype)initWithObserver:(nullable id)observer retainObserved:(BOOL)retainObserved
{
  self = [super init];
  if (nil != self) {
    _observer = observer;
    NSPointerFunctionsOptions keyOptions = retainObserved ? NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPointerPersonality : NSPointerFunctionsWeakMemory|NSPointerFunctionsObjectPointerPersonality;
    _objectInfosMap = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPersonality capacity:0];
    pthread_mutex_init(&_lock, NULL);
  }
  return self;
}

NSMapTable是类似于NSDictionary的存储结构,但NSMapTable能自定义Key和Value的内存引用方式,通过参数retainObserved决定是否要对Key的引用计数器+1

  • observe
- (void)observe:(nullable id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(FBKVONotificationBlock)block
{
  // create info
  _FBKVOInfo *info = [[_FBKVOInfo alloc] initWithController:self keyPath:keyPath options:options block:block];
  // observe object with info
  [self _observe:object info:info];
}
- (void)observe:(nullable id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options action:(SEL)action
{
  // create info
  _FBKVOInfo *info = [[_FBKVOInfo alloc] initWithController:self keyPath:keyPath options:options action:action];
  // observe object with info
  [self _observe:object info:info];
}
- (void)observe:(nullable id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context
{
  // create info
  _FBKVOInfo *info = [[_FBKVOInfo alloc] initWithController:self keyPath:keyPath options:options context:context];
  // observe object with info
  [self _observe:object info:info];
}

FBKVOController提供了3种方式监听对象,最终都是把监听信息包装成_FBKVOInfo对象

- (void)_observe:(id)object info:(_FBKVOInfo *)info
{
  [[_FBKVOSharedController sharedController] observe:object info:info];
}

监听之前先判断是否监听过object对象的同一属性(keyPath);如果之前没监听过,则监听object对象。

[[_FBKVOSharedController sharedController] observe:object info:info];
等等,为什么不是FBKVOController对象去监听,而是** _FBKVOSharedController去监听呢?关于这个问题,等会阅读完 _FBKVOSharedController**之后就知道了。

  • unobserve
- (void)_unobserve:(id)object info:(_FBKVOInfo *)info
{
  // get observation infos
  NSMutableSet *infos = [_objectInfosMap objectForKey:object];
  // lookup registered info instance
  _FBKVOInfo *registeredInfo = [infos member:info];
  // unobserve
  [[_FBKVOSharedController sharedController] unobserve:object info:registeredInfo];
}

- (void)_unobserve:(id)object
{
  NSMutableSet *infos = [_objectInfosMap objectForKey:object];
  // unobserve
  [[_FBKVOSharedController sharedController] unobserve:object infos:infos];
}

- (void)_unobserveAll
{
  NSMapTable *objectInfoMaps = [_objectInfosMap copy];
  // clear table and map
  [_objectInfosMap removeAllObjects];
  _FBKVOSharedController *shareController = [_FBKVOSharedController sharedController];
  for (id object in objectInfoMaps) {
    // unobserve each registered object and infos
    NSSet *infos = [objectInfoMaps objectForKey:object];
    [shareController unobserve:object infos:infos];
  }
}

移除监听,全是交由_FBKVOSharedController类去完成的,FBKVOController自身只是做了一个记录,核心功能全在_FBKVOSharedController

- (void)dealloc
{
  [self unobserveAll];
  pthread_mutex_destroy(&_lock);
}

FBKVOController在即将被销毁的时候,把所有监听对象都移除监听,避免内存泄漏。

_FBKVOSharedController

@implementation _FBKVOSharedController
{
  NSHashTable<_FBKVOInfo *> *_infos;
  pthread_mutex_t _mutex;
}
...
@end

内部有一个NSHashTable类型的_infos成员变量,用于存放监听信息_FBKVOInfo对象

+ (instancetype)sharedController
{
  static _FBKVOSharedController *_controller = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    _controller = [[_FBKVOSharedController alloc] init];
  });
  return _controller;
}

看到这个就猜想到_FBKVOSharedController是一个单例。
接下来就是整个FBKVOController库的核心所在了

- (void)observe:(id)object info:(nullable _FBKVOInfo *)info
{
  // register info
  pthread_mutex_lock(&_mutex);
  [_infos addObject:info];
  pthread_mutex_unlock(&_mutex);

  // add observer
  [object addObserver:self forKeyPath:info->_keyPath options:info->_options context:(void *)info];
}

所有对象的观察者都是_FBKVOSharedController单例对象,这样避免了原生KVO中的Observer和被observe对象的相互关联了。并把监听信息info当做上下文context传递

- (void)observeValueForKeyPath:(nullable NSString *)keyPath
                      ofObject:(nullable id)object
                        change:(nullable NSDictionary *)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
    pthread_mutex_lock(&_mutex);
    info = [_infos member:(__bridge id)context];
    pthread_mutex_unlock(&_mutex);
  }

  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) {
          NSDictionary *changeWithKeyPath = change;
          // add the keyPath to the change dictionary for clarity when mulitple keyPaths are being observed
          if (keyPath) {
            NSMutableDictionary *mChange = [NSMutableDictionary dictionaryWithObject:keyPath forKey:FBKVONotificationKeyPathKey];
            [mChange addEntriesFromDictionary:change];
            changeWithKeyPath = [mChange copy];
          }
          info->_block(observer, object, changeWithKeyPath);
        } 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];
        }
      }
    }
  }
}

当观察者对象observer被销毁时,不做处理,优先选择block回调,其次再选择@selector方式回调,最后才选择原生KVO方法回调。

最后的移除观察者很简单

- (void)unobserve:(id)object info:(nullable _FBKVOInfo *)info
{
  // unregister info
  pthread_mutex_lock(&_mutex);
  [_infos removeObject:info];
  pthread_mutex_unlock(&_mutex);

  // remove observer
  if (info->_state == _FBKVOInfoStateObserving) {
    [object removeObserver:self forKeyPath:info->_keyPath context:(void *)info];
  }
  info->_state = _FBKVOInfoStateNotObserving;
}

- (void)unobserve:(id)object infos:(nullable NSSet<_FBKVOInfo *> *)infos
{
  // unregister info
  pthread_mutex_lock(&_mutex);
  for (_FBKVOInfo *info in infos) {
    [_infos removeObject:info];
  }
  pthread_mutex_unlock(&_mutex);

  // remove observer
  for (_FBKVOInfo *info in infos) {
    if (info->_state == _FBKVOInfoStateObserving) {
      [object removeObserver:self forKeyPath:info->_keyPath context:(void *)info];
    }
    info->_state = _FBKVOInfoStateNotObserving;
  }
}

_FBKVOInfo

_FBKVOInfo作为FBKVOController_FBKVOSharedController之间的数据传递,主要是记录观察者和被观察者之间的数据信息

@implementation _FBKVOInfo
{
@public
  __weak FBKVOController *_controller;  // 观察者控制器
  NSString *_keyPath;                   // 想要监听对象的键
  NSKeyValueObservingOptions _options;  // 监听参数
  SEL _action;                          // 回调action
  void *_context;                       // 上下文环境
  FBKVONotificationBlock _block;        // 监听通知回调block
  _FBKVOInfoState _state;               // 监听信息状态 
}
最后再附上一张图简单得说明一下这三个类之间的关系
FBKVOController阅读_第1张图片
KVOController.jpg

建议自己下载源码看一下,很容易阅读

你可能感兴趣的:(FBKVOController阅读)