KVO的实现其实就是分2步骤
1 让被监听的对象isa指向中间类
2 在中间类中重写setter方法 添加监听方法
首先
左边是我们很容易理解 每个对象都有一个Class
右边是在对象添加KVO之后 runtime的一些实现
具体代码实现
为了方便理解
下面有这样一个需求
Person 类 下面有个对象 xiaoming(小明) 监听小明改名字
1 创建一个中间类
// 参数 1是类名 2是被复制的类名 3是额外补充的内存空间
Class Person_copy = objc_allocateClassPair(Person,@"Person_copy".UTF8String,0);
// 复制class 方法
Class kvoClass = objc_allocateClassPair(originClass, kvoClassName.UTF8String, 0);
Method classMethod = class_getClassMethod(Person, @selector(class));
const char *types = method_getTypeEncoding(classMethod);
class_addMethod(Person_copy, @selector(class), (IMP)kvo_class, types);
objc_registerClassPair(Person_copy);
static Class kvo_class(id self, SEL _cmd) {
return class_getSuperclass(object_getClass(self));
}
这样我们就拥有了一个Person_copy的中间类
2 为中间类添加一个setter 方法
我们知道当 xiaoming.name = @"xx"
的时候, 会想上寻找MethodList中是否有setter方法,现在我们还没有改变isa的指向。先为中间类添加这样一个setter方法。
看图上 setter方法分为2部分
第一部分 消息转发到原本的Class 处理
第二部分 监听回调
这边我一直觉得用objc_msgSend 发现不对,很尴尬。最后发现msgSend只能发发送instance ,这时候只能用下面的SendSuper
static void kvo_setter(id self, SEL _cmd, id newValue) {
// 1 看注释 创建第一个参数 struct objc_super
// 这时候xiaoming的isa 还是指向 Person的
struct objc_super superClass = {
.receiver = xiaoming,
.super_class = class_getSuperclass(object_getClass(xiaoming))
}
// 这里有点注意一下直接使用 objc_msgSendSuper的时候 会产生 too many arg的警告
//网上有2种解决办法 1 改设置 2创建一个中转变量让编译器懵逼(我们使用这一种)
void (*objc_msgSendSuperCopy)(void *, SEL , id) = (void *)
objc_msgSendSuper;
objc_msgSendSuperCasted(&superClass,_cmd,newValue);
//**********上面 就是把消息转发给Person了******下面再添加监听的方法
//监听的方法 可以使用Block 将回调Block 使用关联到 实例变量上
//这里就自由发挥了
}
3 这是我们中间类 算是真正的创建好了 怎么让属性赋值的时候(对象调用setter 方法的时候) 去调用我们写好的中间类呢
//这里只要改变对象的isa 指向就可以了
object_setClass(xiaoming, Person_copy);
这样差不多了
这里再补充一下监听回调的方法
//1使用属性关联 将处理Block 绑定到实例变量上
objc_setAssociatedObject(xiaoming, (__bridge const void *)(key), @“这里是处理Block”, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//2 在Person_copy的setter方法调用BLOCK回调
//取出
id 回调 = objc_getAssociatedObject(self, (__bridge const void *)key);
执行回调
一开始觉得KVO实现很麻烦,理解之后觉得挺简单。算是做个笔记吧。具体代码,以后再传吧。。。。。GG