FBKVO源码学习

zhujiangtaotaise 2020-01-01

用法:

1. 初始化并

- (FBKVOController *)kvoCtrl{

    if (!_kvoCtrl) {

        _kvoCtrl = [FBKVOController controllerWithObserver:self];

    }

    return _kvoCtrl;

}

2. 添加观察者两种方式,target和函数式编程

[self.kvoCtrl observe:self.person keyPath:@"age" options:0 action:@selector(fx_observerAge)];

[self.kvoCtrl observe:self.person keyPath:@"name" options:(NSKeyValueObservingOptionNew) block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {

        NSLog(@"****%@****",change);

    }];

源码查看:

中间也生成一个信息类,来保存KVO信息。

- (void)observe:(nullable id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options action:(SEL)action

{

  NSAssert(0 != keyPath.length && NULL != action, @"missing required parameters observe:%@ keyPath:%@ action:%@", object, keyPath, NSStringFromSelector(action));

  NSAssert([_observer respondsToSelector:action], @"%@ does not respond to %@", _observer, NSStringFromSelector(action));

  if (nil == object || 0 == keyPath.length || NULL == action) {

    return;

  }

  // create info

  _FBKVOInfo *info = [[_FBKVOInfo alloc] initWithController:self keyPath:keyPath options:options action:action];

  // observe object with info

  [self _observe:object info:info];

}

 

保存观察者信息:

这里使用的是 NSMutableSet 存储info,然后再把set到NSMap,原因是set里面是弱引用的,依赖关系更加弱,什么时候想销毁对象都可以。

然后判断是否已经包含,如果有直接返回,如果没有创建然后添加到set里面去。

然后使用一个KVO单例对象(_FBKVOSharedController)所有的KVO都是用这个单例对象。

- (void)_observe:(id)object info:(_FBKVOInfo *)info

{

  // lock

  pthread_mutex_lock(&_lock);

  NSMutableSet *infos = [_objectInfosMap objectForKey:object];

  // check for info existence

  _FBKVOInfo *existingInfo = [infos member:info];

  if (nil != existingInfo) {

    // observation info already exists; do not observe it again

    // unlock and return

    pthread_mutex_unlock(&_lock);

    return;

  }

  // lazilly create set of infos

  if (nil == infos) {

    infos = [NSMutableSet set];

    [_objectInfosMap setObject:infos forKey:object];

  }

  // add info and oberve

  [infos addObject:info];

  // unlock prior to callout

  pthread_mutex_unlock(&_lock);

  [[_FBKVOSharedController sharedController] observe:object info:info];

}

添加观察者还是用的系统的addObserver,对系统KVO做了二次封装。观察者全都是单例Controller。

那么这样处理的原因是什么?

谈下自己的看法:在开发过程中,KVO发生循环引用,这种处理方式打破了循环引用,而且还可以自定义提供回调信息的方式(target,block,代理),消息二次转发。

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

{

  if (nil == info) {

    return;

  }

  // 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];

  if (info->_state == _FBKVOInfoStateInitial) {

    info->_state = _FBKVOInfoStateObserving;

  } else if (info->_state == _FBKVOInfoStateNotObserving) {

    // this could happen when `NSKeyValueObservingOptionInitial` is one of the NSKeyValueObservingOptions,

    // and the observer is unregistered within the callback block.

    // at this time the object has been registered as an observer (in Foundation KVO),

    // so we can safely unobserve it.

    [object removeObserver:self forKeyPath:info->_keyPath context:(void *)info];

  }

}

回调流程判断(先判断block,target,然后是代理),自由度很高。

FBKVO源码学习

所以它的对象持有关系是这样的

self(vc) -> kvoCtrl -> info <- infos <--单例 <- self.person 巧妙的避免了循环引用,很安全。

不需要移除观察者的处理:

在dealloc里面移除了所有的set集合

- (void)dealloc

{

  [self unobserveAll];

  pthread_mutex_destroy(&_lock);

}

- (void)unobserveAll

{

  [self _unobserveAll];

}

- (void)_unobserveAll

{

  // lock

  pthread_mutex_lock(&_lock);

  NSMapTable *objectInfoMaps = [_objectInfosMap copy];

  // clear table and map

  [_objectInfosMap removeAllObjects];

  // unlock

  pthread_mutex_unlock(&_lock);

  _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];

  }

}

总结:中间类只提供响应和信息,其他的事件处理交给单例管理对象处理, 中间层断依赖,简单,轻便.

相关推荐

TiDBPingCAP / 0评论 2020-07-29