KVO底层实现,使用runtime自定义KVO
2018-11-12 本文已影响57人
追寻那一米阳光
KVO底层原理
1.当一个object有观察者时,动态创建这个object的类的子类,子类命名为NSKVONotifying_ClassName
2.对于每个被观察的property在子类NSKVONotifying_ClassName
中重写其seter
方法
3.在重写的set方法中调用- willChangeValueForKey:
和- didChangeValueForKey:
通知观察者
4.当一个property没有观察者时,删除重写的方法
5.当没有observer观察任何一个property时,删除动态创建的子类
注意:
使用KVO时会生成一个被观察类的子类,但是这个类是被隐藏掉的,使用普通的class方法无法获取.因为子类里边重写了- class
方法,返回正常的类.但是在控制台里边使用po object_getClass(id (对象名称))
打印,打印结果为:NSKVONotifying_ClassName
自定义KVO实现
- 创建NSObject的分类
- 创建一个子类、注册
//创建一个子类
NSString *oldName = NSStringFromClass([self class]);
NSString *newName = [NSString stringWithFormat:@"KVO_%@",oldName];
//添加类
Class myClass = objc_allocateClassPair([self class], newName.UTF8String, 0);
//注册类
objc_registerClassPair(myClass);
- 重写set方法,给myClass添加setName方法
NSString *selector_name = [NSString stringWithFormat:@"set%@:",[keyPath capitalizedString]];
SEL method = NSSelectorFromString(selector_name);
class_addMethod(myClass, method, (IMP)setName, "v@:@");
OC中每个方法都有两个默认参数 id self
方法调用者 与SEL _cmd
调用方法的标号
class_addMethod
中"v@:@"
Type Encodings的解释参考文档
- 重写
seter
方法
void setName(id self,SEL _cmd,NSString *newName){
struct objc_super superClass = {self,class_getSuperclass([self class])};
//修改name属性
objc_msgSendSuper(&superClass,_cmd,newName);
//取出全局属性block
void(^block)(id,NSDictionary *) = objc_getAssociatedObject(self, @"block");
if (block) {
block([self superclass],@{@"key":newName});
}
//[self superclass]为了满足隐藏子类的操作
}
思路就是通过runtime
动态创建子类,给子类重写父类的seter
方法,在重写的seter
方法中添加监听机制,我这里使用的是block
回调,因为分类不能添加属性,所以要使用全局属性的时候只能通过runtime
添加属性。
代码地址:代码地址