一、KVC
1、概念:KVC是键值编码,即NSKeyValueCoding。我们可以通过以字符串为Key对对象属性进行操作。
/** 通过传入字符串key获取或者设置类属性 **/
- (nullable id)valueForKey:(NSString *)key;
- (void)setValue:(nullable id)value forKey:(NSString *)key;
/** keyPath:用点号连接的多层级的属性,例如 JZPerson.son **/
- (nullable id)valueForKeyPath:(NSString *)keyPath;
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
2、原理:
valueForKey查找方式:
1.访问器匹配:先寻找与key,isKey, getKey (实测还有_key)同名的方法,返回值为对象类型。
2.实例变量匹配:寻找与key, _key,isKey,_isKey同名的实例变量
setValueForKey查找方式:
1.存取器匹配:先寻找与setKey同名的方法,且参数要为一个对象类型
2.实例变量匹配:寻找与key,_isKey,_key,isKey同名的实例变量,直接赋值。
二、KVO
1、概念:KVO是键值观察,即NSKeyValueObserving。当一个对象被观察的属性发生变化时,观察者做出的处理。在 Objective-C 中有手动或自动两种键值观察的方式。
2、手动方式:
手动键值观察方式比较简单,就是在值发生变化时自动调用方法。
/** name为类属性,手动调用willChangeValueForKey和didChangeValueForKey方法 **/
- (void)setName:(NSString *)name {
[self willChangeValueForKey:@"name"];
_name = name;
[self didChangeValueForKey:@"name"];
}
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
if ([key isEqualToString:@"name"]) {
return NO; // 返回NO实现手动键值观察
}
return [super automaticallyNotifiesObserversForKey:key];
}
3、自动方式:
通过addObserver方法实现对属性变化进行观察。
/** 通过控制器self对person的name属性变化进行观察 **/
self.person = [[JZPerson alloc] init];
[person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
/** 变化时调用方法 **/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
}
// 移除观察
- (void)dealloc {
[self.person removeObserver:self forKeyPath:@"name"];
}
实现原理:
当某个类的对象第一次被观察时,系统就会在运行期动态地创建该类的一个派生类,在这个派生类中重写基类中任何被观察属性的 setter 方法。
派生类在被重写的 setter 方法实现真正的通知机制,就如前面手动实现键值观察那样。这么做是基于设置属性会调用 setter 方法,而通过重写就获得了 KVO 需要的通知机制。当然前提是要通过遵循 KVO 的属性设置方式来变更属性值,如果仅是直接修改属性对应的成员变量,是无法实现 KVO 的。
同时派生类还重写了 class 方法以“欺骗”外部调用者它就是起初的那个类。然后系统将这个对象的 isa 指针指向这个新诞生的派生类,因此这个对象就成为该派生类的对象了,因而在该对象上对 setter 的调用就会调用重写的 setter,从而激活键值通知机制。此外,派生类还重写了 dealloc 方法来释放资源。手动实现的方式并不会生成NSKVONotifying_xx派生类,只有自动方式才会生成派生类。
参考文献: