zoukankan      html  css  js  c++  java
  • FBKVO源码学习

    用法:

    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,然后是代理),自由度很高。

     

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

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

      }

    }

    总结:中间类只提供响应和信息,其他的事件处理交给单例管理对象处理, 中间层隔断了依赖,更简单轻便,框架只增加了自己需要的东西(包括函数式回调,自动释放等)但是还是使用系统的添加和移除管擦者方法,因为并不知道系统方法里面具体做了哪些事情,所以并不修改系统的东西,安全性和实用性比较高。

  • 相关阅读:
    国家标准比例尺地形图说明(摘要自SuperMap Objects Document)
    常用日期函数
    CMD执行BCP命令
    如何利用.snk文件生成DLL文件中的Publickeytoken
    SQL SERVER数据库的表中修改字段属性被阻止“Prevent saving changes that require table recreation”
    如何生成DLL文件
    如何反编译DLL文件
    Visual Studio 2022激活密钥
    sqlserver跨数据库查询
    jQuery对象与DOM对象之间的转换
  • 原文地址:https://www.cnblogs.com/coolcold/p/12128599.html
Copyright © 2011-2022 走看看