KVO

作者: LeeDev | 来源:发表于2018-09-17 21:42 被阅读9次

    kvo :
    1).观察这模式的一种体现 ;
    2).然后原理是,通过isa 混写技术 实现了 把 原实例对象的isa (比如指向OriginClass) 指向了新的子类(NSKVONotifing_OriginClass)
    3).通过 重写 setValue:方法 通过在 内部 实现willChargeForValue:(就是监听的老值)didChargeForValue: (监听新值)

    1. 普通的 kvo监听

    实现 selftest 对象的监听

    - (void)kvoTest {
        
        Test * test = [Test  new];
        [test addObserver:self forKeyPath:@"value" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
        
        test.value = 2;
    }
    
    
    - (void)observeValueForKeyPath:(NSString *)keyPath
                          ofObject:(id)object
                            change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                           context:(void *)context {
        
        NSLog(@"keyPath = %@;change = %@",keyPath,change);
    }
    
    被监听的对象的isa 指针 ,已经指向了 NSKVONotifing_Test

    2. 第三方的库KVOController

    - (void)fbKVO {
        
        
        Test * test = [Test  new];
    
        self.kvo = [[FBKVOController alloc]initWithObserver:self];
    
        [self.kvo observe:test keyPath:@"value" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary<NSString *,id> * _Nonnull change) {
            
            NSLog(@"observer:%@",observer);
            NSLog(@"object = %@",object);
            NSLog(@"change = %@",change);
        }];
        
        
        test.value = 2;
    
    }
    

    解析fbkvo

    FBKVO 其实是封装了系统的KVO;
    主要有 被观察者 对象 object , 观察者的信息_FBKVOInfo;

    FBKVOInfo:
       属性1:FBKVOController  => 封装了一个 观察者(弱引用)
       属性2:_keyPath (观察者的keyPath)
       属性3:NSKeyValueObservingOptions (新旧值)
       属性4:FBKVONotificationBlock (回调值 (id observer, id object, NSDictionary *change))
    

    添加 监听 是通过 单例 [_FBKVOSharedController sharedController] 把我们的 objectFBKVOInfo 添加进来[object addObserver:self forKeyPath:info->_keyPath options:info->_options context:(void *)info];

    - (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, 单例 作为系统的观察者, 把info 传入context,后面监听值的时候 可以方便的取值
      [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];
      }
    }
    

    单例[_FBKVOSharedController sharedController] 获取监听

    - (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];
            }
          }
        }
      }
    }
    

    3. 自定义KVO

    主要是 在监听某个object的时候,去让 object的 isa 指针 指向 子类,并且在子类中 添加 当前的 keyPathsetter 方法。
    这样外界 调用了这个setter方法,就会先进入 子类setter 方法,这样我们就可以 自定义操作了。

    #import "NSObject+KVOBlock.h"
    #import <objc/message.h>
    
    static char * __KVOListKey = "__KVOListKey";
    
    
    @implementation NSObject (KVOBlock)
    
    
    - (void)observerkeyPath:(NSString *)keyPath
                      block:(KVOBlock)block {
        
        [self registerSubClassWithKeyPath:keyPath];
        if (block && keyPath) {
            self.map[keyPath] = [block copy];
        }
    }
    
    
    // 管理 keyPath 和 block 回调
    - (NSMutableDictionary<NSString * , id > *)map {
        NSMutableDictionary * dic = objc_getAssociatedObject(self, &__KVOListKey);
        if (dic == nil) {
            dic = [NSMutableDictionary dictionary];
            objc_setAssociatedObject(self, &__KVOListKey, dic, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        }
        return dic;
    }
    
    
    // 注册类对,就是子类
    - (void)registerSubClassWithKeyPath:(NSString *)keyPath {
        
        //找到源类中的 setterMethod
        SEL setterSelector  = NSSelectorFromString([self setterFromGetter:keyPath]);
        Method setterMethod = class_getInstanceMethod([self class], setterSelector);
        if (setterMethod == nil) {
            return;
        }
        
        // 注册 self的 isa 指针为 【self class】的子类 subClass
        NSString * subClassName = [NSString stringWithFormat:@"__KVONotifying__%@", NSStringFromClass([self class])];
        Class subClass = objc_getClass(subClassName.UTF8String);
        if (subClass == nil) {
            // 传入父类 ,子类的名称
            subClass = objc_allocateClassPair([self class], subClassName.UTF8String, 0);
        }
        objc_registerClassPair(subClass);
        
        //isa 指向 subClass
        object_setClass(self, subClass);
        
        //给 子类 subClass 添加setter方法,这样 只要外界调用setter方法 就好调用 子类的setter方法
        const char * types = method_getTypeEncoding(setterMethod);
        class_addMethod(subClass, setterSelector, (IMP)kvo_setter, types);
    }
    
    // 获取 setter 方法
    - (NSString *)setterFromGetter:(NSString *)keyPath {
        if (keyPath.length == 0) {
            return nil;
        }
        NSString *setter = [[[keyPath substringToIndex:1] uppercaseString] stringByAppendingString:[keyPath substringFromIndex:1]];
        setter = [NSString stringWithFormat:@"set%@:", setter];
        return setter;
    }
    
    // 获取getter方法
    - (NSString *)getterFromSetter:(NSString *)setter {
        
        if (setter.length == 0 && [setter hasPrefix:@"set"]) {
            return nil;
        }
        NSString * getter = [setter substringFromIndex:3];
        getter = [NSString stringWithFormat:@"%@%@",[getter substringToIndex:1].lowercaseString,[getter substringFromIndex:1]];
        if ([getter hasSuffix:@":"]) {
            getter = [getter substringToIndex:getter.length-1];
        }
        return getter;
    }
    
    
    // 监听setter 方法
    static void kvo_setter(id self, SEL _cmd, id newValue) {
        
        
        NSString *setterName = NSStringFromSelector(_cmd);
        NSString *getterName = [self getterFromSetter:setterName];
        if (getterName == nil) {
            return;
        }
        id oldValue = [self valueForKey:getterName];
    
        // https://tech.glowing.com/cn/implement-kvo/
        struct objc_super superclazz = {
            .receiver = self,
            .super_class = class_getSuperclass(object_getClass(self))
        };
        void (* objc_msgSendSuperCasted)(void *, SEL, id) = (void *)objc_msgSendSuper;
        // 当的【self class】 其实就是subClass, 所以它的superClass,就是源类
        objc_msgSendSuperCasted(&superclazz, _cmd, newValue);
        
        NSMutableDictionary *change = [NSMutableDictionary dictionary];
        change[@"keyPath"] = getterName;
        if (newValue) {
            change[NSKeyValueChangeNewKey] = newValue;
        }
        if (oldValue) {
            change[NSKeyValueChangeOldKey] = oldValue;
        }
        
        NSDictionary * dic = [self map];
        KVOBlock  block = dic[getterName];
        if (block) {
            block(change);
        }
    }
    
    
    
    
    @end
    
    

    例子

        Test * test = [Test  new];
    //    self.test = test;
        [test observerkeyPath:@"num" block:^(NSDictionary *change) {
            
            NSLog(@"num = %@",change);
        }];
        
        test.num = @(10);
    

    相关文章

      网友评论

          本文标题:KVO

          本文链接:https://www.haomeiwen.com/subject/jtexnftx.html