前言html
苹果的KVO键值观察机制在平常开发中很常见,可是系统提供的API却不支持block回调;再加上添加观察者addObserver:forKeyPath:options:context:只能观察一个键值,而且还要手动的移除键值观察,这些存在的问题致使开发者对现有的机制十分的不满意,涌现出不少的解决方案,本身实现KVO机制(runtime)以及今天要介绍的KVOController库,KVOController是由FaceBook开源的一个KVO库,经过封装系统的KVO机制,可以支持block回调以及隐式移除键值监听等功能,支持设置多个KeyPaths,并且线程安全。ios
KVOController使用git
详细的介绍不是今天要讲述的内容,网络上有不少的文章,分析了其源码的逻辑,大体原理将键值观察信息封装至_FBKVOInfo对象,而后在FBKVOController类中维护着_objectInfosMap表,类型为NSMapTable(是一种相似于NSDictionary的数据结构,能够对Key和Value进行内存管理),Key为observing被观察者对象,最后经过_FBKVOSharedController类,实现键值监听以及回调;当前的控制器被释放掉后,会自动执行键值观察移除操做,由于FBKVOController对象经过runtime关联当前控制器。github
//简单的使用 [self.KVOController observe:hashObject keyPath:@"className" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld block:^(id _Nullable observer, id _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) { NSLog(@"str -- %@", change[NSKeyValueChangeNewKey]); }]; hashObject.className = @"有点意思"; observe是被观察的对象,观察者对象是_FBKVOSharedController这个对象。
KVO崩溃的通常缘由安全
一、被观察者被释放了(被观察者对象是一个局部变量);网络
二、观察者被释放了,可是没有移除键值监听;数据结构
三、注册的监听没有被移除掉,又从新注册了一遍监听;app
官网上也有说明,If your app targets iOS 9.0 and later or macOS 10.11 and later, you don't need to unregister an observer in its deallocation method. If your app targets earlier releases, be sure to invoke removeObserver:name:object: before observer or any object specified in addObserver:selector:name:object: is deallocated.大概意思,iOS9以前的系统,必需要在观察者对象被释放以前显式执行移除键值观察逻辑(缘由2)。线程
问题分析code
NSMapTable能够由开发者自行的设置管理内存的方案,当使用WeakMemory时即便用self.KVOControllerNonRetaining对象去设置键值观察时,当前控制器Pop后程序会直接crash,缘由是因为被观察者对象被释放掉,致使_objectInfosMap表中的内容自动移除,为何会这样?官方文档中有解释,The major option is to have keys and/or values held “weakly” in a manner that entries are removed when one of the objects is reclaimed.大概的意思就是,当key或者是value使用weak内存管理时,只要key或者value对象被释放,当前的记录将会被移除。记录被移除后,执行_unobserveAll移除键值观察逻辑时,没法移除已注册的键值观察,由于保存在_objectInfosMap中的信息被移除掉了。结合KVO的崩溃的缘由,形成崩溃是由被观察者被释放致使。
建议
所以在使用KVOController库时,尽可能的使用self.KVOController对象去绑定键值观察,由于_objectInfosMap会持有observing对象并不会当即释放掉,直到执行完移除键值观察后,清除_objectInfosMap记录,才会被释放掉,这样就不会形成Pop控制器致使系统crash的问题,其余的方法在KVOController的github地址的issues中有讨论,感兴趣的开发者能够关注。