前言 在阅读公司源码的一些功能时发现了KVOController这个神奇的库。这个库十分的好用,可以主动的去观察一个对象的属性。
例如
1 2 3 4 5 6 [self .KVOControllerNonRetaining observe:_test keyPath:@"test" options:0 block::^(id _Nullable observer, id object, NSDictionary <NSString *, id > *change) { }];
KVOController的源码不多,加上分类也就不到800行,所以我花了一段时间阅读它的源码,这篇文章是阅读源码的总结。
类图 下图是我通过阅读源码画的UML类图,因为有些偷懒,所以这个类图的方法并不全。但这并不重要,这张类图的意义在于我们能够清晰地看明白他们之间的关系。
解析 _FBKVOInfo _FBKVOInfo作为一个被两个类组合的类,在KVOController中属于Model的性质,用来保存所需要的内容,以下是这个类拥有的变量
1 2 3 4 5 6 7 __weak FBKVOController *_controller ; NSString *_keyPath ; NSKeyValueObservingOptions _options ; SEL _action ; void *_context ; FBKVONotificationBlock _block ; _FBKVOInfoState _state ;
_controller _FBKVOInfo在FBKVOController中初始化,初始化时就把FBKVOController对象持有了,这里用一个weak修饰防止循环引用
_keyPath 这个应该不怎么需要解释,这个就是KVO观察的keyPath
_options 这个也是KVO观察的设置,是一个枚举,设置不同的枚举KVO效果是不同的,这里就不详细展开了。
1 2 3 4 5 6 typedef NS_OPTIONS (NSUInteger , NSKeyValueObservingOptions ) { NSKeyValueObservingOptionNew = 0x01, NSKeyValueObservingOptionOld = 0x02, NSKeyValueObservingOptionInitial = 0x04, NSKeyValueObservingOptionPrior = 0x08 };
_action、_block 这个是用来保存FBKVOController需要调用的方法和block
context 上下文,这个也不多解释
_state 这是一个很重要的枚举,用来保存_FBKVOInfo所对应对象的观察状态
1 2 3 4 5 6 7 8 typedef NS_ENUM(uint8_t, _FBKVOInfoState ) { _FBKVOInfoStateInitial = 0 , _FBKVOInfoStateObserving , _FBKVOInfoStateNotObserving , };
FBKVOController FBKVOController是KVOController对外暴露的类,其中我们主要用以下两个方法
1 2 3 - (void) observe:(nullable id) object keyPath:(NSString *) keyPath options:(NSKeyValueObservingOptions) options block:(FBKVONotificationBlock) block; - (void) observe:(nullable id) object keyPath:(NSString *) keyPath options:(NSKeyValueObservingOptions) options action:(SEL) action;
一个是KVO之后的block的回调,另一个是KVO之后调用的方法,下面我们以第一个方法进行讲解。
1 2 3 4 5 6 7 8 9 10 11 12 13 - (void )observe:(nullable 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 ]; }
方法执行步骤:
断言以及错误判断
创建一个_FBKVOInfo对象
调用_observe:info:
根据上文的结论,我们可以得知_FBKVOInfo是一个Model的存在,所以需要先把它初始化了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 - (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 ]; }
_objectInfosMap是临界资源,所以在这个方法里进行加锁防止资源被争抢。
方法执行步骤:
加锁
判断是否存在,存在即解锁结束,不需要再次观察;不存在则进入步骤3
判断_objectInfosMap所对应的集合是否存在,存在则继续;不存在则初始化并保存在_objectInfosMap中
保存新的_FBKVOInfo对象
解锁
调用_FBKVOSharedController
这里涉及到一个知识点是NSMapTable,这是一个类似NSDictionary的容器,但是它不仅能做到key和value之间的映射关系,它也能做到object和object之间的映射关系。这种object和object之间的映射关系在KVOController中体现的很好,每一个被观察者(object)对应一个_FBKVOInfo对象(object)。推荐阅读NSMapTable: 不只是一个能放weak指针的 NSDictionary
_FBKVOSharedController _FBKVOSharedController它是一个单例,这个私有类才是KVOController提供服务的实际实现类。
我们继续来看_FBKVOSharedController被FBKVOController所调用的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 - (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) { [object removeObserver:self forKeyPath:info ->_keyPath context:(void *)info ]; } }
方法执行步骤:
添加临界资源
注册观察
判断_FBKVOInfo对象state,若为初始化,则改变为观察中,若为不在观察中,则移除这个观察
这里涉及到NSHashTable,这个类似于NSSet,本文对此不展开说明。
之所以说_FBKVOSharedController才是KVOSharedController的实际实现类是因为它实现了KVO的回调方法
1 2 3 4 - (void )observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id )object change:(nullable NSDictionary <NSString *, id > *)change context:(nullable void *)context
我们来看一下里面的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 - (void )observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id )object change:(nullable NSDictionary <NSString *, id > *)change context:(nullable void *)context { NSAssert (context, @"missing context keyPath:%@ object:%@ change:%@" , keyPath, object, change); _FBKVOInfo *info; { pthread_mutex_lock(&_mutex); info = [_infos member:(__bridge id )context]; pthread_mutex_unlock(&_mutex); } if (nil != info) { FBKVOController *controller = info->_controller; if (nil != controller) { id observer = controller.observer; if (nil != observer) { if (info->_block) { NSDictionary <NSString *, id > *changeWithKeyPath = change; if (keyPath) { NSMutableDictionary <NSString *, id > *mChange = [NSMutableDictionary dictionaryWithObject:keyPath forKey:FBKVONotificationKeyPathKey]; [mChange addEntriesFromDictionary:change]; changeWithKeyPath = [mChange copy ]; } info->_block(observer, object, changeWithKeyPath); } 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]; } } } } }
方法执行步骤
断言
通过context上下文从临界资源_infos中拿到info
进行保护,防止持有的FBKVOController和observe为空
判断_info持有的block或SEL是否存在,存在则调用;不存在则把消息转发给observe
最后一步调用发现block或者SEL都不存在时必须让object调用,因为observe里可能存在observeValueForKeyPath的实现
为什么使用FBKVOController不需要移除通知 在FBKVOController的dealloc里是这样写的
1 2 3 4 5 - (void)dealloc { [self unobserveAll] ; pthread_mutex_destroy (&_lock); }
unobserveAll所调用的是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 - (void)_unobserveAll { pthread_mutex_lock(&_lock ); NSMapTable *objectInfoMaps = [_objectInfosMap copy]; [_objectInfosMap removeAllObjects]; pthread_mutex_unlock(&_lock ); _FBKVOSharedController *shareController = [_FBKVOSharedController sharedController]; for (id object in objectInfoMaps) { NSSet *infos = [objectInfoMaps objectForKey:object]; [shareController unobserve:object infos:infos]; } }
可以发现FBKVOController通过遍历Map,把所持有的观察者都一一去除了
最终调用的方法是_FBKVOSharedController的取消观察方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 - (void)unobserve:(id)object infos:(nullable NSSet<_FBKVOInfo *> *)infos { if (0 == infos.count ) { return; } pthread_mutex_lock(&_mutex ); for (_FBKVOInfo *info in infos) { [_infos removeObject:info]; } pthread_mutex_unlock(&_mutex ); for (_FBKVOInfo *info in infos) { if (info->_state == _FBKVOInfoStateObserving ) { [object removeObserver:self forKeyPath:info->_keyPath context:(void *)info]; } info->_state = _FBKVOInfoStateNotObserving ; } }
这个方法可以看出来object所对应的_FBKVOSharedController所持有的_FBKVOInfo全部都被removeObserver了
“NSObject+FBKVOController.h” KVOController还有一个NSObject的分类,提供两种方式使用KVOController的懒加载,分别是持有方式和不持有方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 - (FBKVOController *)KVOController { id controller = objc_getAssociatedObject(self , NSObjectKVOControllerKey ); if (nil == controller) { controller = [FBKVOController controllerWithObserver:self ]; self .KVOController = controller; } return controller; } - (FBKVOController *)KVOControllerNonRetaining { id controller = objc_getAssociatedObject(self , NSObjectKVOControllerNonRetainingKey ); if (nil == controller) { controller = [[FBKVOController alloc] initWithObserver:self retainObserved:NO ]; self .KVOControllerNonRetaining = controller; } return controller; }
他们的区别就是被观察者的内存管理机制是strong还是weak,前者是strong,后者是weak。