在看这篇文章之前,建议自己写一个小的FBKVOController Example,如果懒得写可以在Github上clone我写的一个非常简单的example。这样能建立一个大体的了解。
对代码中的细节部分没有做介绍,例如锁机制,Set,Map这些,只要知道作用即可,不影响对核心代码的理解。
代码结构:
1.FBKVOController
对外公开的类,对外提供了初始化,数种监听的方法。
2._FBKVOInfo
内部类,用来记录监听所需的参数信息。
3._FBKVOSharedController
内部类,真正实现kvo的类,通过FBKVOController的外部方法调用。
内部实现
1.在ViewController中初始化,调用。
ViewController.m
@property (nonatomic,strong) FBKVOController * kvoController;
@property (nonatomic,strong) DataModel * model;
.....
self.kvoController = [FBKVOController controllerWithObserver:self];//初始化FBKVOController
self.model = [[dataView alloc] init];//被监听的model
[self.kvoController observe:_model keyPath:@"colorString" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial block:^(ViewController * observer, id object, NSDictionary *change) {
//some code in block
//....
}];
KVOController有两种监听方式:一种是block,另一种是用action(或者selector)的方式,上面是block方式。
上面代码实现的是监听_model中的colorString属性,当该数值初始化(NSKeyValueObservingOptionInitial)或者重新赋值(NSKeyValueObservingOptionNew)时,执行block中的代码。
2 .FBKVOController提供的外部方法
- (void)observe:(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];
}
FBKVOController的监听方法首先会检查参数的合法性,如果参数为NULL,则直接return,否则将接受的参数通过// create info 这段代码存储到类info中,然后调用方法: - (void)_observe:(id)object info:(_FBKVOInfo *)info 继续看看这个方法都做了哪些事情。
- (void)_observe:(id)object info:(_FBKVOInfo *)info
{
// lock
OSSpinLockLock(&_lock);
NSMutableSet *infos = [_objectInfosMap objectForKey:object];
// check for info existence
_FBKVOInfo *existingInfo = [infos member:info];
if (nil != existingInfo) {
NSLog(@"observation info already exists %@", existingInfo);
// unlock and return
OSSpinLockUnlock(&_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
OSSpinLockUnlock(&_lock);
[[_FBKVOSharedController sharedController] observe:object info:info];
}
该方法首先检查一下之前是否有重复监听的属性,如果有的话,直接返回,否则就调用_FBKVOSharedController中的监听方法,之前说过,_FBKVOSharedController是真正实现监听的内部类。
3 . _FBKVOSharedController内部类
FBKVO实现代码使用了很多C,C++的语法。至此开始真正进入observe的具体实现。
- (void)observe:(id)object info:(_FBKVOInfo *)info
{
if (nil == info) {
return;
}
// register info
OSSpinLockLock(&_lock);
[_infos addObject:info];
OSSpinLockUnlock(&_lock);
// add observer
[object addObserver:self forKeyPath:info->_keyPath options:info->_options context:(void *)info];
}
该方法前面都是对参数的记录,最后一段代码是关键:
[object addObserver:self forKeyPath:info->_keyPath options:info->_options context:(void *)info];
这段代码中,object是之前被监听的_model类,self是 _FBKVOSharedController,在从头理一遍代码,我们的初衷是要监听 _model类中的属性,在这里变成监听 _FBKVOSharedController中的属性。而且是调用的系统方法:
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context; //API1
_FBKVOSharedController内部调用上面系统提供的方法,然后重写了另一个系统方法:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context //API2
通过调用API1,对属性进行监听,如果属性出现变化的时候,系统会去调用API2,重写API2,可以控制KVO的具体实现,不论是增加block还是action都可以在API2中进行。
下面是API2的具体实现:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(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
OSSpinLockLock(&_lock);
info = [_infos member:(__bridge id)context];
OSSpinLockUnlock(&_lock);
}
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) {
info->_block(observer, object, change);
} 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];
}
}
}
}
}
方法会检测,是否要执行block,如果block参数不为空则执行:info->_block(observer, object, change); 如果有action,则用performSelector:执行action,否则调用系统自带的监听方法。
如果有任何问题欢迎再下面留言,或者扫描二维码