前言
iOS 底层第21
天的学习。在 iOS 底层第 20
天的学习中已经了解了 KVO
的底层原理。
那既然都已经了解了底层的原理。那我们是不是可以自定义 KVO
呢?
今天就开始一步一步带你如何自定义KVO
以及分享一个开源的KVO
框架
自定义 KVO
思路&步骤
- 1️⃣ 申请 new class 开辟内存空间
要自定义
KVO
先要明白核心是什么,而KVO
核心就是isa-swizzing
。isa
指向了一个new class
。我把这个new class
称之为打工类
。
- 2️⃣ 注册 new class
当
class
申请好了一片空间后。系统肯定不知道,所以我们还需要把class
注册到系统里
- 3️⃣ 开始添加方法
class
都已经创建好了。根据KVO
底层原理分析得知class
里 还有4
个重写的方法,开始向class
添加方法。
- 4️⃣ 方法的实现
对
class
内部方法的实现(setter,class,dealloc
)
- 5️⃣ isa 指向 new class
方法添加好了,最后把
isa -> new class
- 0️⃣ 验证是否存在属性 setter方法
其实在最开始还有一步就是对传参的验证,
KVO
底层原理中,已经验证过KVO
只能对属性
进行监听,无法对成员变量
进行监听。因此我们要在一开始对成员变量
进行check
代码👇
- 新建一个
NSObject+XKVO
类目,添加一个x_addObserver
方法
@implementation NSObject (XKVO)
- (void)x_addObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(nullable void *)context {
// 0: 验证是否存在setter方法 : 不让实例进来
}
- 开始第 0️⃣ 步 ,验证是否存在属性 setter方法
// 0: 验证是否存在setter方法 : 不让实例进来
[self judgeSetterMethodFromKeyPath:keyPath];
// 验证是否存在setter方法
- (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];
}
}
// 从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];
}
- 第 1️⃣ 步, 申请 new class 开辟内存空间
- (Class) createChildClassWithKeyPath:(NSString *) keyPath{
NSString *oldClassName = NSStringFromClass([self class]);
NSString *newClassName = [NSString stringWithFormat:@"%@%@",kXKKVOPrefix,oldClassName];
// 判断类是否存在
Class newClass = NSClassFromString(newClassName);
if (newClass) return newClass;
// 1.开辟内存,申请新类,
newClass = objc_allocateClassPair([self class], newClassName.UTF8String, 0);
}
- 第2️⃣ 步 , 注册 new class
- (Class) createChildClassWithKeyPath:(NSString *) keyPath{
// 1.开辟内存,申请新类, { ... }
// 2. 注册
objc_registerClassPair(newClass);
}
- 第 3️⃣ 步, 开始添加方法
- (Class) createChildClassWithKeyPath:(NSString *) keyPath{
// 1.开辟内存,申请新类 { ... }
// 2. 注册 { ... }
// 3. 添加方法
// setter
SEL setterSel = NSSelectorFromString(setterForGetter(keyPath));
Method setterMethod = class_getInstanceMethod([self class], setterSel);
const char *type = method_getTypeEncoding(setterMethod);
class_addMethod(newClass, setterSel, (IMP)xk_setter, type);
// class
SEL classSel = NSSelectorFromString(@"class");
Method classMethod = class_getClassMethod([self class], classSel);
const char *classType = method_getTypeEncoding(classMethod);
class_addMethod(newClass, classSel, (IMP)xk_class, classType);
}
- 第 4️⃣ 步, 方法的实现
// 重写的imp -
static void xk_setter(id self,SEL _cmd,id newValue){
NSLog(@"xk_setter 来了:%@",newValue);
// 核心,在下面会实现
}
Class xk_class(id self,SEL _cmd){
Class cls = object_getClass(self); // 获取当前对象 isa 指向的类
return class_getSuperclass(cls); // 获取 cls 的父类
}
- 第5️⃣ 步, isa 指向 new class
- (void)x_addObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(nullable void *)context {
// 0: 验证是否存在setter方法 : 不让实例进来
[self judgeSetterMethodFromKeyPath:keyPath];
// 封装动态生成子类 步骤1,2,3
Class newClass = [self createChildClassWithKeyPath:keyPath];
// 5. isa -> new class
object_setClass(self,newClass);
}
- 小结,在👆已经把自定义
KVO
的大致的步骤用代码的方式给梳理一下。但是在 第 4️⃣ 步对xk_setter
并未实现。在 KVO 原理 已知在调用KVO_类 setter
时,会对其父类发送消息调用其父类的setter
方法。 - 实现
setter
方法
static void xk_setter(id self,SEL _cmd,id newValue){
NSLog(@"xk_setter 来了:%@",newValue);
// 向父类发送消息 objc_msgSendSuper
struct objc_super x_super;
Class superClass = class_getSuperclass(object_getClass(self));
x_super.receiver = self;
x_super.super_class = superClass;
// objc_msgSendSuper()
void (*lg_msgSendSuper)(void *,SEL , id) = (void *)objc_msgSendSuper;
lg_msgSendSuper(&x_super,_cmd,newValue);
// 通知 - 观察者
NSString *keyPath = getterForSetter(NSStringFromSelector(_cmd)); // 根据 cmd 去获取
// 观察者要从哪里获取?
NSObject *observer = [NSObject new];
[observer x_observeValueForKeyPath:keyPath ofObject:self change:@{} context:NULL];
}
// 添加 x_observeValueForKeyPath 观察
- (void)x_observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context {
}
// 从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];
}
- 这时碰到一个问题就是
observer
是在x_addObserver
里传进来的,在setter
方法里 无法获取到。因此需要在x_addObserver
把observer
给存起来。 - 用
关联对象
把observer
存起来,代码👇
- (void)x_addObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(nullable void *)context {
// ....
// 保存观察者
objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kKVOAssiociateKey),observer,OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
static void xk_setter(id self,SEL _cmd,id newValue){
// ....
// 通知 - 观察者
NSString *keyPath = getterForSetter(NSStringFromSelector(_cmd)); // 根据 cmd 去获取
// 读取 observer
NSObject *observer = objc_getAssociatedObject(self,(__bridge const void * _Nonnull)(kKVOAssiociateKey));
[observer x_observeValueForKeyPath:keyPath ofObject:self change:@{keyPath:newValue} context:NULL];
}
- 自定义 kVO 的一个基本的雏形已经
ok
了 把程序运行起来看看结果👇
你是否会有疑问,如果要添加
多个值
要如何改进呢?
自定义 KVO 1.0 版
- 要兼容多值,大致的思路就是定义一个
array
把keypath,observe
等等信息存储起来,再用关联对象
保存。
- (void)x_addObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(XKKeyValueObservingOptions)options
context:(nullable void *)context {
// 0: 验证是否存在setter方法 : 不让实例进来
[self judgeSetterMethodFromKeyPath:keyPath];
// -------- 保存观察者 start --------
XKVOInfo *info = [[XKVOInfo alloc] initWitObserver:observer forKeyPath:keyPath options:options];
NSMutableArray *observeArray = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kKVOAssiociateKey));
if(!observeArray) {
observeArray = [NSMutableArray arrayWithCapacity:1];
}else {
// 判断 keypath 是否有同名
for (XKVOInfo *info in observeArray) {
if ([info.keyPath isEqualToString:keyPath]) {
NSLog(@"同名了 keyPath = %@",keyPath);
return;
}
}
}
[observeArray addObject:info];
objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kKVOAssiociateKey),observeArray,OBJC_ASSOCIATION_RETAIN_NONATOMIC);
// -------- 保存观察者 end --------
// 封装动态生成子类 步骤1,2,3,4
Class newClass = [self createChildClassWithKeyPath:keyPath];
// 5. isa -> new class
object_setClass(self,newClass);
}
- 这里说明下,为了兼容
多值
的监听,加了一个keyPath
同名判断,如果同名就return
- 对
createChildClassWithKeyPath
改进,前面已经对keyPath
进行了同名判断, 当类存在时,这里还需要对新的setter 方法
进行添加。不然就无法实现对多值的监听
.
- (Class) createChildClassWithKeyPath:(NSString *) keyPath{
NSString *oldClassName = NSStringFromClass([self class]);
NSString *newClassName = [NSString stringWithFormat:@"%@%@",kXKKVOPrefix,oldClassName];
// 判断类是否存在
Class newClass = NSClassFromString(newClassName);
if (newClass) { // 已经存在类
// 添加 setter 方法
SEL setterSel = NSSelectorFromString(setterForGetter(keyPath));
Method setterMethod = class_getInstanceMethod([self class], setterSel);
const char *type = method_getTypeEncoding(setterMethod);
class_addMethod(newClass, setterSel, (IMP)xk_setter, type);
return newClass;
}
}
- 对
xk_setter
改进
static void xk_setter(id self,SEL _cmd,id newValue){
// ....
// 读取 observer
// 1: 拿到观察者
NSMutableArray *observerArr = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kKVOAssiociateKey));
id oldValue = [self valueForKey:keyPath];
for (XKVOInfo *info in observerArr) {
if ([info.keyPath isEqualToString:keyPath]) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSMutableDictionary<NSKeyValueChangeKey,id> *change = [NSMutableDictionary dictionaryWithCapacity:1];
// 对新旧值进行处理
if (info.options & XKKeyValueObservingOptionNew) {
[change setObject:newValue forKey:NSKeyValueChangeNewKey];
}
if (info.options & XKKeyValueObservingOptionOld) {
[change setObject:@"" forKey:NSKeyValueChangeOldKey];
if (oldValue) {
[change setObject:oldValue forKey:NSKeyValueChangeOldKey];
}
}
// 2: 调用观察者
[info.observer x_observeValueForKeyPath:keyPath ofObject:self change:change context:NULL];
});
}
}
}
- 程序运行👇
- 最后我们把
x_removeObserver
也给实现一下,1.0 版本
算是顺利完成了。
- (void)x_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath {
NSMutableArray *observerArr = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kKVOAssiociateKey));
if (observerArr.count<=0) {
Class superClass = [self class]; // 获取分类
object_setClass(self, superClass); // 指回给父类
return;
}
// remove 一个 observer 的 keyPath
// 就从数据集合里也移除一个
for (XKVOInfo *info in observerArr) {
if ([info.keyPath isEqualToString:keyPath]) {
[observerArr removeObject:info];
objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kKVOAssiociateKey), observerArr, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
break;
}
}
if (observerArr.count<=0) {
Class superClass = [self class]; // 获取分类
object_setClass(self, superClass); // 指回给父类
}
}
自定义 KVO 2.0 版
函数式编程
- 函数式编程可以理解为:把
函数
当成一个参数
传入到方法里 ,举个🌰y = f(x)
=>y = f(f(x))
- 定义一个
block
对x_addObserver
进行优化代码👇
typedef void(^XKVOBlock)(id observer,
NSString *keyPath,
NSDictionary<NSKeyValueChangeKey, id> *change);
@interface XKVOInfo : NSObject
- (instancetype)initWitObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(XKKeyValueObservingOptions)options
handleBlock:(XKVOBlock) block;
@end
// 开始进行调用
@implementation NSObject (XKVO)
- (void)x_addObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(XKKeyValueObservingOptions)options
context:(nullable void *)context
handleBlock:(XKVOBlock) block{
// 0: 验证是否存在setter方法 : 不让实例进来
[self judgeSetterMethodFromKeyPath:keyPath];
// 保存观察者
XKVOInfo *info = [[XKVOInfo alloc] initWitObserver:observer forKeyPath:keyPath options:options handleBlock:block];
NSMutableArray *observeArray = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kKVOAssiociateKey));
if(!observeArray) {
observeArray = [NSMutableArray arrayWithCapacity:1];
}else {
// 判断 keypath 是否有同名
for (XKVOInfo *info in observeArray) {
if ([info.keyPath isEqualToString:keyPath]) {
NSLog(@"同名了 keyPath = %@",keyPath);
return;
}
}
}
[observeArray addObject:info];
objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kKVOAssiociateKey),observeArray,OBJC_ASSOCIATION_RETAIN_NONATOMIC);
// .....
}
// 回调
static void xk_setter(id self,SEL _cmd,id newValue){
// ...
// 1: 拿到观察者
NSMutableArray *observerArr = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kKVOAssiociateKey));
id oldValue = [self valueForKey:keyPath];
for (XKVOInfo *info in observerArr) {
if ([info.keyPath isEqualToString:keyPath]) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSMutableDictionary<NSKeyValueChangeKey,id> *change = [NSMutableDictionary dictionaryWithCapacity:1];
// 对新旧值进行处理
if (info.options & XKKeyValueObservingOptionNew) {
[change setObject:newValue forKey:NSKeyValueChangeNewKey];
}
if (info.options & XKKeyValueObservingOptionOld) {
[change setObject:@"" forKey:NSKeyValueChangeOldKey];
if (oldValue) {
[change setObject:oldValue forKey:NSKeyValueChangeOldKey];
}
}
// 2: block 回调
if(info.handleBlock) {
info.handleBlock(info.observer,keyPath,change);
}
});
}
}
}
自定义 KVO 3.0 版
自动销毁机制
- 在上面我们在
dealloc
主动调用了x_removeObserver
进行了释放 - 那如何才能实现自动调用了呢?
方法1: method 替换
// 方法1: 方法的替换
- (Class) createChildClassWithKeyPath:(NSString *) keyPath{
// ...
Method m1 = class_getInstanceMethod([self class],
NSSelectorFromString(@"dealloc"));
Method m2 = class_getInstanceMethod([self class], @selector(xk_dealloc));
method_exchangeImplementations(m1, m2);
return newClass;
}
- (void)xk_dealloc{
NSLog(@"%s",__func__);
}
// 父类实现 dealloc
@implementation XKStudent
- (void) dealloc {
NSLog(@"%s",__func__);
}
@end
- 根据👆的代码,发现了一个问题就是如果
XKStudent
不实现dealloc
就会报错,很显然这个方法不行。再说了,子类的事情就让子类自己去干,为何要涉及到父类呢?
方法2: 重写 dealloc
- (Class)createChildClassWithKeyPath:(NSString *)keyPath{
// ...
// 方法2: 添加 xk_dealloc,对 dealloc 进行重写
SEL deallocSEL = NSSelectorFromString(@"dealloc");
Method deallocMethod = class_getInstanceMethod([self class], deallocSEL);
const char *deallocTypes = method_getTypeEncoding(deallocMethod);
class_addMethod(newClass, deallocSEL, (IMP)xk_dealloc, deallocTypes);
}
static void xk_dealloc(id self,SEL _cmd) {
NSLog(@"xk_dealloc");
Class superClass = [self class];
object_setClass(self, superClass);
NSString *keyPath = getterForSetter(NSStringFromSelector(_cmd));
[self _removeObservers]; // 移除所有观察
}
- 重写
dealloc
运行结果👇
第三方 FBKVOController 探索
使用
- (void)viewDidLoad {
[super viewDidLoad];
// ....
[self.kvoCtrl observe:self.person keyPath:@"age" options:(NSKeyValueObservingOptionNew) action:@selector(_observerAge)];
[self.kvoCtrl observe:self.person keyPath:@"name" options:(NSKeyValueObservingOptionNew) block:^(id _Nullable observer, id _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
NSLog(@"****%@****",change);
}];
}
#pragma mark - lazy
- (FBKVOController *)kvoCtrl{
if (!_kvoCtrl) {
_kvoCtrl = [FBKVOController controllerWithObserver:self];
}
return _kvoCtrl;
}
源码分析
- 进入
[FBKVOController controllerWithObserver:self]
+ (instancetype)controllerWithObserver:(nullable id)observer
{
return [[self alloc] initWithObserver:observer];
}
- (instancetype)initWithObserver:(nullable id)observer retainObserved:(BOOL)retainObserved
{
self = [super init];
if (nil != self) {
_observer = observer;
NSPointerFunctionsOptions keyOptions = retainObserved ? NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPointerPersonality : NSPointerFunctionsWeakMemory|NSPointerFunctionsObjectPointerPersonality;
// 创建一张表,保存 kvo_info
_objectInfosMap = [[NSMapTable alloc] initWithKeyOptions:keyOptions valueOptions:NSPointerFunctionsStrongMemory|NSPointerFunctionsObjectPersonality capacity:0];
pthread_mutex_init(&_lock, NULL);
}
return self;
}
- 进入
- (void)observe:
- (void)observe:(nullable id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options action:(SEL)action
{
if (nil == object || 0 == keyPath.length || NULL == action) {
return;
}
// create info -> 创建一个 kvo info 信息
_FBKVOInfo *info = [[_FBKVOInfo alloc] initWithController:self keyPath:keyPath options:options action:action];
// observe object with info
[self _observe:object info:info];
}
- 进入
[self _observe:object info:info];
疑问1:这里的
_observe:object
观察的object
是那个对象?
- (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 这是一个单例
[[_FBKVOSharedController sharedController] observe:object info:info];
}
- 进入
_FBKVOSharedController
@implementation _FBKVOSharedController
{
NSHashTable<_FBKVOInfo *> *_infos;
pthread_mutex_t _mutex;
}
+ (instancetype)sharedController
{
static _FBKVOSharedController *_controller = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_controller = [[_FBKVOSharedController alloc] init];
});
return _controller;
}
- 由👆可知,
_FBKVOSharedController
是一个单例
疑问2:为什么
_FBKVOSharedController
是一个单例呢?
- 继续探索进入
- (void)observe:
- (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];
}
- 👆代码调用了系统
API:[object addObserver:self]
开始解释疑问1:
_observe:object
观察的object
是那个对象?
- 这里
object
? ,我们看一下最开始调用的是什么👇
// 开始调用
[self.kvoCtrl observe:self.person keyPath:@"name" options:(NSKeyValueObservingOptionNew) block:^(id _Nullable observer, id _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
NSLog(@"****%@****",change);
}];
// object = self.person
- (void)observe:(nullable id)object keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(FBKVONotificationBlock)block
{
// ...
// observe object with info
[self _observe:object info:info];
}
这里
object
=self.person
因此在单例
_FBKVOSharedController
在调用observe
方法 实现的是self.person
添加了一个Observer
继续往下探索,在单例
_FBKVOSharedController
里又找到observeValueForKeyPath
方法
- (void)observeValueForKeyPath:(nullable NSString *)keyPath
ofObject:(nullable id)object
change:(nullable NSDictionary<NSString *, id> *)change
context:(nullable void *)context{
_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);
}
// 核心代码
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];
}
}
}
}
- 👆 代码主要是对
custom block or action
进行了相关的调用
开始解释疑问2:为什么
_FBKVOSharedController
是一个单例呢?
- 先来看一下👇代码
- (FBKVOController *)kvoCtrl{
if (!_kvoCtrl) {
_kvoCtrl = [FBKVOController controllerWithObserver:self];
}
return _kvoCtrl;
}
-
FBKVOController
创建了一个实例对象_kvoCtrl
并未创建单例,主要是为了能够重复利用FBKVOController
,不用的时候能够释放内存,节省内存使用空间。而_FBKVOSharedController
使用了单例我的理解就是把KVO
里重复使用的代码
封装在一个单例类里,下次可以更容易的调用。 - 这里还需要注意的一点就是
[FBKVOController controllerWithObserver:self]
这里self
一定是 一个weak
,为了防止循环引用无法释放内存。代码👇
/**
The observer notified on key-value change. Specified on initialization.
*/
@property (nullable, nonatomic, weak, readonly) id observer;
- 继续探索:已知
FBKVOController
可以进行重复利用 ,我们看一下FBKVOController
是如何进行dealloc
的
- (void)dealloc
{
[self unobserveAll];
pthread_mutex_destroy(&_lock);
}
// 进入 unobserveAll
- (void)unobserveAll
{
[self _unobserveAll];
}
// 进入 _unobserveAll
- (void)_unobserveAll
{
// lock
pthread_mutex_lock(&_lock);
NSMapTable *objectInfoMaps = [_objectInfosMap copy];
// clear table and map
[_objectInfosMap removeAllObjects];
// unlock
pthread_mutex_unlock(&_lock);
// 单例 shareController
_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];
}
}
- 在
FBKVOController
进行dealloc
时,移除观察也非常方便,利用了单例类_FBKVOSharedController
。把与KVO
相关重复代码add Observe
,remove Observe
,Observe Value
都封装在_FBKVOSharedController
FBKVOController 小结
- 你有没有觉得
FBKVOController
像不像一个处理KVO
的中介,本来在ViewController
做的处理,全部封装到了FBKVOController
里。 - 其实这里用到了一个叫:
中介者模式
的设计思想 - 中介者模式:用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其松耦合,而且可以独立地改变他们之间的交互
- 对
FBKVOController
理解
总结
- 这次主要内容是根据
KVO
底层原理 分享了如何一步一步实现自定义KVO
以及对 三方库FBKVOController
的探索 - 其实我觉得最主要的:是要多学习探索一些优秀的开源库,别人为何要这么设计?其背后的运用的一些思维方式我们是否可以进行
整合
,并把整合后的那个东西用在自己的项目中。