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>:===> key
static 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_LGPerson
object_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的指向是LGPerson
SEL 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 : 添加setter
SEL 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_LGPerson
KVO回调:{
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_LGPerson
object_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:调用block
if ([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 : 添加dealloc
SEL 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 = YES
NSPointerFunctionsOptions 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
{
// 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];
}
FBKVOController
中持有NSMapTable
,使用NSMapTable
代替关联对象object
作为key
,获取NSMutableSet
NSMutableSet
中存储_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 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];
}
}
传入的
object
为实例对象,调用系统KVO
的注册观察者,开始对属性监听传入
addObserver
方法的self
为当前单例的中介者中介者中持有
NSHashTable
,以弱引用的方式存储_FBKVOInfo
通过
_FBKVOInfo
的_state
成员变量,根据枚举值_FBKVOInfoStateInitial
、_FBKVOInfoStateObserving
、_FBKVOInfoStateNotObserving
决定新增或删除KVO
4.2.5 中介者-实现回调
- (void)observeValueForKeyPath:(nullable NSString *)keyPath
ofObject:(nullable id)object
change:(nullable NSDictionary<NSString *, id> *)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<NSString *, id> *changeWithKeyPath = change;
// add the keyPath to the change dictionary for clarity when mulitple keyPaths are being observed
if (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
{
// 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];
}
}
当
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 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;
}
}
调用系统
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
的NSPointerFunctionsOptions
NSDictionary
的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;
}