文章目录
OC对象 - KVC
俗称“键值编码”,可以通过一个key来访问某个属性
1. 常用API
- - (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
- - (void)setValue:(id)value forKey:(NSString *)key;
- - (id)valueForKeyPath:(NSString *)keyPath;
- - (id)valueForKey:(NSString *)key;
2. 原理
2.1 setValue:forKey:的原理
- 在执行setValue:forKey:的时候
- 按照
setKey
、_setKey:
顺序查找方法
- 找到方法就传递参数,调用方法
- 没找到方法,会看一下
accessInstanceVariablesDirectly
方法的 return(默认return YES) - 如果
accessInstanceVariablesDirectly
方法的 return NO,就会走setValue:forUndefinedKey:
,并抛出异常NSUnknownKeyException
- 否则的话
- 此时会按照
_key、_isKey、key、isKey
的顺序查找成员变量
,找到了就直接赋值
2.2 验证setValue:forKey:
2.2.1 查找方法阶段
- 我们在ZSXPerson.m中实现下这两个方法
@implementation ZSXPerson
- (void)setAge:(int)age {
NSLog(@"%s", __func__);
_age = age;
}
- (void)_setAge:(int)age {
NSLog(@"%s", __func__);
_age = age;
}
@end
此时可以看到执行的是setAge:
方法
- 验证查找
_setAge:
方法
要验证_setAge:
方法,我们不能再用@property 来声明 age 属性,因为@property 会自动帮我们生成 get、set 方法
- ZSXPerson 我们去掉@property声明的age属性,.m文件里面实现
_setAge:
方法,然后同样使用setValue:forKey:来给 age 赋值
@interface ZSXPerson : NSObject
@end
@implementation ZSXPerson
//- (void)setAge:(int)age {
// NSLog(@"%s", __func__);
//}
- (void)_setAge:(int)age {
NSLog(@"%s", __func__);
}
@end
- 我们发现
_setAge:
方法确实执行了
- 如果我们把
setAge:
方法再打开,会发现setAge:
是比_setAge:
方法优先被查找调用的
2.2.2 查找成员变量阶段
2.2.2.1 验证成员变量按顺序查找
- 我们假设ZSXPerson有这样几个成员变量。(
setAge:(int)age
和_setAge:(int)age
需要去掉,否则会优先找方法)
@interface ZSXPerson : NSObject {
@public
int _age;
int _isAge;
int age;
int isAge;
}
@end
@implementation ZSXPerson
//- (void)setAge:(int)age {
// NSLog(@"%s", __func__);
//}
//- (void)_setAge:(int)age {
// NSLog(@"%s", __func__);
//}
@end
-
_age
-
_isAge
- age
- isAge
验证了 成员变量按顺序查找
2.3 setValue:forKey: 触发KVO监听
有set方法
我们清楚KVO的原理,以及KVCsetValue:forKey:
原理,已经能确定是会触发KVO监听的
没有set方法
我们主要验证下没有set方法的情况下,是否会触发KVO监听
- 我们刚刚ZSXPerson的.m文件中,已经注释掉相应的set方法
- 这时候给ZSXPerson的
实例对象
添加KVO监听age
值变化
可以看到 KVO 监听也是会触发的
我们已经没有任何 set 方法了,但是 KVO 监听依然触发了。这是因为 KVC 内部本身会触发 keyPath 值变化通知
2.2.2.2 accessInstanceVariablesDirectly
我们尝试accessInstanceVariablesDirectly
返回NO
此时它不会查找成员变量,因此找不到对应的 key 赋值,抛异常了
2.3 valueForKey:的原理
- 在执行valueForKey:的时候
- 按照
getKey
、key
、isKey
、_key
顺序查找方法
- 找到方法就传递参数,调用方法
- 没找到方法,会看一下
accessInstanceVariablesDirectly
方法的 return(默认return YES) - 如果
accessInstanceVariablesDirectly
方法的 return NO,就会走setValue:forUndefinedKey:
,并抛出异常NSUnknownKeyException
- 否则的话
- 此时会按照
_key、_isKey、key、isKey
的顺序查找成员变量
,找到了就直接赋值
2.4 验证valueForKey:
2.4.1 查找方法阶段
- 分别创建
getKey
、key
、isKey
、_key
四个方法 - 我们直接给实例对象person的age赋值,然后使用
valueForKey:
获取age
valueForKey:
按顺序查找方法
,最后查不到对应方法抛异常了
2.4.1 查找成员变量阶段
找不到方法,接下来按顺序查找成员变量
- 我们创建
_key、_isKey、key、isKey
几个成员变量 - 先给几个成员变量赋不同的值,然后使用
valueForKey:
获取age
valueForKey:
按顺序查找成员变量
,最后查不到对应方法抛异常了
@oubijiexi