KVO

作者: 鄙人哈哈哈哈5871 | 来源:发表于2017-06-10 16:15 被阅读13次

    一.原理
    KVO是系统用运行时机制给需要观察的对象增加一个子类的方法实现的,如果一个对象被观察,系统会给他增加以各子类,重写它的set方法,从而实现观察对象属性变化的功能,为了使外部看不出来,系统还重写了被观察对象的-class,-isEqual:等方法。
    二.自定义KVO(仅实现key,未实现keyPath)
    1.这个方式是给要观察的类添加一个子类

    - (Class)achiveNewSubClass:(NSString *)className
              withCurrentClass:(Class)currentClass
    {
      Class newClass = objc_allocateClassPair([self class],
                                              className.UTF8String,
                                              0);
      
      // 可以在这里给类添加成员变量等
      objc_registerClassPair(newClass);
      return newClass;
    }
    

    2.这个是重写被观察对象的set方法

    void kvo_setter(id self, SEL selecter, id newValue) {
      
      KHHandle handle = objc_getAssociatedObject(self,
                                                 @"123".UTF8String);
      NSString * key = achiveKeyPath(selecter);
      
      
      struct objc_super superclazz = {
        .receiver = self,
        .super_class = class_getSuperclass(object_getClass(self))
      };
      
        id oldValue = [self valueForKey:key];
    
      void (* objc_msgSendToSuper)(struct objc_super * ,SEL , id) = (void *)objc_msgSendSuper;
      // 在这里调取运行时创建的子类的父类的set方法赋值
      objc_msgSendToSuper(&superclazz, selecter, newValue);
        
      NSDictionary * dict = @{KHKeyValueNew : newValue ?:  @"",
                              KHKeyValueOld : oldValue ?: @"",
                              };
      handle(dict);
    }
    

    3.这个是提供给外部调用的方法

    - (void)KH_addObserverWithKeyPath:(NSString *)keyPath
                            withBlock:(KHHandle)handle
    {
      Class currentClass = [self class];
      NSString * className = NSStringFromClass(currentClass);
      if ([className hasPrefix:CLASS_PREFIX]) return;
      className = [CLASS_PREFIX stringByAppendingString:className];
      Class subClass =NSClassFromString(className);
      if (subClass) return;
      objc_setAssociatedObject(self,
                               @"123".UTF8String,
                               handle,
                               OBJC_ASSOCIATION_COPY_NONATOMIC);
      subClass = [self achiveNewSubClass:className
                        withCurrentClass:currentClass];
      
      object_setClass(self, subClass);
      
      SEL selecter = NSSelectorFromString(achiveMethodName(keyPath));
      
      Method me = class_getInstanceMethod(currentClass, selecter);
      
      const char * types =  method_getTypeEncoding(me);
      
     BOOL isSuccess = class_addMethod(subClass, selecter, (IMP)kvo_setter, types);
      
      
      NSLog(@"%d",isSuccess);
    }
    

    相关文章

      网友评论

          本文标题:KVO

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