1 kvc的概念
指在iOS的开发中,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存取方法。这样就可以在运行时动态在访问和修改对象的属性。
1.1 kvc简单实战
假设我们创建了一个Person类.有age和name两个属性.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property(nonatomic, assign) int age;
@property(nonatomic, strong) NSString *name;
@end
NS_ASSUME_NONNULL_END
在我们初始化Person类后,进行如下赋值
Person *person = [[Person alloc]init];
//赋值 和取值
[person setValue:@"小明" forKey:@"name"];
NSLog(@"=====%@",[person valueForKey:@"name"]);
会打印输出=====小明.但由于我们key是我们手动输入的,可能存在我们取值的key在Person类中不存在的情况,这时候我们如何去捕获这个错误呢?
#import "Person.h"
@implementation Person
-(void)setValue:(id)value forUndefinedKey:(NSString *)key {
}
@end
-(void)setValue:(id)value forUndefinedKey:(NSString *)key,该方法用于捕获访问的key不存在发生的错误.
2 kvo的概念
当指定的对象的属性被修改后,则对象就会接受到通知。简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。
2.1 KVO原理
当某个类的实例对象的key第一次被观察时,系统就会在运行期动态地 创建该类的一个派生类NSKVONotifying_类名, 在这个派生类中重写该类中被观察的属性的 setter 方法。
2.1 kvo简单实战
注册观察者,并添加观察对象属性
self.p = [[Person alloc]init];
self.p.age = 18;
[self.p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
模拟改变属性
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
self.p.age = 11;
}
实现回调方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
//我们在该方法中获取到 oldValue和newValue,因为change是一个字典类型
id oldValue = [change objectForKey:NSKeyValueChangeOldKey];
id newValue = [change objectForKey:NSKeyValueChangeNewKey];
NSLog(@"oldValue%@----newValue%@",oldValue,newValue);
NSLog(@"keyPath=%@,object=%@,change=%@,context=%@",keyPath,object,change,context);
}
当添加属性观察者的时候,系统动态创建Person的派生类NSKVONotifying_Person, 并修改jack的isa,使其指向这个派生类(也就是说,jack就变成是NSKVONotifying_Person这个派生类的实例)。这样修改后,当 touch屏幕 修改属性的时候,调用的就是派生类重写的该属性的setter方法。
注意 : 由于这种继承方式的注入是在运行时而不是编译时实现的,如果给定的实例没有观察者,那么KVO不会有任何开销,因为此时根本就没有KVO代码存在。