FBKVOController实现原理(简单描述)

在看这篇文章之前,建议自己写一个小的FBKVOController Example,如果懒得写可以在Github上clone我写的一个非常简单的example。这样能建立一个大体的了解。

对代码中的细节部分没有做介绍,例如锁机制,Set,Map这些,只要知道作用即可,不影响对核心代码的理解。

代码结构:

1.FBKVOController
 对外公开的类,对外提供了初始化,数种监听的方法。
2._FBKVOInfo
 内部类,用来记录监听所需的参数信息。
3._FBKVOSharedController
内部类,真正实现kvo的类,通过FBKVOController的外部方法调用。

内部实现

1.在ViewController中初始化,调用。
ViewController.m

@property (nonatomic,strong) FBKVOController * kvoController;
@property (nonatomic,strong) DataModel * model;

.....

self.kvoController = [FBKVOController controllerWithObserver:self];//初始化FBKVOController
    self.model = [[dataView alloc] init];//被监听的model
    [self.kvoController observe:_model keyPath:@"colorString" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial block:^(ViewController * observer, id object, NSDictionary *change) {
        //some code in block
        //....
    }];

KVOController有两种监听方式:一种是block,另一种是用action(或者selector)的方式,上面是block方式。

上面代码实现的是监听_model中的colorString属性,当该数值初始化(NSKeyValueObservingOptionInitial)或者重新赋值(NSKeyValueObservingOptionNew)时,执行block中的代码。


2 .FBKVOController提供的外部方法
- (void)observe:(id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(FBKVONotificationBlock)block
{
  NSAssert(0 != keyPath.length && NULL != block, @"missing required parameters observe:%@ keyPath:%@ block:%p", object, keyPath, block);
  if (nil == object || 0 == keyPath.length || NULL == block) {
    return;
  }

  // create info
  _FBKVOInfo *info = [[_FBKVOInfo alloc] initWithController:self keyPath:keyPath options:options block:block];

  // observe object with info
  [self _observe:object info:info];
}

FBKVOController的监听方法首先会检查参数的合法性,如果参数为NULL,则直接return,否则将接受的参数通过// create info 这段代码存储到类info中,然后调用方法: - (void)_observe:(id)object info:(_FBKVOInfo *)info 继续看看这个方法都做了哪些事情。

- (void)_observe:(id)object info:(_FBKVOInfo *)info
{
  // lock
  OSSpinLockLock(&_lock);

  NSMutableSet *infos = [_objectInfosMap objectForKey:object];

  // check for info existence
  _FBKVOInfo *existingInfo = [infos member:info];
  if (nil != existingInfo) {
    NSLog(@"observation info already exists %@", existingInfo);

    // unlock and return
    OSSpinLockUnlock(&_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
  OSSpinLockUnlock(&_lock);

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

该方法首先检查一下之前是否有重复监听的属性,如果有的话,直接返回,否则就调用_FBKVOSharedController中的监听方法,之前说过,_FBKVOSharedController是真正实现监听的内部类。

3 . _FBKVOSharedController内部类

FBKVO实现代码使用了很多C,C++的语法。至此开始真正进入observe的具体实现。

- (void)observe:(id)object info:(_FBKVOInfo *)info
{
  if (nil == info) {
    return;
  }

  // register info
  OSSpinLockLock(&_lock);
  [_infos addObject:info];
  OSSpinLockUnlock(&_lock);

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

该方法前面都是对参数的记录,最后一段代码是关键:

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

这段代码中,object是之前被监听的_model类,self是 _FBKVOSharedController,在从头理一遍代码,我们的初衷是要监听 _model类中的属性,在这里变成监听 _FBKVOSharedController中的属性。而且是调用的系统方法:

 - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context; //API1

_FBKVOSharedController内部调用上面系统提供的方法,然后重写了另一个系统方法:

  - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context  //API2

通过调用API1,对属性进行监听,如果属性出现变化的时候,系统会去调用API2,重写API2,可以控制KVO的具体实现,不论是增加block还是action都可以在API2中进行。

下面是API2的具体实现:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(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];
        }
      }
    }
  }
}

方法会检测,是否要执行block,如果block参数不为空则执行:info->_block(observer, object, change); 如果有action,则用performSelector:执行action,否则调用系统自带的监听方法。

如果有任何问题欢迎再下面留言,或者扫描二维码
wechat

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值