翻一下 FBKVOController 的源代码

引子,苹果提供了两种 KVO

第二种,挺方便的

1, 通过调用方法,addObserver(

  • 监听的对象
class Cate: NSObject{
    
    @objc dynamic var age = 51
    
    var k: String{
        "age"
    }
}


class ViewController: UIViewController {
    let cat = Cate()
}
复制代码
  • 添加观察者
cat.addObserver(self, forKeyPath: cat.k, options: .new, context: nil)
复制代码
  • 响应观察
 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if let dict = change{
            print("age :   ", dict[.newKey] ?? "ha ha")
        }
    }
复制代码
  • 移除观察者
cat.removeObserver(self, forKeyPath: cat.k, context: nil)
复制代码

2,便捷的 closure

  • 观察的行为,作为一个对象
var moneyObservation: NSKeyValueObservation?
复制代码
  • 添加观察者,闭包响应

被观察对象,见上文

直接写路径,不再是字符串硬编码 ( 较容易出错 )

         moneyObservation = cat.observe(\.age, changeHandler: { cat, change in
            print("money :  ", cat.age)
        })
复制代码
  • 移除观察者
moneyObservation = nil
复制代码

进入 FBKVOController, FB 自研部分

FBKVOController 对系统的 KVO 方法,有一个封装,

从苹果提供的 KVO 实现一,到 KVO 实现 2

和随着所属控制器的释放,自动移除观察

FBKVOController 思路跟一些三方库,非常一致

  • 有一个单例 _FBKVOSharedController,具体做事情的

  • 有一个关联对象 FBKVOController,动态将事件添加到该单例

( 还有,动态将事件,注销 )

FBKVOController, 自个作为属性,创建使用,也可

FBKVOController 的具体实现

特点就是,把一般对控制器 ctrl 的观察,转化为对统一的单例对象的观察

添加观察者

  • 先保存信息


@implementation FBKVOController

- (void)observe:(nullable id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(FBKVONotificationBlock)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];
}


@end
复制代码
  • 实现观察者的转移,

一般是 ctrl , 观察对象的某个属性

转化为

单例 _FBKVOSharedController ,观察对象的某个属性

@implementation FBKVOController


- (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];
}


@end
复制代码

剩余的就是

单例 _FBKVOSharedController ,调用苹果的 KVO 实现一

响应变化

很全面

  • block

  • target - action

  • 苹果的 KVO 实现一,的响应方法

@implementation _FBKVOSharedController


- (void)observeValueForKeyPath:(nullable NSString *)keyPath
                      ofObject:(nullable id)object
                        change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change
                       context:(nullable void *)context
{
  _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) {
      // 这里是 weak - strong dance 的第二步
      // 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<NSKeyValueChangeKey, id> *changeWithKeyPath = change;
          // add the keyPath to the change dictionary for clarity when mulitple keyPaths are being observed
          if (keyPath) {
            NSMutableDictionary<NSString *, id> *mChange = [NSMutableDictionary dictionaryWithObject:keyPath forKey:FBKVONotificationKeyPathKey];
            [mChange addEntriesFromDictionary:change];
            changeWithKeyPath = [mChange copy];
          }
          // block 响应
          info->_block(observer, object, changeWithKeyPath);
        } else if (info->_action) {
          // target - action
          [observer performSelector:info->_action withObject:change withObject:object];
        } else {
          //  苹果的 KVO 实现一,的响应方法
          [observer observeValueForKeyPath:keyPath ofObject:object change:change context:info->_context];
        }
      }
    }
  }
}



@end

复制代码

自动释放

FBKVOController 的对象,作为 ctrl 的属性,

ctrl 释放,他的属性 FBKVOController 对象,释放

调用 FBKVOController 的 dealloc 方法


@implementation FBKVOController


// 触发,释放
- (void)dealloc
{
  [self unobserveAll];
  pthread_mutex_destroy(&_lock);
}


// 具体的释放逻辑

// 两层, FBKVOController 中
// _FBKVOSharedController 中
- (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];
  }
}


@end

复制代码

githud demo

猜你喜欢

转载自juejin.im/post/7013823065486278669
今日推荐