1 kvc的概念

指在iOS的开发中,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存取方法。这样就可以在运行时动态在访问和修改对象的属性。

1.1 kvc简单实战

假设我们创建了一个Person类.有age和name两个属性.

  1. #import <Foundation/Foundation.h>
  2. NS_ASSUME_NONNULL_BEGIN
  3. @interface Person : NSObject
  4. @property(nonatomic, assign) int age;
  5. @property(nonatomic, strong) NSString *name;
  6. @end
  7. NS_ASSUME_NONNULL_END

在我们初始化Person类后,进行如下赋值

  1. Person *person = [[Person alloc]init];
  2. //赋值 和取值
  3. [person setValue:@"小明" forKey:@"name"];
  4. NSLog(@"=====%@",[person valueForKey:@"name"]);

会打印输出=====小明.但由于我们key是我们手动输入的,可能存在我们取值的key在Person类中不存在的情况,这时候我们如何去捕获这个错误呢?

  1. #import "Person.h"
  2. @implementation Person
  3. -(void)setValue:(id)value forUndefinedKey:(NSString *)key {
  4. }
  5. @end

-(void)setValue:(id)value forUndefinedKey:(NSString *)key,该方法用于捕获访问的key不存在发生的错误.

2 kvo的概念

当指定的对象的属性被修改后,则对象就会接受到通知。简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。

2.1 KVO原理

当某个类的实例对象的key第一次被观察时,系统就会在运行期动态地 创建该类的一个派生类NSKVONotifying_类名, 在这个派生类中重写该类中被观察的属性的 setter 方法。

2.1 kvo简单实战

注册观察者,并添加观察对象属性

  1. self.p = [[Person alloc]init];
  2. self.p.age = 18;
  3. [self.p addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];

模拟改变属性

  1. -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
  2. self.p.age = 11;
  3. }

实现回调方法

  1. -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
  2. //我们在该方法中获取到 oldValue和newValue,因为change是一个字典类型
  3. id oldValue = [change objectForKey:NSKeyValueChangeOldKey];
  4. id newValue = [change objectForKey:NSKeyValueChangeNewKey];
  5. NSLog(@"oldValue%@----newValue%@",oldValue,newValue);
  6. NSLog(@"keyPath=%@,object=%@,change=%@,context=%@",keyPath,object,change,context);
  7. }

当添加属性观察者的时候,系统动态创建Person的派生类NSKVONotifying_Person, 并修改jack的isa,使其指向这个派生类(也就是说,jack就变成是NSKVONotifying_Person这个派生类的实例)。这样修改后,当 touch屏幕 修改属性的时候,调用的就是派生类重写的该属性的setter方法。
注意 : 由于这种继承方式的注入是在运行时而不是编译时实现的,如果给定的实例没有观察者,那么KVO不会有任何开销,因为此时根本就没有KVO代码存在。
image.png