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,需要下列条件之一即可
- 实现了KVC
- 有访问器方法(KVO后,会运行时重写setter方法,添加willChangeValueForKey,didChangeValueForKey)
- 显示调用will/didChangeValueForKey:
KVO实现原理
依赖于Runtime
- isa的改变
- 动态创建子类
- 替换重写setter方法
- 重写class方法
动态创建被观察对象的子类,重写setter方法,并且要管理一个对象的所有观察者.
- 动态创建子类
// 子类名
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;复制代码
改变isa指针
isa指针的作用: isa指针指向实例的类(对于这里的情况)
实例通过isa指针找到类,可以得到方法列表,属性列表等信息
当我们将isa指针指向子类时,就可以调用子类的方法,使用子类的属性等。
于是,调用该实例的setter方法其实是调用了子类的setter方法。管理观察者
由于一个对象可能被多个观察者观察,所以可以用关联对象的方法来管理所有的观察者。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];复制代码
重写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.class 与 object_getClass(object)进行判断 来防止KVO导致的AOP无效复制代码
「掘金技术征文」
juejin.im/post/58d8e9…