类的单个属性观察
观察值的4中模式
/**
* NSKeyValueObservingOptionNew 新值
* NSKeyValueObservingOptionOld 旧值
* NSKeyValueObservingOptionInitial 注册发送通知,改变时发送通知
* NSKeyValueObservingOptionPrior 改变之前发送通知,改变后发送通知
*/
针对Person
类里面的name
属性进行观察
[person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
查看观察
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(@"%@", change);
}
打印效果
2019-03-22 09:20:16.875033+0800 KVO[1844:81256] {
kind = 1;
new = 0;
}
类的里面嵌套其他的属性观察
观察Person
里面Dog
类的age
,通过点语法直接监听
[person addObserver:self forKeyPath:@"dog.age" options:NSKeyValueObservingOptionNew context:nil];
输入结果
2019-03-22 09:23:25.854859+0800 KVO[1876:84141] {
kind = 1;
new = 11;
}
一次性观察多个值
例如观察Person
里面的Dog
的age
和level
,则在Person
类中重新+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key
+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([key isEqualToString:@"dog"]) {
NSArray *array = @[@"_dog.name", @"_dog.level"];
keyPaths = [keyPaths setByAddingObjectsFromArray:array];
}
return keyPaths;
}
直接观察Dog
类
[person addObserver:self forKeyPath:@"dog" options:NSKeyValueObservingOptionNew context:nil];
打印结果
2019-03-22 09:27:11.485240+0800 KVO[1929:87568] {
kind = 1;
new = "<Dog: 0x6000027c0b80>";
}
手动模式
在Person类中重写+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
// 修改为手动模式
return NO;
}
手动触发
[self.person willChangeValueForKey:@"name"];
[self.person didChangeValueForKey:@"name"];
观察容器类
需要利用KVC
例如观察person
中的array
[[self.person mutableArrayValueForKey:@"array"] addObject:@"obj"];
输出结果
2019-03-22 11:02:13.996095+0800 KVO[5521:170395] {
indexes = "<_NSCachedIndexSet: 0x600002216160>[number of indexes: 1 (in 1 ranges), indexes: (0)]";
kind = 2;
new = (
obj
);
}
KVO的底层实现
实质:观察值的setter方法
通过runtime,新建一个子类继承于观察类(NSKVONotifing_类名),动态的修改了观察类的类型为子类的类型,在子类里面重写set方法,调用
- (void)setName:(NSString *)name {
[self willChangeValueForKey:@"name"];
[super setName:name];
[self didChangeValueForKey:@"name"];
}
网友评论