一、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方法來觸發通知。