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

KVO的使用

要实现will/didChangeValueForKey:方法

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

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

触发

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

因此要实现KVO,需要下列条件之一即可

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

KVO实现原理

依赖于Runtime

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

动态创建被观察对象的子类,重写setter方法,并且要管理一个对象的所有观察者.

  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. 管理观察者
    由于一个对象可能被多个观察者观察,所以可以用关联对象的方法来管理所有的观察者。

    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方法

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…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值