KVO && KVC

111 阅读2分钟

KVO(Key-Value Observing, 键值监听)使用

添加观察者
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath 
options:(NSKeyValueObservingOptions)options context:(nullable void *)context;

改变属性值响应函数
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object 
change:(nullable NSDictionary<NSKeyValueChangeKey, id> *)change context:(nullable void *)context;

移除观察者
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;

KVO原理

通过runtime API 动态生成一个子类NSKVONotifying_xxxx,然后实例对象的isa指针会指向这个动态生成的子类,在改变属性值的时候会调用Foundation的
_NSSetxxxValueAndNotify 这个C语言函数,然后在这个函数里面会调用
- (void)willChangeValueForKey:(NSString *)key
父类原来的setter方法
- (void)didChangeValueForKey:(NSString *)key的内部会调用
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object 
change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
所以我们可以手动调用willChangeValueForKey和didChangeValueForKey来触发KVO
直接给成员变量赋值的时候因为没有调用setter方法导致没有调用_NSSetxxxValueAndNotify然后就没有调用willxxx和didxxx,所以没法触发KVO

KVC原理

- (void)setValue:(nullable id)value forKey:(NSString *)key;
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
set方法会通过查找key对应的setKey: _setKey方法来赋值如果找到了就赋值没找到就会调用
+ (BOOL)accessInstanceVariablesDirectly
如果返回yes 就会按照_key _isKey key isKey的顺序查找成员变量 如果找到直接对相应的成员变量直接赋值 
			如果没找到就会报错调用setValue:forUndefinedKey:并抛出异常NSUnknownKeyException
如果返回no 就会报错调用setValue:forUndefinedKey:并抛出异常NSUnknownKeyException

- (nullable id)valueForKey:(NSString *)key;
- (nullable id)valueForKeyPath:(NSString *)keyPath;
取值会按照 getKey key isKey _key 的顺序取值,如果这些方法都找不到就会调用
+ (BOOL)accessInstanceVariablesDirectly
如果返回yes 就会按照_key _isKey key isKey的顺序查找成员变量 如果找到直接对相应的成员变量直接取值
			如果没找到就会报错调用valueForUndefinedKey: 并抛出异常NSUnknownKeyException
如果返回no 就会报错调用valueForUndefinedKey: 并抛出异常NSUnknownKeyException

kvc修改属性是会触发kvo的 因为通过kvc修改属性的时候会调用set方法,假如没有些set方法 也会通过调用willchangevalue和didChangeValue 所以kvc是会触发kvo的