语言描述
KVO 的实质. ---- 官方文档上面对于 KVO 实现调用的描述,也称作,调用了 runtime 的isa swizzling
.实质上是将类进行过了替换.
假设我们定义了一个Person
类,包含有name
这个属性
@interface Person:NSObject
@property (nonatomic,copy) NSString *name;
@end
在ViewController
中添加观察者,观察name
值的变化.
系统会动态的创建一个Person
的子类 NSKVONotifying_Person
. 并重写name
的setter
方法
[self willChangeValueForKey:@"name"]; // 调用 observer 告知改变旧值
[self setName:name];
[self didChangeValueForKey:@"name"]; // 调用 observer 告知改变成新值
重写之后,用这个类去替换原先的Person
类.这样,在 Person
调用 person.name = @"someName";
时,KVO 操作对应的内容就会被调用.
Hank 公开课笔记中的内容
关于自定义 KVO
- 首先,要动态创建一个
Person
类的子类.
-(void)FF_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
/*
1.动态添加一个类
*/
NSString * oldClassName = NSStringFromClass([self class]);
NSString * newClassName = [@"MyKVO_" stringByAppendingString:oldClassName];
const char * newName = [newClassName UTF8String];
//定义一个类
Class subClass = objc_allocateClassPair([self class], newName, 0); // 动态创建一个类,继承于[self Class],类名为 newName
//注册这个类
objc_registerClassPair(subClass);
//改变isa指针!! 让方法调用者,调用到当前的自定义方法.(可以看做原先 isa 指向 person 类,现在让他指向 subClass)
object_setClass(self, subClass);
//重写setAge方法!!
/*
说明, 我们平日里,在子类中调用方法,(假设 A有一个方法 methodA, a 是 A 的子类, a 是可以去调用 methodA),实质上是先在 SEL 列表中查找这个 methodA 方法,如果找不到, 继续去寻找他的父类 A 的 SEL 中是否包含有这个方法.找到后去调用.
因此,实际上子类本身是不包含 `setAge`这个方法的.如果直接调用,那么实质上去调用父类的这个方法,这不是我们想要的.因为这里我们需要去重写这个`setAge`的方法.
*/
class_addMethod(subClass, @selector(setAge:), (IMP)setAge, "v@:@");
}
//有默认参数!! RAC
void setAge(id self,SEL _cmd,int age){
NSLog(@"进入");
id class = [self class];
//让自己指向父类
object_setClass(self, class_getSuperclass([self class]));
objc_msgSend(self, @selector(setAge:),age);
object_setClass(self, class);
}