Objective - C KVC
2020-04-30 本文已影响0人
爱玩游戏的iOS菜鸟
(一)KVC (Key-Value Coding)
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)访问属性常规用法
- 设置
-
setValue: forKey:
设置对象的属性值(单层) -
setValue: forKeyPath:
可以设置属性的属性(可以多层)
self.person = [[ZQPerson alloc]init];
//setValue: forKey:方法
[self.person setValue:@10 forKey:@"age"];
self.person.dog = [[ZQDog alloc] init];
//setValue: forKeyPath:方法
[self.person setValue:@20 forKeyPath:@"dog.height"];
- 访问(同理)
valueForKeyPath:
valueForKey:
//输出10 10
NSLog(@"%@ %@",[self.person valueForKey:@"age"],[self.person valueForKeyPath:@"dog.height"])
疑问:通过KVC设置成员变量的值,会触发KVO吗?
- (void)viewDidLoad {
[super viewDidLoad];
self.person = [[ZQPerson alloc]init];
[self.person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
[self.person setValue:@20 forKey:@"age"];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
NSLog(@"%@ %@ %@",keyPath,object,change);
}
-(void)dealloc{
[self.person removeObserver:self forKeyPath:@"age"];
}
输出结果:会触发!
age <ZQPerson: 0x60000026a5e0> {
kind = 1;
new = 20;
old = 0;
}
到底是什么原因呢?
(3)设值原理
①设置ZQPerson的_age成员变量为私有,添加两个方法(如下),重新运行
//ZQPerson.h
@interface ZQPerson : NSObject{
int _age;
}
-(void)setAge:(int)age;
-(void)_setAge:(int)age;
@end
//ZQPerson.m
@implementation ZQPerson
-(void)setAge:(int)age{
_age = age;
NSLog(@"setAge:%d",age);
}
-(void)_setAge:(int)age{
_age = age;
NSLog(@"其次找这里");
}
@end
输出结果:
setAge:20
age <ZQPerson: 0x600003304ac0> {
kind = 1;
new = 20;
old = 0;
}
②接着注释掉setAge
,再次执行:
_setAge:20
age <ZQPerson: 0x600002b442d0> {
kind = 1;
new = 20;
old = 0;
}
我们可以得出结论一:
setValue: forKey:
方法首先会查找setKey
,_setKey
的优先顺序先查找方法
④我们删除所有的set相关方法,重新运行:
age <ZQPerson: 0x6000015d5cc0> {
kind = 1;
new = 20;
old = 0;
}
KVO依然会触发!!!
⑤继续,我们重写+(BOOL)accessInstanceVariablesDirectly
方法,返回NO,发现报错了valueForUndefinedKey
reason: '[<ZQPerson 0x6000029ac7f0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key age.'
我们可以得出结论二:
如果没有找到方法,会判断该方法是否允许查找成员变量,不允许的情况下直接报错
⑥接下来,我们将+(BOOL)accessInstanceVariablesDirectly
返回YES,添加_age
,_isAge
,age
,isAge
四个成员变量:
@interface ZQPerson : NSObject{
int _age;
int _isAge;
int age;
int isAge;
}
@end
运行结果:
我们可以得出结论三:
若方法是否允许查找成员变量,则会按照
_age
,_isAge
,age
,isAge
的优先顺序给成员变量赋值,如果找不到,还是会报错valueForUndefinedKey
下面对上面的验证流程做一个总结:
accessInstanceVariablesDirectly方法的默认返回值是YES
只要通过
setValue: forKey:
的方法,只要赋值成功了,就会触发KVO(自己可以去代码验证)
可能在赋值过程内部,调用了willChangeValueForKey:
及didChangeValueForKey:
,可以通过重写这两个方法,发现确实会调用这两个方法(验证流程省略)