iOS 底层学习21

前言

iOS 底层第21天的学习。在 iOS 底层第 20 天的学习中已经了解了 KVO的底层原理。
那既然都已经了解了底层的原理。那我们是不是可以自定义 KVO 呢?
今天就开始一步一步带你如何自定义KVO 以及分享一个开源的KVO框架

自定义 KVO

思路&步骤

  • 1️⃣ 申请 new class 开辟内存空间

要自定义 KVO 先要明白核心是什么,而 KVO 核心就是 isa-swizzingisa 指向了一个 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_addObserverobserver 给存起来。
  • 关联对象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 版

  • 要兼容多值,大致的思路就是定义一个 arraykeypath,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))
  • 定义一个 blockx_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 Observeremove Observe,Observe Value 都封装在_FBKVOSharedController

FBKVOController 小结

  • 你有没有觉得 FBKVOController 像不像一个处理 KVO 的中介,本来在 ViewController 做的处理,全部封装到了 FBKVOController 里。
  • 其实这里用到了一个叫:中介者模式 的设计思想
  • 中介者模式:用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其松耦合,而且可以独立地改变他们之间的交互
  • FBKVOController 理解

总结

  • 这次主要内容是根据 KVO 底层原理 分享了如何一步一步实现自定义 KVO 以及对 三方库 FBKVOController 的探索
  • 其实我觉得最主要的:是要多学习探索一些优秀的开源库,别人为何要这么设计?其背后的运用的一些思维方式我们是否可以进行整合,并把整合后的那个东西用在自己的项目中。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容