项目中有用到FBKVOController,觉得有点神奇,和原生KVO一样的功能,但是用法却不一样。原生KVO是被观察者添加观察者对象,添加了还要移除;FBKVOController则是观察者主动添加被观察者对象,无需开发者手动移除监听。今天我们就来阅读一下FBKVOController的源码,整体上看,FBKVOController的代码很简洁。
NSObject+FBKVOController
这个分类只便捷得给NSObject添加2个属性,分别是KVOController
和KVOControllerNonRetaining
,这两者的区别在于会不会对观察的对象造成引用计数器+1
@interface NSObject (FBKVOController)
@property (nonatomic, strong) FBKVOController *KVOController;
@property (nonatomic, strong) FBKVOController *KVOControllerNonRetaining;
@end
FBKVOController
整个框架的核心在于这个文件,内部实现了KVO监听,这个文件内部主要有3个类,
FBKVOController
,_FBKVOSharedController
,_FBKVOInfo
FBKVOController
- (instancetype)initWithObserver:(nullable id)observer retainObserved:(BOOL)retainObserved
{
self = [super init];
if (nil != self) {
_observer = observer;
NSPointerFunctionsOptions keyOptions = retainObserved ? NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPointerPersonality : NSPointerFunctionsWeakMemory|NSPointerFunctionsObjectPointerPersonality;
_objectInfosMap = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPersonality capacity:0];
pthread_mutex_init(&_lock, NULL);
}
return self;
}
NSMapTable是类似于NSDictionary的存储结构,但NSMapTable能自定义Key和Value的内存引用方式,通过参数retainObserved
决定是否要对Key的引用计数器+1
- observe
- (void)observe:(nullable id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(FBKVONotificationBlock)block
{
// create info
_FBKVOInfo *info = [[_FBKVOInfo alloc] initWithController:self keyPath:keyPath options:options block:block];
// observe object with info
[self _observe:object info:info];
}
- (void)observe:(nullable id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options action:(SEL)action
{
// create info
_FBKVOInfo *info = [[_FBKVOInfo alloc] initWithController:self keyPath:keyPath options:options action:action];
// observe object with info
[self _observe:object info:info];
}
- (void)observe:(nullable id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context
{
// create info
_FBKVOInfo *info = [[_FBKVOInfo alloc] initWithController:self keyPath:keyPath options:options context:context];
// observe object with info
[self _observe:object info:info];
}
FBKVOController提供了3种方式监听对象,最终都是把监听信息包装成_FBKVOInfo对象
- (void)_observe:(id)object info:(_FBKVOInfo *)info
{
[[_FBKVOSharedController sharedController] observe:object info:info];
}
监听之前先判断是否监听过object
对象的同一属性(keyPath);如果之前没监听过,则监听object
对象。
[[_FBKVOSharedController sharedController] observe:object info:info];
等等,为什么不是FBKVOController对象去监听,而是** _FBKVOSharedController去监听呢?关于这个问题,等会阅读完 _FBKVOSharedController**之后就知道了。
- unobserve
- (void)_unobserve:(id)object info:(_FBKVOInfo *)info
{
// get observation infos
NSMutableSet *infos = [_objectInfosMap objectForKey:object];
// lookup registered info instance
_FBKVOInfo *registeredInfo = [infos member:info];
// unobserve
[[_FBKVOSharedController sharedController] unobserve:object info:registeredInfo];
}
- (void)_unobserve:(id)object
{
NSMutableSet *infos = [_objectInfosMap objectForKey:object];
// unobserve
[[_FBKVOSharedController sharedController] unobserve:object infos:infos];
}
- (void)_unobserveAll
{
NSMapTable *objectInfoMaps = [_objectInfosMap copy];
// clear table and map
[_objectInfosMap removeAllObjects];
_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];
}
}
移除监听,全是交由_FBKVOSharedController类去完成的,FBKVOController自身只是做了一个记录,核心功能全在_FBKVOSharedController中
- (void)dealloc
{
[self unobserveAll];
pthread_mutex_destroy(&_lock);
}
FBKVOController在即将被销毁的时候,把所有监听对象都移除监听,避免内存泄漏。
_FBKVOSharedController
@implementation _FBKVOSharedController
{
NSHashTable<_FBKVOInfo *> *_infos;
pthread_mutex_t _mutex;
}
...
@end
内部有一个NSHashTable类型的_infos
成员变量,用于存放监听信息_FBKVOInfo对象
+ (instancetype)sharedController
{
static _FBKVOSharedController *_controller = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_controller = [[_FBKVOSharedController alloc] init];
});
return _controller;
}
看到这个就猜想到_FBKVOSharedController是一个单例。
接下来就是整个FBKVOController库的核心所在了
- (void)observe:(id)object info:(nullable _FBKVOInfo *)info
{
// 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];
}
所有对象的观察者都是_FBKVOSharedController单例对象,这样避免了原生KVO中的Observer和被observe对象的相互关联了。并把监听信息info
当做上下文context传递
- (void)observeValueForKeyPath:(nullable NSString *)keyPath
ofObject:(nullable id)object
change:(nullable NSDictionary *)change
context:(nullable void *)context
{
NSAssert(context, @"missing context keyPath:%@ object:%@ change:%@", keyPath, object, change);
_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) {
// 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 *changeWithKeyPath = change;
// add the keyPath to the change dictionary for clarity when mulitple keyPaths are being observed
if (keyPath) {
NSMutableDictionary *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];
}
}
}
}
}
当观察者对象observer被销毁时,不做处理,优先选择block回调,其次再选择@selector方式回调,最后才选择原生KVO方法回调。
最后的移除观察者很简单
- (void)unobserve:(id)object info:(nullable _FBKVOInfo *)info
{
// unregister info
pthread_mutex_lock(&_mutex);
[_infos removeObject:info];
pthread_mutex_unlock(&_mutex);
// remove observer
if (info->_state == _FBKVOInfoStateObserving) {
[object removeObserver:self forKeyPath:info->_keyPath context:(void *)info];
}
info->_state = _FBKVOInfoStateNotObserving;
}
- (void)unobserve:(id)object infos:(nullable NSSet<_FBKVOInfo *> *)infos
{
// unregister info
pthread_mutex_lock(&_mutex);
for (_FBKVOInfo *info in infos) {
[_infos removeObject:info];
}
pthread_mutex_unlock(&_mutex);
// remove observer
for (_FBKVOInfo *info in infos) {
if (info->_state == _FBKVOInfoStateObserving) {
[object removeObserver:self forKeyPath:info->_keyPath context:(void *)info];
}
info->_state = _FBKVOInfoStateNotObserving;
}
}
_FBKVOInfo
_FBKVOInfo作为FBKVOController和_FBKVOSharedController之间的数据传递,主要是记录观察者和被观察者之间的数据信息
@implementation _FBKVOInfo
{
@public
__weak FBKVOController *_controller; // 观察者控制器
NSString *_keyPath; // 想要监听对象的键
NSKeyValueObservingOptions _options; // 监听参数
SEL _action; // 回调action
void *_context; // 上下文环境
FBKVONotificationBlock _block; // 监听通知回调block
_FBKVOInfoState _state; // 监听信息状态
}
最后再附上一张图简单得说明一下这三个类之间的关系
建议自己下载源码看一下,很容易阅读