1. 自定义KVO
仿照KVO的实现方式,自定义NSObject的分类LGKVO
1.1 准备工作
定义全局静态常量:
static NSString *const kLGKVOPrefix = @"LGKVONotifying_";static NSString *const kLGKVOAssiociateKey = @"kLGKVO_AssiociateKey";
实现setterForGetter方法:
#pragma mark - 从get方法获取set方法的名称 key ===>>> setKey:static NSString *setterForGetter(NSString *getter){if (getter.length <= 0) { return nil;}NSString *firstString = [[getter substringToIndex:1] uppercaseString];NSString *leaveString = [getter substringFromIndex:1];return [NSString stringWithFormat:@"set%@%@:",firstString,leaveString];}
实现getterForSetter方法:
#pragma mark - 从set方法获取getter方法的名称 set<Key>:===> keystatic NSString *getterForSetter(NSString *setter){if (setter.length <= 0 || ![setter hasPrefix:@"set"] || ![setter hasSuffix:@":"]) { return nil;}NSRange range = NSMakeRange(3, setter.length-4);NSString *getter = [setter substringWithRange:range];NSString *firstString = [[getter substringToIndex:1] lowercaseString];return [getter stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:firstString];}
1.2 注册观察者
实现lg_addObserver方法:
- (void)lg_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(LGKeyValueObservingOptions)options context:(nullable void *)context{// 1: 验证是否存在setter方法 : 不让实例进来[self judgeSetterMethodFromKeyPath:keyPath];// 2: 动态生成子类Class newClass = [self createChildClassWithKeyPath:keyPath];// 3: isa的指向 : LGKVONotifying_LGPersonobject_setClass(self, newClass);// 4: 保存观察者信息[self lg_addInfoWithObserver:observer forKeyPath:keyPath options:options];}
【第一步】验证被监听者为属性,必须存在
setter方法【第二步】动态生成子类
【第三步】修改
isa指向【第四步】保存观察者信息
1.2.1 验证被监听者
实现judgeSetterMethodFromKeyPath方法:
- (void)judgeSetterMethodFromKeyPath:(NSString *)keyPath{Class superClass = object_getClass(self);SEL setterSeletor = NSSelectorFromString(setterForGetter(keyPath));Method setterMethod = class_getInstanceMethod(superClass, setterSeletor);if (!setterMethod) {@throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"当前%@不存在setter方法",keyPath] userInfo:nil];}}
1.2.2 动态生成子类
实现createChildClassWithKeyPath方法:
- (Class)createChildClassWithKeyPath:(NSString *)keyPath{NSString *oldClassName = NSStringFromClass([self class]);NSString *newClassName = [NSString stringWithFormat:@"%@%@",kLGKVOPrefix,oldClassName];Class newClass = NSClassFromString(newClassName);// 防止重复创建生成新类if (newClass) return newClass;/*** 如果内存不存在,创建生成* 参数一: 父类* 参数二: 新类的名字* 参数三: 新类的开辟的额外空间*/// 2.1 : 申请类newClass = objc_allocateClassPair([self class], newClassName.UTF8String, 0);// 2.2 : 注册类objc_registerClassPair(newClass);// 2.3.1 : 添加class : class的指向是LGPersonSEL classSEL = NSSelectorFromString(@"class");Method classMethod = class_getInstanceMethod([self class], classSEL);const char *classTypes = method_getTypeEncoding(classMethod);class_addMethod(newClass, classSEL, (IMP)lg_class, classTypes);// 2.3.2 : 添加setterSEL setterSEL = NSSelectorFromString(setterForGetter(keyPath));Method setterMethod = class_getInstanceMethod([self class], setterSEL);const char *setterTypes = method_getTypeEncoding(setterMethod);class_addMethod(newClass, setterSEL, (IMP)lg_setter, setterTypes);return newClass;}
【第一步】验证动态子类是否已经创建
已创建,直接返回
未创建,进入【第二步】
【第二步】创建子类
申请类,需要父类、新类的名称、开辟的额外空间
注册类
添加方法,需要
class、setter方法
1.2.3 保存观察者信息
创建LGKVOInfo,用来存储观察者信息
#import <Foundation/Foundation.h>typedef NS_OPTIONS(NSUInteger, LGKeyValueObservingOptions) {LGKeyValueObservingOptionNew = 0x01,LGKeyValueObservingOptionOld = 0x02,};@interface LGKVOInfo : NSObject@property (nonatomic, weak) NSObject *observer;@property (nonatomic, copy) NSString *keyPath;@property (nonatomic, assign) LGKeyValueObservingOptions options;- (instancetype)initWitObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(LGKeyValueObservingOptions)options;@end@implementation LGKVOInfo- (instancetype)initWitObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(LGKeyValueObservingOptions)options{self = [super init];if (self) {self.observer = observer;self.keyPath = keyPath;self.options = options;}return self;}@end
实现lg_addInfoWithObserver方法:
-(void)lg_addInfoWithObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(LGKeyValueObservingOptions)options {LGKVOInfo *info = [[LGKVOInfo alloc] initWitObserver:observer forKeyPath:keyPath options:options];NSMutableArray *observerArr = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kLGKVOAssiociateKey));if (!observerArr) {observerArr = [NSMutableArray arrayWithCapacity:1];[observerArr addObject:info];objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kLGKVOAssiociateKey), observerArr, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}}
1.3 实现子类方法
动态生成子类时,添加的class和setter方法,它们的IMP分别指向lg_class和lg_setter函数地址
1.3.1 lg_class
实现lg_class函数:
Class lg_class(id self,SEL _cmd){return class_getSuperclass(object_getClass(self));}
使用中间类重写后的class方法,获取的还是原始类对象,仿佛KVO所做的一切都不存在
1.3.2 lg_setter
实现lg_setter函数:
static void lg_setter(id self,SEL _cmd,id newValue){// 1:消息转发:转发给父类,改变父类的值NSString *keyPath = getterForSetter(NSStringFromSelector(_cmd));id oldValue = [self valueForKey:keyPath];void (*lg_msgSendSuper)(void *,SEL , id) = (void *)objc_msgSendSuper;struct objc_super superStruct = {.receiver = self,.super_class = class_getSuperclass(object_getClass(self)),};lg_msgSendSuper(&superStruct,_cmd,newValue);// 2:获取观察者NSMutableArray *observerArr = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kLGKVOAssiociateKey));for (LGKVOInfo *info in observerArr) {if ([info.keyPath isEqualToString:keyPath]) {dispatch_async(dispatch_get_global_queue(0, 0), ^{NSMutableDictionary<NSKeyValueChangeKey,id> *change = [NSMutableDictionary dictionaryWithCapacity:1];// 3:对新、旧值进行处理if (info.options & LGKeyValueObservingOptionNew) {[change setObject:newValue forKey:NSKeyValueChangeNewKey];}if (info.options & LGKeyValueObservingOptionOld) {if (oldValue) {[change setObject:oldValue forKey:NSKeyValueChangeOldKey];}else{[change setObject:@"" forKey:NSKeyValueChangeOldKey];}}// 4:发送消息给观察者SEL observerSEL = @selector(lg_observeValueForKeyPath:ofObject:change:context:);((void (*) (id, SEL, NSString *, id, NSDictionary *, void *)) objc_msgSend)(info.observer,observerSEL,keyPath,self,change,NULL);});}}}
【第一步】消息转发:转发给父类,改变父类的值
【第二步】获取观察者
【第三步】对新、旧值进行处理
【第四步】发送消息给观察者
1.4 移除观察者
实现lg_removeObserver方法:
- (void)lg_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath{NSMutableArray *observerArr = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kLGKVOAssiociateKey));if (observerArr.count<=0) {return;}for (LGKVOInfo *info in observerArr) {if ([info.keyPath isEqualToString:keyPath]) {[observerArr removeObject:info];objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kLGKVOAssiociateKey), observerArr, OBJC_ASSOCIATION_RETAIN_NONATOMIC);break;}}if (observerArr.count<=0) {// 指回给父类Class superClass = [self class];object_setClass(self, superClass);}}
【第一步】移除观察者
【第二步】
isa指回原始类对象
1.5 自定义KVO的使用
- (void)viewDidLoad {[super viewDidLoad];self.person = [[LGPerson alloc] init];NSLog(@"注册观察者之前:%s",object_getClassName(self.person));[self.person lg_addObserver:self forKeyPath:@"nickName" options:(LGKeyValueObservingOptionNew|LGKeyValueObservingOptionOld) context:NULL];NSLog(@"注册观察者之后:%s",object_getClassName(self.person));}- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{self.person.nickName = @"KC";}- (void)lg_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{NSLog(@"KVO回调:%@",change);}- (void)dealloc{NSLog(@"移除观察者之前:%s",object_getClassName(self.person));[self.person lg_removeObserver:self forKeyPath:@"nickName"];NSLog(@"移除观察者之后:%s",object_getClassName(self.person));}-------------------------//输出以下内容:注册观察者之前:LGPerson注册观察者之后:LGKVONotifying_LGPersonKVO回调:{new = KC;old = "";}移除观察者之前:LGKVONotifying_LGPerson移除观察者之后:LGPerson
2. 函数式KVO
函数式编程是一种编程方式,把运算过程写成一系列嵌套的函数调用。函数编程支持函数作为第一类对象,有时称为闭包或者仿函数(functor)对象
函数式编程的优点:
代码简洁,开发快速
- 函数式编程大量使用函数,减少了代码的重复,因此程序比较短,开发速度较快
接近自然语言,易于理解
- 函数式编程的自由度很高,可以写出很接近自然语言的代码
更方便的代码管理
- 函数式编程不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果必定相同。因此,每一个函数都可以被看做独立单元,很有利于进行单元测试(
unit testing)和除错(debugging),以及模块化组合
- 函数式编程不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果必定相同。因此,每一个函数都可以被看做独立单元,很有利于进行单元测试(
易于并发编程
- 函数式编程不需要考虑死锁(
deadlock),因为它不修改变量,所以根本不存在锁线程的问题。不必担心一个线程的数据,被另一个线程修改,所以可以很放心地把工作分摊到多个线程,部署并发编程(concurrency)
- 函数式编程不需要考虑死锁(
2.1 注册观察者
定义LGKVOBlock:
typedef void(^LGKVOBlock)(id observer,NSString *keyPath,id oldValue,id newValue);
修改lg_addObserver方法,将block作为方法参数
- (void)lg_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath block:(LGKVOBlock)block{// 1: 验证是否存在setter方法 : 不让实例进来[self judgeSetterMethodFromKeyPath:keyPath];// 2: 动态生成子类Class newClass = [self createChildClassWithKeyPath:keyPath];// 3: isa的指向 : LGKVONotifying_LGPersonobject_setClass(self, newClass);// 4: 保存观察者信息[self lg_addInfoWithObserver:observer forKeyPath:keyPath block:block];}
2.1.1 保存观察者信息
修改LGKVOInfo,增加block属性,初始化方法增加block参数
#import <Foundation/Foundation.h>typedef NS_OPTIONS(NSUInteger, LGKeyValueObservingOptions) {LGKeyValueObservingOptionNew = 0x01,LGKeyValueObservingOptionOld = 0x02,};@interface LGKVOInfo : NSObject@property (nonatomic, weak) NSObject *observer;@property (nonatomic, copy) NSString *keyPath;@property (nonatomic, copy) LGKVOBlock handleBlock;- (instancetype)initWitObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath handleBlock:(LGKVOBlock)block;@end@implementation LGKVOInfo- (instancetype)initWitObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath handleBlock:(LGKVOBlock)block{if (self=[super init]) {_observer = observer;_keyPath = keyPath;_handleBlock = block;}return self;}@end
修改lg_addInfoWithObserver方法,将block作为方法参数
-(void)lg_addInfoWithObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath block:(LGKVOBlock)block {LGKVOInfo *info = [[LGKVOInfo alloc] initWitObserver:observer forKeyPath:keyPath handleBlock:block];NSMutableArray *mArray = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kLGKVOAssiociateKey));if (!mArray) {mArray = [NSMutableArray arrayWithCapacity:1];objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kLGKVOAssiociateKey), mArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}[mArray addObject:info];}
2.2 实现子类方法
修改子类的setter方法,使用函数式编程,传入的block为回调方法。所以在setter方法中,无需调用KVO回调方法,改为调用info中保存的block即可
2.2.1 lg_setter
修改lg_setter函数,将调用KVO回调方法,改为调用info中保存的block
static void lg_setter(id self,SEL _cmd,id newValue){// 1:消息转发:转发给父类,改变父类的值NSString *keyPath = getterForSetter(NSStringFromSelector(_cmd));id oldValue = [self valueForKey:keyPath];void (*lg_msgSendSuper)(void *,SEL , id) = (void *)objc_msgSendSuper;struct objc_super superStruct = {.receiver = self,.super_class = class_getSuperclass(object_getClass(self)),};lg_msgSendSuper(&superStruct,_cmd,newValue);// 2:获取观察者NSMutableArray *mArray = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kLGKVOAssiociateKey));for (LGKVOInfo *info in mArray) {// 3:调用blockif ([info.keyPath isEqualToString:keyPath] && info.handleBlock) {info.handleBlock(info.observer, keyPath, oldValue, newValue);}}}
2.3 函数式KVO的使用
- (void)viewDidLoad {[super viewDidLoad];self.person = [[LGPerson alloc] init];NSLog(@"注册观察者之前:%s",object_getClassName(self.person));[self.person lg_addObserver:self forKeyPath:@"nickName" block:^(id _Nonnull observer, NSString * _Nonnull keyPath, id _Nonnull oldValue, id _Nonnull newValue) {NSLog(@"回调方法:%@ - %@",oldValue,newValue);}];NSLog(@"注册观察者之后:%s",object_getClassName(self.person));}- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{self.person.nickName = @"KC";}- (void)dealloc{NSLog(@"移除观察者之前:%s",object_getClassName(self.person));[self.person lg_removeObserver:self forKeyPath:@"nickName"];NSLog(@"移除观察者之后:%s",object_getClassName(self.person));}-------------------------//输出以下内容:注册观察者之前:LGPerson注册观察者之后:LGKVONotifying_LGPerson回调方法:(null) - KC移除观察者之前:LGKVONotifying_LGPerson移除观察者之后:LGPerson
3. 自动销毁机制
使用KVO必须手动移除观察者,我们是否可以重写子类的dealloc方法,在其销毁的时候,自动移除观察者
3.1 动态生成子类
修改createChildClassWithKeyPath方法,添加dealloc方法
- (Class)createChildClassWithKeyPath:(NSString *)keyPath{...// 2.3.3 : 添加deallocSEL deallocSEL = NSSelectorFromString(@"dealloc");Method deallocMethod = class_getInstanceMethod([self class], deallocSEL);const char *deallocTypes = method_getTypeEncoding(deallocMethod);class_addMethod(newClass, deallocSEL, (IMP)lg_dealloc, deallocTypes);return newClass;}
3.2 实现子类方法
动态生成子类时,添加的dealloc方法,IMP指向lg_dealloc函数地址
3.2.1 lg_dealloc
实现lg_dealloc函数:
static void lg_dealloc(id self,SEL _cmd){NSLog(@"自动销毁之前:%s",object_getClassName(self));Class superClass = [self class];object_setClass(self, superClass);NSLog(@"自动销毁之后:%s",object_getClassName(self));}
3.3 自动销毁机制的使用
- (void)dealloc{// LGViewController中的dealloc什么都不处理// 依赖子类中的lg_dealloc自动销毁}-------------------------//输出以下内容:注册观察者之前:LGPerson注册观察者之后:LGKVONotifying_LGPerson回调方法:(null) - KC自动销毁之前:LGKVONotifying_LGPerson自动销毁之后:LGPerson
4. FBKVOController
上述案例中,自定义的KVO代码并不完善,目的只是阐述KVO的底层原理,列举KVO使用中的一些痛点
更完善的自定义KVO,已经被 Facebook实现并开源:
采用中介者设计模式,对常用的
KVO机制进行了额外的一层封装使用数组形式,可同时对
model中多个属性进行监听提供
action和block两种方式进行回调,避免KVO的相关代码四处散落提供自动销毁机制,无需在
dealloc方法中移除观察者
4.1 基本使用
4.1.1 使用block回调
#import "ViewController.h"#import <FBKVOController.h>#import "LGPerson.h"@interface ViewController ()@property (nonatomic, strong) FBKVOController *kvoCtrl;@property (nonatomic, strong) LGPerson *person;@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];self.person = [[LGPerson alloc] init];[self.kvoCtrl observe:self.person keyPath:@"name" options:(NSKeyValueObservingOptionNew) block:^(id _Nullable observer, id _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {NSLog(@"****%@****",change);}];}- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{self.person.name = @"KC";}- (FBKVOController *)kvoCtrl{if (!_kvoCtrl) {_kvoCtrl = [FBKVOController controllerWithObserver:self];}return _kvoCtrl;}@end
4.1.2 使用action回调
[self.kvoCtrl observe:self.person keyPath:@"age" options:NSKeyValueObservingOptionNew action:@selector(lg_observerAge)];
4.1.3 对可变数组的监听
[self.kvoCtrl observe:self.person keyPath:@"mArray" options:(NSKeyValueObservingOptionNew) block:^(id _Nullable observer, id _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {NSLog(@"****%@****",change);}];
4.2 源码解析
4.2.1 FBKVOController的初始化
- (instancetype)initWithObserver:(nullable id)observer retainObserved:(BOOL)retainObserved{self = [super init];if (nil != self) {//一般情况下 observer 会持有 FBKVOController 为了避免循环引用,此处的_observer 的内存管理语义是弱引用_observer = observer;//定义 NSMapTable key的内存管理策略,在默认情况,传入的参数 retainObserved = YESNSPointerFunctionsOptions keyOptions = retainObserved ? NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPointerPersonality : NSPointerFunctionsWeakMemory|NSPointerFunctionsObjectPointerPersonality;//创建 NSMapTable key 为 id 类型,value 为 NSMutableSet 类型_objectInfosMap = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPersonality capacity:0];//初始化互斥锁,避免多线程间的数据竞争pthread_mutex_init(&_lock, NULL);}return self;}
4.2.2 注册观察者
- (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类,存储观察者信息,包含:FBKVOController、keyPath、options、block添加
observe和info的关联
4.2.3 observe和info的关联
- (void)_observe:(id)object info:(_FBKVOInfo *)info{// lockpthread_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 returnpthread_mutex_unlock(&_lock);return;}// lazilly create set of infosif (nil == infos) {infos = [NSMutableSet set];[_objectInfosMap setObject:infos forKey:object];}// add info and oberve[infos addObject:info];// unlock prior to calloutpthread_mutex_unlock(&_lock);[[_FBKVOSharedController sharedController] observe:object info:info];}
FBKVOController中持有NSMapTable,使用NSMapTable代替关联对象object作为key,获取NSMutableSetNSMutableSet中存储_FBKVOInfo如果
infos不存在,进行创建,并添加到NSMapTable中将
info对象添加到NSMutableSet类型的infos中_FBKVOSharedController为单例模式的中介者,负责真正的KVO操作
4.2.4 中介者-注册系统KVO
- (void)observe:(id)object info:(nullable _FBKVOInfo *)info{if (nil == info) {return;}// register infopthread_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];}}
传入的
object为实例对象,调用系统KVO的注册观察者,开始对属性监听传入
addObserver方法的self为当前单例的中介者中介者中持有
NSHashTable,以弱引用的方式存储_FBKVOInfo通过
_FBKVOInfo的_state成员变量,根据枚举值_FBKVOInfoStateInitial、_FBKVOInfoStateObserving、_FBKVOInfoStateNotObserving决定新增或删除KVO
4.2.5 中介者-实现回调
- (void)observeValueForKeyPath:(nullable NSString *)keyPathofObject:(nullable id)objectchange:(nullable NSDictionary<NSString *, id> *)changecontext:(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 existspthread_mutex_lock(&_mutex);info = [_infos member:(__bridge id)context];pthread_mutex_unlock(&_mutex);}if (nil != info) {// take strong reference to controllerFBKVOController *controller = info->_controller;if (nil != controller) {// take strong reference to observerid observer = controller.observer;if (nil != observer) {// dispatch custom block or action, fall back to default actionif (info->_block) {NSDictionary<NSString *, id> *changeWithKeyPath = change;// add the keyPath to the change dictionary for clarity when mulitple keyPaths are being observedif (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];}}}}}
通过
info获取FBKVOController从
FBKVOController中,获取observer,也就是真正的VC通过
info获取block并调用,传入必要的参数
4.2.6 FBKVOController的销毁
- (void)dealloc{[self unobserveAll];pthread_mutex_destroy(&_lock);}- (void)unobserveAll{[self _unobserveAll];}- (void)_unobserveAll{// lockpthread_mutex_lock(&_lock);NSMapTable *objectInfoMaps = [_objectInfosMap copy];// clear table and map[_objectInfosMap removeAllObjects];// unlockpthread_mutex_unlock(&_lock);_FBKVOSharedController *shareController = [_FBKVOSharedController sharedController];for (id object in objectInfoMaps) {// unobserve each registered object and infosNSSet *infos = [objectInfoMaps objectForKey:object];[shareController unobserve:object infos:infos];}}
当
VC销毁时,控制器中初始化的FBKVOController也会跟随销毁FBKVOController的dealloc方法,先拷贝自身持有的NSMapTable到临时表,然后将其清空,再遍历临时表,从单例的中介者中,取消实例对象和infos的关联
4.2.7 中介者-移除系统KVO的监听
- (void)unobserve:(id)object infos:(nullable NSSet<_FBKVOInfo *> *)infos{if (0 == infos.count) {return;}// unregister infopthread_mutex_lock(&_mutex);for (_FBKVOInfo *info in infos) {[_infos removeObject:info];}pthread_mutex_unlock(&_mutex);// remove observerfor (_FBKVOInfo *info in infos) {if (info->_state == _FBKVOInfoStateObserving) {[object removeObserver:self forKeyPath:info->_keyPath context:(void *)info];}info->_state = _FBKVOInfoStateNotObserving;}}
调用系统
KVO的removeObserver方法,移除观察者销毁流程:
VC销毁→FBKVOController销毁→调用中介者的unobserve方法→移除观察者
4.3 扩展
4.3.1 NSSet与NSHashTable
NSSet是排重后的object集合,NSHashTable是NSSet的容器,并且只有可变版本,允许对添加到容器中的对象是弱引用的持有关系,当NSHashTable中的对象销毁时,该对象也会从容器中移除
4.3.2 NSDictionary与NSMapTable
NSMapTable与NSDictionary类似,唯一区别是多一个功能,可设置key和value的NSPointerFunctionsOptionsNSDictionary的key策略固定是copy,考虑到开销问题,一般使用简单的数字或字符串作为key。但如果碰到使用object作为key的场景,使用NSMapTable更为适宜NSMapTable可通过NSFunctionsPointer来分别定义对key和value的内存管理策略,简单可以分为strong、weak以及copy
4.3.3 NSSet的member方法
在FBKVOController中,注册观察者方法,每次都创建_FBKVOInfo类型的临时变量info
将info传入关联方法中,使用NSSet的member方法,判断info是否存在
这里有个疑问,既然info是每次都创建的临时变量,每次的地址一定是不同的,使用NSSet的member方法,是根据什么来判断info是否存在的?
NSSet的member方法:确定给定的对象是否存在于集合中,如果存在则返回该对象
- (ObjectType)member:(ObjectType)object;
- 将检查集合中的每个元素是否与
object相等,直到找到匹配项或到达集合的末尾。如果isEqual:返回YES,则认为对象是相等的
也就是说,NSSet的member方法,依赖于object对象的isEqual:方法,如果该方法返回YES,则认为对象是相等的
找到_FBKVOInfo的isEqual方法:
- (BOOL)isEqual:(id)object{if (nil == object) {return NO;}if (self == object) {return YES;}if (![object isKindOfClass:[self class]]) {return NO;}return [_keyPath isEqualToString:((_FBKVOInfo *)object)->_keyPath];}
- 最终判断两个对象的
keyPath是否相同,所以即使info的地址不一样,也有可能被判断为相同对象
5. GNUstep Base
5.1 addObserver

- setup:开始准备阶段
- replacementForClass:生成动态子类

- 保存
info

- 多级
keyPath的处理
5.1.1 setup

- 开始准备阶段
5.1.2 replacementForClass

- 通过
c查找r,存在直接返回 - 不存在,调用
GSKVOReplacement的initWithClass初始化r,添加到表中
5.1.3 GSKVOReplacement的initWithClass方法

GSObjCMakeClass:动态生成子类GSObjCAddClasses:注册到内存GSObjCAddClassBehavior:添加方法并重写
5.2 setter

- 判断
automaticallyNotifiesObserversForKey方法的返回 - 返回
YES- 调用
willChangeValueForKey - 调用
imp - 调用
didChangeValueForKey
- 调用
- 返回
NO- 调用
imp
- 调用
5.2.1 didChangeValueForKey

notifyForKey:发送通知
5.2.2 notifyForKey

- 让观察者触发相应,触发
KVO的observeValueForKeyPath回调
5.3 removeObserver

- 通过哈希表找到
pathInfo pathInfo不为空,获取到observer- 开始移除观察者
对于isa的处理,重写GSKVOBase的dealloc方法
- (void) dealloc{// Turn off KVO for self ... then call the real dealloc implementation.[self setObservationInfo: nil];object_setClass(self, [self class]);[self dealloc];GSNOSUPERDEALLOC;}
