KVO、KVC

KVO(Key Value Observing) 键值观察,苹果提供的一套事件通知机制

KVO用于监听对象某个属性的改变,当被观察的属性的值发生变化时,会自动调用相应的方法。

使用:
  1. 注册监听
    通过[addObserver:forKeyPath:options:context:]方法注册KVO,这样可以接收到keyPath属性的变化事件

  2. 监听方法
    通过方法[observeValueForKeyPath:ofObject:change:context:]实现KVO的监听

  3. 移除监听
    在不需要监听的时候,通过方法[removeObserver:forKeyPath:],移除监听

//1. 注册监听
[self.person addObserver:self forKeyPath:NSStringFromSelector(@selector(age)) options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

//2. 监听实现
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    NSLog(@"监听到%@的%@属性值改变了 %@", object, keyPath, change);
}

//3. 移除监听
[self.person removeObserver:self forKeyPath:NSStringFromSelector(@selector(age))];
KVO实现原理

当一个对象使用了KVO监听,iOS系统会修改这个对象的isa指针,改为指向一个全新的通过Runtime(isa-swizzling)动态创建的子类,子类拥有自己的set方法实现,set方法实现内部会顺序调用willChangeValueForKey方法、原来的setter方法实现、didChangeValueForKey方法,而didChangeValueForKey方法内部又会调用监听器的observeValueForKeyPath:ofObject:change:context:监听方法

通过在addObserver前后打断点可以得出以下结论:

addObserver之前 person的isa指向Person类对象
addObserver之后 isa指向NSKVONotifying_Person对象

image.png
image.png
如何手动触发KVO

被监听的属性的值被修改时,就会自动触发KVO。

如果想要手动触发KVO,则需要我们自己调用willChangeValueForKey和didChangeValueForKey方法即可在不改变属性值的情况下手动触发KVO,并且这两个方法缺一不可

//手动触发KVO 被监听的属性的值未发生改变 也触发KVO
//以下方法缺一不可
[self.person willChangeValueForKey:@"age"];
[self.person didChangeValueForKey:@"age"];

KVC(Key Value Coding) 键值编码,可以通过一个key来访问属性

KVC提供了一种间接访问其属性或成员变量的机制,可以通过字符串来访问对应的属性方法或成员变量。

常见API
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;

- (void)setValue:(id)value forKey:(NSString *)key;

- (id)valueForKeyPath:(NSString *)keyPath;

- (id)valueForKey:(NSString *)key;
setValue:forKey:原理
  1. 按照setKey、_setKey的顺序查找方法,找到方法则直接调用方法并赋值;
  2. 未找到方法则调用+ (BOOL)accessInstanceVariablesDirectly方法,若方法+ (BOOL)accessInstanceVariablesDirectly返回YES,则按照_key、_isKey、key、isKey的顺序查找成员变量,找到直接赋值,找不到则调用setValue:forUndefinedKey: 并抛出NSUnknownKeyException异常,若+ (BOOL)accessInstanceVariablesDirectly方法返回NO,则调用setValue:forUndefinedKey: 并抛出NSUnknownKeyException异常;
valueForKey:原理
  1. 按照getKey、key、isKey、_key的顺序查找方法,找到则直接调用赋值;
  2. 若未找到,则查看+ (BOOL)accessInstanceVariablesDirectly方法的返回值,若返回YES,按照_key、_isKey、key、isKey的顺序查找成员变量,找到则取值,找不到则调用valueForUndefinedKey:并抛出NSUnknownKeyException异常;若返回NO,则调用valueForUndefinedKey:方法并抛出异常NSUnknownKeyException;

通过KVC修改属性会触发KVO么
会触发KVO,KVC在修改属性时,会调用willChangeValueForKey: 和 didChangeValueForKey:方法

forKey与forKeyPath区别:
forKey只能给当前对象的属性赋值
forKeyPath可以给对象的属性的属性赋值

valueForKey与valueForKeyPath区别:
valueForKey只能获取当前对象属性的值
valueForKeyPath可以获取当前对象属性的属性的值

你可能感兴趣的:(KVO、KVC)