iOS 深刻理解KVO实现 | 掘金技术征文

KVO的使用

要实现will/didChangeValueForKey:方法javascript

kvo的实例 实际在运行时被调用java

- (void)willChangeValueForKey:(NSString *)key;  
- (void)didChangeValueForKey:(NSString *)key;复制代码

触发数组

- (void)observeValueForKeyPath:(NSString *)keyPath  
                      ofObject:(id)object  
                        change:(NSDictionary *)change  
                       context:(void *)context;复制代码

所以要实现KVO,须要下列条件之一便可post

  1. 实现了KVC
  2. 有访问器方法(KVO后,会运行时重写setter方法,添加willChangeValueForKey,didChangeValueForKey)
  3. 显示调用will/didChangeValueForKey:

KVO实现原理

依赖于Runtimeui

  1. isa的改变
  2. 动态建立子类
  3. 替换重写setter方法
  4. 重写class方法

动态建立被观察对象的子类,重写setter方法,而且要管理一个对象的全部观察者.spa

  1. 动态建立子类
// 子类名
NSString *kvoclassName = [kXJYKVOPrefix stringByAppendingString:originalclassName];
Class class = NSClassFromString(kvoclassName);

if (class) {
return class;
}

// class doesn't exist yet, make it
Class originalclass = object_getClass(self);
Class kvoclass = objc_allocateClassPair(originalclass, kvoclassName.UTF8String, 0);

// 得到签名
Method classMethod = class_getInstanceMethod(originalclass, @selector(class));
const char *types = method_getTypeEncoding(classMethod);
// 替换setter 实现
class_addMethod(kvoClazz, @selector(class), (IMP)kvo_class, types);
objc_registerClassPair(kvoclass);

return kvoclass;复制代码
  1. 改变isa指针
    isa指针的做用: isa指针指向实例的类(对于这里的状况)
    实例经过isa指针找到类,能够获得方法列表,属性列表等信息
    当咱们将isa指针指向子类时,就能够调用子类的方法,使用子类的属性等。
    因而,调用该实例的setter方法实际上是调用了子类的setter方法指针

  2. 管理观察者
    因为一个对象可能被多个观察者观察,因此能够用关联对象的方法来管理全部的观察者。code

    XJYObservationInfo *info = [[XJYObservationInfo alloc]initWithObserver:self key:key block:block];
    // 维护改KVO观察者数组
    NSMutableArray *observers = objc_getAssociatedObject(self, (__bridge const void *)(kXJYKVOAssociatedObservers));
    if (!observers) {
    observers = [NSMutableArray array];
    objc_setAssociatedObject(self, (__bridge const void *)(kXJYKVOAssociatedObservers), observers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    [observers addObject:info];复制代码
  3. 重写class方法server

class_addMethod(kvoClazz, @selector(class), (IMP)kvo_class, types);
static Class kvo_class(id self, SEL _cmd)
{
return class_getSuperclass(object_getClass(self));
}复制代码

额外扩充

还记得Aspects中 对于KVO的特殊处理吗,KVO改变了实例对象的isa指针,在此处 Aspects对KVO过的实例进行了特殊的处理
Aspects:对象

// Probably a KVO'ed class. Swizzle in place. Also swizzle meta classes in place.
else if (statedClass != baseClass) {
return aspect_swizzleClassInPlace(baseClass);
}复制代码
object.class 因为KVO重写了class方法,因此不能准确的找到类 object_getClass()方法能够准确的找到isa指针 object.classobject_getClass(object)进行判断 来防止KVO致使的AOP无效复制代码

「掘金技术征文」
juejin.im/post/58d8e9…

相关文章
相关标签/搜索