基本用法
字典快速赋值
KVC 可以将字典里面和 model 同名的 property 进行快速赋值 setValuesForKeysWithDictionary
//前提:model 中的各个 property 必须和 NSDictionary 中的属性一致
- (instancetype)initWithDic:(NSDictionary *)dic{
BannerModel *model = [BannerModel new];
[model setValuesForKeysWithDictionary:dic];
return model;
}
但是这里会有2种特殊情况。
- 情况一 在 model 里面有 property 但是在 NSDictionary 里面没有这个值
运行上面的代码,代码不崩溃,只不过在输出值的时候输出了 null
- 情况二 在 NSDictionary 中存在某个值,但是在 model 里面没有值
运行后编译成功,但是代码奔溃掉。原因是 KVC 。所以我们只需要实现这么一个方法。甚至不需要写函数体部分
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
}
- 情况三 如果 Dictionary 和 Model 中的 property 不同名
我们照样可以利用 setValue:forUndefinedKey: 去处理
//model
@property (nonatomic,copy)NSString *name;
@property (nonatomic,copy)NSString *sex;
@property (nonatomic,copy) NSString* age;
//NSDictionary
NSDictionary *dic = @{@"username":@"张三",@"sex":@"男",@"id":@"22"};
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
if([key isEqualToString:@"id"]){
self.age=value;
}
if([key isEqualToString:@"username"]){
self.name=value;
}
}
情况四 如果我们观察对象的属性是数组,我们经常会观察不到变化,因为 KVO 是观察 setter 方法。我们可以用 mutableArrayValueForKeyPath 进行属性的操作
NSMutableArray *hobbies = [_person mutableArrayValueForKeyPath:@"hobbies"]; [hobbies addObject:@"Web"];
情况五 注册依赖键.
KVO 可以观察属性的二级属性对象的所有属性变化。说人话就是“假如 Person 类有个 Dog 类,Dog 类有 name、fur、weight 等属性,我们给 Person 的 Dog 属性观察,假如 Dog 的任何属性变化是,Person 的观察者对象都可以拿到当前的变化值。我们只需要在 Person 中写下面的方法即可”
[self.person addObserver:self
forKeyPath:NSStringFromSelector(@selector(dog))
options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
context:ContextMark];
self.person.dog.name = @"啸天犬";
self.person.dog.weight = 50;
// Person.m
+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([key isEqualToString:@"dog"]) {
NSArray *affectingKeys = @[@"name", @"fur", @"weight"];
keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
}
return keyPaths;
}
KVO的本质
kVO 是Objective-C 对观察者模式的实现。也是 Cocoa Binding 的基础。
几个基本的知识点
1 KVO 观察者和属性被观察的对象之间不是强引用的关系
2 KVO 的触发分为自动触发模式和手动触发模式2种。通常我们使用的都是自动通知,注册观察者之后,当条件触发的时候会自动调用-(void)observeValueForKeyPath… 如果需要实现手动通知,我们需要使用下面的方法
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key { return NO; }
3 若类有实例变量 NSString *_foo, 调用 setValue:forKey: 是以 foo 还是 _foo 作为 key ?
都可以4 KVC 的 keyPath 中的集合运算符如何使用
5 KVO 和 KVC 的 keyPath 一定是属性吗? 可以是成员变量
KVO的缺陷
KVO 虽然很强大,你只能重写 -
observeValueForKeyPath:ofObject:change:context: 来获得通知,想要提供自定义的 selector ,不行;想要传入一个 block,没门儿。感觉如果加入 block 就更棒了。