一、KVO基础操作
添加观察者
[_myClass addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
//属性名
NSLog(@"keyPath = %@",keyPath);
//观察属性的对象,self
NSLog(@"object = %@",object);
//kind : 1 (新值) new : 属性的新值
//NSKeyValueChangeSetting = 1,改变的操作
//NSKeyValueChangeInsertion = 2,插入的操作 数组
//NSKeyValueChangeRemoval = 3,移除的操作 数组
//NSKeyValueChangeReplacement = 4,代替的操作 数组
NSLog(@"change = %@",change);
NSLog(@"context = %@",context);
}
在dealloc中移除观察者
[_myClass removeObserver:self forKeyPath:@"name"];
`NSKeyValueObservingOptionNew`:把更改之后的值提供给处理方法
`NSKeyValueObservingOptionOld`:把更改之前的值提供给处理方法
`NSKeyValueObservingOptionInitial`:把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值。
`NSKeyValueObservingOptionPrior`:分2次调用。在值改变之前和值改变之后。
自动或者手动打开观察者
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{
//返回NO,手动来触发,否则自动触发。
return YES;
}
如果设置成手动触发,当要改变属性值的时候需要加入如下代码:
[_myClass willChangeValueForKey:@"name"];
[_myClass setValue:@"KVC" forKey:@"name"];
[_myClass didChangeValueForKey:@"name"];
二、底层实现:运行时替换ISA指针。
用黑魔法进行观察
NSLog(@"myClassBefore:%@",[self.myClass class]);
NSLog(@"runtimeBefore:%@",object_getClass(self.myClass));
[_myClass addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
NSLog(@"myClassAfter:%@",[self.myClass class]);
NSLog(@"runtimeAfter:%@",object_getClass(self.myClass));
打印结果:
myClassBefore:MyClass
runtimeBefore:MyClass
myClassAfter:MyClass
runtimeAfter:NSKVONotifying_MyClass
对myClass对象的name属性添加观察,runtime
会改变myClass对象的类,MyClass
->NSKVONotifying_MyClass
。如何证明NSKVONotifying_MyClass
为MyClass
的子类?加入头文件#import <objc/runtime.h>,用runtime进行观察。
- (void)viewDidLoad {
[super viewDidLoad];
_myClass = [[MyClass alloc] init];
NSLog(@"%@",[SecondController findSubClass:[_myClass class]]);
[_myClass addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
NSLog(@"%@",[SecondController findSubClass:[_myClass class]]);
}
+ (NSArray *)findSubClass:(Class)defaultClass{
//获取该类中所有类的个数
int count = objc_getClassList(NULL, 0);
if (count < 0) {
return [NSArray array];
}
NSMutableArray * output = [NSMutableArray arrayWithObject:defaultClass];
Class * classes = (Class *)malloc(sizeof(Class) * count);
//获取所有的类
objc_getClassList(classes, count);
for (int i = 0; i < count; i ++) {
if (defaultClass == class_getSuperclass(classes[i])) {
[output addObject:classes[i]];
}
}
free(classes);
return output;
}
打印结果如下:
(
MyClass
)
(
MyClass,
"NSKVONotifying_MyClass"
)
三、KVO对数组元素的监听
[_myClass addObserver:self forKeyPath:@"arr" options:NSKeyValueObservingOptionNew context:nil];
//KVO监听对数组中元素的变化
[[_myClass mutableArrayValueForKey:@"arr"] addObject:@"add"];
//这样子添加是监听不到的
[_myClass.arr addObject:@"add"];
为什么用下面的方法不能监听呢?KVO是基于KVC的,KVO能发送通知,都是通过KVC的方法处理的。
NSLog(@"%@",[[_myClass mutableArrayValueForKey:@"arr"] class]);
会自动生成NSMutableArray的子类:NSKeyValueNotifyingMutableArray
,并重写了NSMutableArray相关的方法,如add,insert等(用黑魔法,runtime可以列出所有的方法),这里面添加了KVC的通信方法,当调用add这些方法的时候,就会触发通知。在add放里面添加了willChangeValueForKey和didChangeValueForKey方法来触发通知。