全称
- KVO: Key-Value-Observer
- KVC: Key-Value-Coding
KVO的使用
使用过程
监听回调返回新值和旧值
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
添加监听
[被监听的实例对象 addObserver:监听器 forKeyPath:属性 options:options context:nil]
监听接收事件
- (void)observeValueForKeyPath:(NSString*)keyPathofObject:(id)objectchange:(NSDictionaryid> *)changecontext:(void*)context{ NSLog(@"%@--%@--%@",object,keyPath,change); }
记得添加监听和移除监听,需要配对使用
原理
测试类 Person
系统会生成NSKVONotifying_Person类,该类继承Person,并且将Person的isa指向NSKVONotifying_Person。
-
NSKVONotifying_Person从写监控属性的setter方法,内部调用
_assetXXXValueAndNotify();
_assetXXXValueAndNotify内部实现:2.1
- (void)willChangeValueForKey:(NSString *)key;
2.2
- (void)didChangeValueForKey:(NSString *)key;
2.3
didChangeValueForKey
里面调用[super didChangeValueForKey]
时,会触发回调方法
- (void)observeValueForKeyPath:(NSString*)keyPathofObject: (id)objectchange:(NSDictionaryid> *)changecontext:(void*)context
通知开发者数据发生变化。
3.NSKVONotifying_Person内部还会从写 - (Class)class;
方法,目的是隐藏系统生成的类。
4.如果需要手动调用,只需要调用2个方法:
willChangeValueForKey
didChangeValueForKey
即可;
5.单纯使用KVO,访问成员变量person -> age = 10;
是不会触发KVO监听的。
KVC setValueForKey: 和setValueForKeyPath:
假如Person对象有个属性叫age
setValueForKey
- 可以对实例对象的某个属性赋值
setValueForKeyPath:入参是路径,
可以对实例对象中的,某个对象的某个属性,进行赋值;例如入参可以是:person.data.num,就代表对person对象的data对象的num属性进行赋值
可以访问对象的隐藏属性
-
可以求最大最小值,再也不用手动遍历数组求值了
求最大值:[array valueForKeyPath:@"@max.self"]
使用KVC赋值
例如Person中有一个age属性
查找顺序
1.setAge:
2._setAge:假如上面2个方法都没有实现,则系统会调用是否允许直接访问成员变量方法:
+ (BOOL)accessInstanceVariablesDirectly
如果返回NO,则系统调用setValue:forUndefineKey
,直接报NSUnknowKeyException异常;如果没有setter方法,只有成员变量时,并且
+ (BOOL)accessInstanceVariablesDirectly
返回YES,KVO同样可以监测到属性值发生了改变;
例如需要查找的属性叫age,则查找顺序如下
1. _age
2. _isAge
3. age
4. isAge
如果方法未找到,上面4个属性也未找到,则会报上面的异常setValue:forUndefineKey
KVC取值 valueForKey: 和valueForKeyPath:
- 方法查找顺序
1. getKey
2. key
3. isKey
4. _key
如果方法未找到,则会调用系统方法+ (BOOL)accessInstanceVariablesDirectly
判断是否可以直接访问成员变量,如果是NO,则会报valueForUndefineKey,如果是YES,则会按如下顺序查找成员变量:
1. _key
2. _isKey
3. key
4. isKey