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