原文 kvc kvo
KVC 概论
KVC(全称 key-value-coding)即键值编码。KVC 的操作方法由NSKeyValueCoding 非正式协议提供,而NSObject(NSKeyValueCoding)就实现了这个协议,也就是说ObjC中几乎所有的对象都支持 KVC 操作,它是一种不通过存取方法(Setter、Getter),而通过属性名称字符串(key)间接访问类属性(实例变量)的机制。
- (void)setValue:(nullable id)value forKey:(NSString *)key;
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
- (id)valueForKey:(NSString *)key;
- (nullable id)valueForKeyPath:(NSString *)keyPath;
KVC 实现原理
1、[item setValue:@"白开水ln简书" forKey:@"name"];
1.首先去模型中查找有没有 setName,若有,直接调用赋值 [self setName:@"白开水ln简书"]。
2.若无,去模型中查找有没有 name 属性,若有,直接访问属性赋值 name = value。
3.若无,再去模型中查找有没有 _name 成员变量,若有,直接访问属性赋值 _name = value。
4.找不到,就会直接报找不到的错误(valueForUndefinedKey:)。
2、[item setValuesForKeysWithDictionary:dict];
1.遍历字典中所有 key。
2.去模型中查找有没有对应的属性。
LNPerson *person = [[LNPerson alloc] init];
person.dog = [[LNDog alloc] init];
[person.dog setValue:@"阿黄" forKey:@"name"];
[person setValue:@"旺财" forKeyPath:@"dog.name"];
NSLog(@"%@", person.dog.name);
区别:forKey: 和 forKeyPath:
1、forKeyPath 包含了所有 forKey 的功能
2、forKeyPath 进行内部的点语法,层层访问内部的属性
3、注意:key 值一定要在属性中找到,开发中最好使用forKeyPath。
KVO(Key-Value-Obersver)即键值监听,利用一个key来找到某个属性并监听其属性值得改变,当该属性发生变化时,会自动的通知观察者,这比通知中心需要post通知来说,简单了许多。其实这也是一种典型的观察者模式。
KVO 使用步骤
给目标对象的属性添加观察者
在回调方法中监听属性的变化
移除观察者
// 1.添加观察者
[self.person addObserver:self forKeyPath:@"name" options: NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];}
- (void)dealloc{
// 2.移除观察者
[self.person removeObserver:self forKeyPath:@"name"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(LNPerson *)person change:(NSDictionary *)change context:(void *)context{
NSLog(@"%@------%@------%@", keyPath, change);
}
KVO 实现原理
当一个类的属性被观察的时候,系统会通过runtime动态的创建一个该类的派生类,而且系统将这个类的isa指针指向了派生类,从而实现了给监听的属性赋值时调用的是派生类的setter方法。
还会在这个类的基类中重写被观察的属性的setter方法,重写的setter方法会在调用原setter方法前后,通知观察对象值得改变
同时派生类还重写了 class 方法以“欺骗”外部调用者它就是起初的那个类。
此外,派生类还重写了 dealloc 方法来释放资源。
KVO 实现原理
可以看到重写的 setter 方法,给属性赋值的前后分别调用了两个方法。
- (void)willChangeValueForKey:(NSString *)key;
- (void)didChangeValueForKey:(NSString *)key;
而- (void)didChangeValueForKey:(NSString *)key;会调用
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary *)change context:(nullable void *)context;