原文 : 与佳期的我的博客(gonghonglou.com)git
Apple 使用了 isa 混写(isa-swizzling)来实现 KVO 。当观察对象 A 时,KVO 机制动态建立一个新的名为:NSKVONotifying_A 的新类,该类继承自对象 A 的本类,Apple 还重写了该类的 -class 方法,返回父类,即对象 A 的本类。且 KVO 为 NSKVONotifying_A 重写观察属性的 setter 方法,setter 方法会负责在调用原 setter 方法以前和以后,通知全部观察对象属性值的更改状况。
isa 指针的做用:每一个对象都有 isa 指针,指向该对象的类,它告诉 Runtime 系统这个对象的类是什么。因此对象注册为观察者时,isa 指针指向新子类,那么这个被观察的对象就变成新子类的对象(或实例)了。 于是在该对象上对 setter 的调用就会调用已重写的 setter,从而激活键值通知机制。github
咱们能够经过断点看到,被观察者对象的 isa 指针已经变成了 NSKVONotifying_ 开头的类: 安全
对于 KVO 使用不当的话很容易出现 Crash,好比添加和移除观察不对应,重复 removeObserver: 或者移除一个不存在的观察者就会形成 Crash,尤为是在多线程操做时防不胜防:bash
2019-07-13 17:50:14.805177+0800 GHLCrashGuard_Example[77448:5047850] *** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <GHLKVOViewController 0x7fd072c17720> for the key path "name" from <GHLTestObject 0x60000106c160> because it is not registered as an observer.'多线程
为了不这种重复添加或者重复移除观察形成的崩溃,能够对 KVO 包装一层。建立一个额外的观察者对象,全部的添加观察和移除观察都经过这个额外的对象,这样在添加和移除的时候就能够作安全判断了。 FaceBook 出品的 KVOController 就是作的这样的事情。app
self.kvo = [FBKVOController controllerWithObserver:self];
[self.kvo observe:self.obj keyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld block:^(id _Nullable observer, id _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
NSLog(@"oldName:%@", [change objectForKey:NSKeyValueChangeOldKey]);
NSLog(@"newName:%@", [change objectForKey:NSKeyValueChangeNewKey]);
}];
复制代码
FBKVOController 的关键主要就在如下三个方法上:ui
+ (instancetype)controllerWithObserver:(nullable id)observer;
建立 FBKVOController 对象,主要作了两件事:一、存储了观察者 _observer,二、建立了 _objectInfosMap,用于存储被观察对象的信息。this
- (void)observe:(nullable id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(FBKVONotificationBlock)block;
spa
一、线程
// create info
_FBKVOInfo *info = [[_FBKVOInfo alloc] initWithController:self keyPath:keyPath options:options block:block];
复制代码
主要在建立 _FBKVOInfo 对象,存储 FBKVOController(存储着观察者 _observer) keyPath(观察属性) options(观察时机) block(回调)
二、
- (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];
}
复制代码
添加观察者以前作的判断,避免重复添加观察。而且添加了 pthread_mutex_lock 互斥锁保证线程安全。
三、
- (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];
}
}
复制代码
调用系统方法 addObserver: 添加观察者,而且在这后判断了 info->_state 若是是非观察状态则执行 removeObserver:
一、
- (void)_unobserve:(id)object info:(_FBKVOInfo *)info
{
// lock
pthread_mutex_lock(&_lock);
// get observation infos
NSMutableSet *infos = [_objectInfosMap objectForKey:object];
// lookup registered info instance
_FBKVOInfo *registeredInfo = [infos member:info];
if (nil != registeredInfo) {
[infos removeObject:registeredInfo];
// remove no longer used infos
if (0 == infos.count) {
[_objectInfosMap removeObjectForKey:object];
}
}
// unlock
pthread_mutex_unlock(&_lock);
// unobserve
[[_FBKVOSharedController sharedController] unobserve:object info:registeredInfo];
}
复制代码
一样是作了安全判断,并经过 pthread_mutex_lock 锁保证线程安全
二、
- (void)unobserve:(id)object info:(nullable _FBKVOInfo *)info
{
if (nil == info) {
return;
}
// 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;
}
复制代码
最后一步调用系统方法 removeObserver: 移除观察者,info->_state 设置为非观察状态
Demo 地址:GHLCrashGuard
小白出手,请多指教。如言有误,还望斧正!
转载请保留原文地址:gonghonglou.com/2019/07/07/…