一、KVC
KVC的全称是Key-Value Coding,俗称“键值编码”,KVC提供了一种间接访问其属性方法或成员变量的机制,可以通过字符串来访问对应的属性方法或成员变量。
经常用到的api
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKeyPath:(NSString *)keyPath;
- (id)valueForKey:(NSString *)key;
+(BOOL)accessInstanceVariablesDirectly;// 此方法默认实现返回YES
KVC原理
+(BOOL)accessInstanceVariablesDirectly
默认返回YES,表示如果没有找到Set方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员,设置成NO就不这样搜索
KVC赋值原理
- 以
set<Key>:、_set<Key>的顺序查找对应命名的setter方法,如果找到的话,调用这个方法并将值传进去(根据需要进行对象转换); - 如果没有发现
setter方法,但是accessInstanceVariablesDirectly类属性返回YES,则按_<key>、_is<Key>、<key>、is<Key>的顺序查找一个对应的实例变量。如果发现则将value赋值给实例变量; - 如果没有发现
setter方法或实例变量,则调用setValue:forUndefinedKey:方法,默认抛出一个异常,但是一个NSObject的子类可以提出合适的行为。
KVC取值原理
-
通过
getter方法查找实例,以get<Key>,<key>,is<Key>,_<key>的顺序搜索符合规则的方法,如果有,就调用对应的方法; -
如果没有找到
getter方法,并且在类方法accessInstanceVariablesDirectly是返回YES的的情况下搜索一个名为_<key>、_is<Key>、<key>、is<Key>的实例; -
如果返回值是一个对象指针,则直接返回这个结果;如果返回值是一个基础数据类型,但是这个基础数据类型是被
NSNumber支持的,则存储为NSNumber并返回;如果返回值是一个不支持NSNumber的基础数据类型,则通过NSValue进行存储并返回; -
在上述情况都失败的情况下调用
valueForUndefinedKey:方法,默认抛出异常,但是子类可以重写此方法。
二、KVO
KVO的全称是Key-Value Observing,俗称“键值监听”,可以用于监听某个对象属性值的改变
KVO原理
- 利用RuntimeAPI动态生成一个子类,并且让instance对象的isa指向这个全新的子类
- 当修改instance对象的属性时,会调用Foundation的_NSSetXXXValueAndNotify函数: willChangeValueForKey: 父类原来的setter didChangeValueForKey: 内部会触发监听器(Oberser)的监听方法( observeValueForKeyPath:ofObject:change:context:)
手动触发KVO
手动调用
- (void)didChangeValueForKey:(NSString *)key
直接修改成员变量会触发KVO么?
不会触发KVO
通过KVC修改属性会触发KVO么?
会触发KVO
如何手动关闭KVO
被观察的对象复写如下方法 返回NO即可关闭KVO
+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
{
return NO;
}
如果关闭后还想触发 KVO的话 修改需要手动调用在变量setter的前后 主动调用 willChangeValueForKey:和didChangeValueForKey:
如何对 NSMutableArray 进行 KVO
- 一般情况下只有通过调用 set 方法对值进行改变才会触发 KVO。但是在调用NSMutableArray的 addObject或removeObject 系列方法时,并不会触发它的 set 方法。所以为了实现NSMutableArray的 KVO,官方为我们提供了如下方法:
@property (nonatomic, strong) NSMutableArray *arr;
//添加元素操作
[[self mutableArrayValueForKey:@"arr"] addObject:item];
//移除元素操作
[[self mutableArrayValueForKey:@"arr"] removeObjectAtIndex:0];
KVO移除之后中间类的说明
-
实例对象在注册KVO观察者之后,isa指针由原有类更改为指向中间类 -
中间类重写了观察属性的setter方法、class、dealloc、_isKVOA方法 -
dealloc方法中,移除KVO观察者之后,实例对象isa指向由中间类更改为原有类 -
中间类从创建后,就一直存在内存中,不会被销毁(考虑到重用问题(KVO再次注册),中间一旦注册到内存中去,就不会销毁)