iOS底层探索—–自定义KVO

前言

这是我参与8月更文挑战的第16天,活动详情查看:8月更文挑战

自定义 KVO

准备工作

上篇文章,我们对 KVO 的底层进行了详细的分析,那么这篇文章,就仿照 KVO 的实现,进行自定义。

因为在底层中,KVO 是以 NSObject+NSKeyValueObserving 分类的形式展示出来的,那么我们自定义,也先创建 NSObject+LGKVO 的分类。

NSObject+LGKVO.h中:

- (void)lg_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(nullable void *)context;

- (void)lg_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
复制代码

NSObject+LGKVO.m中:

首先定义两个全局静态常量:

static NSString *const kLGKVOPrefix = @"LGKVONotifying_";
static NSString *const kLGKVOAssiociateKey = @"kLGKVO_AssiociateKey";
复制代码

再导入#import <objc/message.h>头文件。

然后再分别把 gettersetter 方法给定义好,方便后面直接运行调试:

  • setter 方法
#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];
}
复制代码
  • getter方法
#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];
}
复制代码

注册观察者

接着就在NSObject+LGKVO.m中实现注册观察者方法,

思路是:

  • 1: 验证是否存在 setter 方法 : 不让实例进来
  • 2: 动态生成子类
  • 3: isa 的指向 : LGKVONotifying_LGPerson
  • 4: 保存观察者

代码如下:

- (void)lg_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)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: 保存观察者
    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);
    }
}
复制代码

验证是否存在 setter 方法

#pragma mark - 验证是否存在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];
    }
}
复制代码

动态生成子类

思路:

  • ①、验证动态子类是否已经创建
    • 如果是已创建了,就直接返回
    • 如果是未创建,就进入②
  • ②、创建子类
    • 先申请类,需要父类、新类的名称、开辟新空间
    • 再注册类
    • 最后添加方法,需要 classsetter 方法
#pragma mark - 动态生成子类
- (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;
}
复制代码

保存观察者

这步需要新建一个 LGKVOInfo 继承于 NSObject,用来保存观察者信息,将多个LGKVOInfo实例添加到数组中,再通过关联对象保存到self中,进行相关的操作。
详细代码如下:

LGKVOInfo.h 中:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

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
NS_ASSUME_NONNULL_END
复制代码

LGKVOInfo.m 中:

#import "LGKVOInfo.h"

@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
复制代码

实现子类方法

在动态生成子类时,会重写classsetter方法,它们的IMP分别指向lg_classlg_setter函数地址。通过关联对象保存到self中,这个就和上篇在分析 KVO 时那样,使用中间类重写后的class方法,获取的还是原始类对象,就好像KVO所做的一切都不存在似的。

  • lg_class
Class lg_class(id self,SEL _cmd){
    return class_getSuperclass(object_getClass(self));
}
复制代码
  • lg_setter

思路:

  • ①、消息转发:转发给父类,改变父类的值
  • ②、获取观察者
  • ③、对新、旧值进行处理
  • ④、发送消息给观察者
static void lg_setter(id self,SEL _cmd,id newValue){
    NSLog(@"来了:%@",newValue);

    // 1: 消息转发 : 转发给父类
    // 改变父类的值 --- 可以强制类型转换
    NSString *keyPath = getterForSetter(NSStringFromSelector(_cmd));
    id oldValue       = [self valueForKey:keyPath];
    
    void (*lg_msgSendSuper)(void *,SEL , id) = (void *)objc_msgSendSuper;

    // void /* struct objc_super *super, SEL op, ... */
    struct objc_super superStruct = {
        .receiver = self,
        .super_class = class_getSuperclass(object_getClass(self)),
    };

    //objc_msgSendSuper(&superStruct,_cmd,newValue)
    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) {
                    [change setObject:@"" forKey:NSKeyValueChangeOldKey];
                    if (oldValue) {
                        [change setObject:oldValue forKey:NSKeyValueChangeOldKey];
                    }
                }

                // 4: 消息发送给观察者
                SEL observerSEL = @selector(lg_observeValueForKeyPath:ofObject:change:context:);
                objc_msgSend(info.observer,observerSEL,keyPath,self,change,NULL);
            });
        }
    }
}
复制代码

移除观察者

思路:

  • 移除观察者;
  • isa指回原始类对象
- (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);
    }
}
复制代码

那么到了这里,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";
}

#pragma mark - KVO回调
- (void)lg_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{

    NSLog(@"%@",change);
}

- (void)dealloc{

    NSLog(@"移除观察者之前:%s",object_getClassName(self.person));
    [self.person lg_removeObserver:self forKeyPath:@"nickName"];
    NSLog(@"end");
    NSLog(@"移除观察者之后:%s",object_getClassName(self.person));
}
复制代码

看下打印结果:

FCA23DC0-6354-4020-B8F4-2D26D40A9FBD.png

自定义 KVO — 函数式

函数式编程是把运算过程写成一系列嵌套的函数,然后进行调用。函数式编程支持函数作为第一类对象,也可以被称为闭包或者仿函数(functor)对象。

函数式编程的优点:

  • ①、代码简洁,开发速度快:函数式编程大量使用函数,减少了代码的重复,因此程序比较短,开发速度较快;

  • ②、接近编程自然语言,易于理解:函数式编程的自由度很高,可以写出很接近自然语言的代码;

  • ③、代码管理更方便:函数式编程不依赖外界的状态,也不会改变外界的状态,只要给与指定的参数,那么返回的结果必定是相同的。因此,每一个函数都可以被看做独立单元,十分有利于单元测试(unit testing)和 bug 修改(debugging),也十分便于模块化组合;

  • ④、易于并发编程:函数式编程不需要考虑死锁(deadlock),因为它不修改变量,所以根本不存在锁线程的问题。不必担心一个线程的数据,被另一个线程修改,所以可以很放心地把工作分摊到多个线程,部署并发编程(concurrency)。

那么自定义 KVO ,为什么要使用函数式编程了?

上文通过lg_observeValueForKeyPath:ofObject:change:context:这种调用方式很不方便。所以,我们引入函数式思想,通过block来实现监听的回调。接下来我们就进行优化。

LGKVOInfo.h中,添加 LGKVOBlock

@interface LGLKVOInfo : NSObject 

@property (nonatomic, weak) NSObject *observer;
@property (nonatomic, copy) NSString *keyPath;
@property (nonatomic, copy) LGKVOBlock handleBlock; 
@end 

@implementation LGLKVOInfo 
- (instancetype)initWitObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath handleBlock:(LGKVOBlock)block { 
    if (self=[super init]) { 
        _observer = observer;
        _keyPath = keyPath; 
        _handleBlock = block; 
    } 
    return self; 
}
@end
复制代码

NSObject+LGKVO.h中,lg_addObserver 方法也添加 block

typedef void(^LGKVOBlock)(id observer,NSString *keyPath,id oldValue,id newValue); 

@interface NSObject (LGKVO) 

- (void)lg_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath block:(LGKVOBlock)block; 

- (void)lg_removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath; 
@end
复制代码

NSObject+LGKVO.m中,lg_addObserver 方法调整:

- (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: 保存信息
    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];
}
复制代码

NSObject+SSLKVO.m中,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);
    }
}
复制代码

NSObject+LGKVO.m中,lg_setter 方法调整,无需调用KVO回调方法,改为调用info中保存的block即可:

static void lg_setter(id self,SEL _cmd,id newValue){

    NSLog(@"来了:%@",newValue);
    NSString *keyPath = getterForSetter(NSStringFromSelector(_cmd));
    id oldValue = [self valueForKey:keyPath];

    // 消息转发 : 转发给父类
    // 改变父类的值 --- 可以强制类型转换
    void (*lg_msgSendSuper)(void *,SEL , id) = (void *)objc_msgSendSuper;

    // void /* struct objc_super *super, SEL op, ... */
    struct objc_super superStruct = {
        .receiver = self,
        .super_class = class_getSuperclass(object_getClass(self)),
    };
    //objc_msgSendSuper(&superStruct,_cmd,newValue)
    lg_msgSendSuper(&superStruct,_cmd,newValue);

    // 信息数据回调
    NSMutableArray *mArray = objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kLGKVOAssiociateKey));

    for (LGKVOInfo *info in mArray) {
        if ([info.keyPath isEqualToString:keyPath] && info.handleBlock) {
            info.handleBlock(info.observer, keyPath, oldValue, newValue);
        }
    }
}
复制代码

优化后了,再在控制器里面调用,就会简洁很多,代码如下:

- (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)); 
}
复制代码

运行后打印结果:
E31BFB8C-B1B7-48D2-B817-3128C545133B.png

自动销毁机制

上篇文章中,我们分析了,在使用KVO之后,必须手动移除观察者,有时候可能会把这个环节给忽略掉,那么我们是否可以重写子类的dealloc方法,在需要其销毁的时候,自动移除观察者了。

  • 动态生成子类:首先在 createChildClassWithKeyPath 方法里面添加 dealloc
- (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);

    // 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;
}
复制代码
  • 动态生成子类时,添加的dealloc方法,IMP指向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)); 
    
}
复制代码
  • 自动销毁机制的使用,直接在控制器里面调用 dealloc 方法:
- (void)dealloc{
    NSLog(@"已经销毁了");
}
复制代码

运行打印结果:

91F7978C-23EE-4B69-95DB-B4D7CB17FBA4.png

FBKVOController 探索

FBKVOController的基本使用

FBKVOControllerFacebook开源的一个基于系统KVO实现的框架。支持Objective-CSwift语言。
在gitHub 的下载地址

FBKVOController 的优点:

  • 不需要手动移除观察者;框架自动帮我们移除观察者
  • 函数式编程,可以一行代码实现系统KVO的三个步骤。
  • 实现 KVO 与事件发生处的代码上下文相同,不需要跨方法传参数;
  • 增加了blockSEL自定义操作对NSKeyValueObserving回调的处理支持,提升使用 KVO 的体验;
  • 每一个 keyPath 会对应一个block或者SEL,不需要在 block 中使用 if 判断 keyPath,也就是说,不需要统一的 observeValueForKeyPath 方法里写 if 判断;
  • 可以同时对一个对象的多个属性进行监听,写法简洁。
  • 线程安全。

我们先看看FBKVOController的使用,代码如下:

#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 = @"cooci";
} 

- (FBKVOController *)kvoCtrl{ 
    if (!_kvoCtrl) { 
        _kvoCtrl = [FBKVOController controllerWithObserver:self]; 
    } 
    return _kvoCtrl;
} 

@end
复制代码
  • 这里是使用的 block 回调:
[self.kvoCtrl observe:self.person keyPath:@"name" options:(NSKeyValueObservingOptionNew) block:^(id _Nullable observer, id _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) { 
        NSLog(@"****%@****",change); 
}]; 
复制代码

除了 block 方式调用,还可以使用其他的方式实现:

  • 使用action回调:
[self.kvoCtrl observe:self.person keyPath:@"age" options:NSKeyValueObservingOptionNew action:@selector(lg_observerAge)];
复制代码
  • 对可变数组的监听
[self.kvoCtrl observe:self.person keyPath:@"mArray" options:(NSKeyValueObservingOptionNew) block:^(id _Nullable observer, id _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) { 
    NSLog(@"****%@****",change); 
}];
复制代码

FBKVOController源码解析

初始化

7D2F4551-A118-49FF-9D70-38A3DF514EC4.png

注册观察者

  • _FBKVOInfo类中,存储了观察者信息,包含:FBKVOControllerkeyPathoptionsblock等;
  • observeinfo进行关联;

58926420-8447-4011-98F9-06A7FFAF959A.png

observeinfo的关联详情

  • FBKVOController初始化的时候,就持有NSMapTable,使用NSMapTable代替关联对象;
  • 然后把object作为key,获取NSMutableSet类型的 value,同时,NSMutableSet中存储_FBKVOInfo
  • 如果infos为空,进行创建,并添加到NSMapTable中;
  • infos不为空之后,将info对象添加到NSMutableSet类型的infos中;
  • 中间者_FBKVOSharedController是单例模式,负责真正的KVO操作;

BF7021B9-D207-48C1-B1DB-7F16849A5EAA.png

中间者_FBKVOSharedController分析

  • 注册系统KVO
    • 传入的object为实例对象,调用系统KVO的注册观察者,开始对属性监听;
    • 传入addObserver方法的self为当前单例的中介者;
    • 中间者持有NSHashTable,以弱引用的方式存储_FBKVOInfo
    • 通过_FBKVOInfo_state成员变量,根据枚举值_FBKVOInfoStateInitial_FBKVOInfoStateObserving_FBKVOInfoStateNotObserving决定新增或删除KVO

07D08882-904A-4EBD-8235-CD87F6EB0A57.png

  • 实现回调
    • 通过info获取FBKVOController
    • FBKVOController中,获取observer,也就是真正的VC
    • 通过info获取block并调用,传入必要的参数;

ABEB90CD-56BE-45C8-A3FA-3DA0E30CEB34.png

  • 销毁

1952C365-8445-485B-B5DC-895ECF453FB3.png

AA793E9A-D82B-4D0E-85EE-8FCCDA1F378F.png

拷贝自身持有的NSMapTable到临时表,然后将其清空,再遍历临时表,从单例的中介者中,取消实例对象和infos的关联

3E2245D1-1CB2-47EF-A46B-F2F077EA1DEC.png

  • 移除系统KVO的监听
    • 调用系统KVOremoveObserver方法,移除观察者
    • 销毁流程:VC销毁 –> FBKVOController销毁 –> 中间者的unobserve方法 –> 移除观察者

DF102A4A-C926-457F-8E9A-170D998C1622.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享