KVO

一.原理
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)