KVC API
KVC以非正式协议的方式出现在 Foundation/NSKeyValueCoding
KVC方法执行顺序
Persion.m
#import "Persion.h"
@implementation Persion
@synthesize name = _name;
- (void)setName:(NSString *)name {
NSLog(@"%s",__func__);
_name = name;
}
- (NSString *)name {
NSLog(@"%s",__func__);
return _name;
}
+ (BOOL)accessInstanceVariablesDirectly {
NSLog(@"%s",__func__);
return [super accessInstanceVariablesDirectly];
}
- (id)valueForKey:(NSString *)key {
NSLog(@"%s",__func__);
return [super valueForKey:key];
}
- (void)setValue:(id)value forKey:(NSString *)key {
NSLog(@"%s",__func__);
return [super setValue:value forKey:key];
}
@end
ViewController.m
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Persion *persion = [[Persion alloc] init];
[persion setValue:@"zyy" forKey:@"name"];
[persion valueForKey:@"name"];
}
输出
2016-10-27 17:32:21.717 KVC[8519:355539] -[Persion setValue:forKey:]
2016-10-27 17:32:21.718 KVC[8519:355539] -[Persion setName:]
2016-10-27 17:32:21.718 KVC[8519:355539] -[Persion valueForKey:]
2016-10-27 17:32:21.718 KVC[8519:355539] -[Persion name]
有的同学可能会问 为什么 还要写 @synthesize?
在同时实现setter和getter时,类中的@property不会自动生成带下滑线的成员变量,所以这里要手动生成。
看编译器输出结果,发现2个点
1、+ (BOOL)accessInstanceVariablesDirectly 方法没调用
2、KVC 自动调用属性对应的setter和getter方法
稍作修改再次运行
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Persion *persion = [[Persion alloc] init];
[persion setValue:@"zyy" forKey:@"_name"];
[persion valueForKey:@"_name"];
}
@end
输出
2016-10-27 17:46:11.649 KVC[8779:369047] -[Persion setValue:forKey:]
2016-10-27 17:46:11.649 KVC[8779:369047] +[Persion accessInstanceVariablesDirectly]
2016-10-27 17:46:11.649 KVC[8779:369047] -[Persion valueForKey:]
2016-10-27 17:46:11.650 KVC[8779:369047] +[Persion accessInstanceVariablesDirectly]
对比发现:
1、+ (BOOL)accessInstanceVariablesDirectly被调用了
注意:这个方法默认返回为YES。当返回YES且key值不是name时,会自动匹配_name(返回NO不会进行匹配)
2、这次name对应的setter和getter并没有调用
我们追加一个打印,查看一下 persion.name属性到底有没有被 赋上值
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Persion *persion = [[Persion alloc] init];
[persion setValue:@"zyy" forKey:@"_name"];
[persion valueForKey:@"_name"];
NSLog(@"%@",persion.name);
}
@end
输出
2016-10-27 17:53:22.764 KVC[8932:375941] -[Persion setValue:forKey:]
2016-10-27 17:53:22.765 KVC[8932:375941] +[Persion accessInstanceVariablesDirectly]
2016-10-27 17:53:22.765 KVC[8932:375941] -[Persion valueForKey:]
2016-10-27 17:53:22.765 KVC[8932:375941] +[Persion accessInstanceVariablesDirectly]
2016-10-27 17:53:22.765 KVC[8932:375941] -[Persion name]
2016-10-27 17:53:22.766 KVC[8932:375941] zyy
name确实已被赋值,并且persion.name仍然没有执行name的getter,那么name究竟是怎么被赋值\取值的? 我们加上几行测试代码
@implementation Persion
@synthesize name = _name;
- (void)set_name:(NSString *)name {
NSLog(@"%s",__func__);
_name = name;
}
- (NSString *)_name {
NSLog(@"%s",__func__);
return _name;
}
- (void)setName:(NSString *)name {
NSLog(@"%s",__func__);
_name = name;
}
- (NSString *)name {
NSLog(@"%s",__func__);
return _name;
}
输出
2016-10-27 17:58:13.006 KVC[9062:381370] -[Persion setValue:forKey:]
2016-10-27 17:58:13.007 KVC[9062:381370] -[Persion set_name:]
2016-10-27 17:58:13.007 KVC[9062:381370] -[Persion valueForKey:]
2016-10-27 17:58:13.007 KVC[9062:381370] -[Persion _name]
对比发现:
1、+ (BOOL)accessInstanceVariablesDirectly现在又不会调用了(虽然仍然返回YES),我去,怎么回事。
2、新添加的 set_name 和 _name 方法 却被调用了。
仔细想一下,发现是这样
找到了_name对应的setter和getter,既然找到了,自然不用匹配。
为什么persion.name会有值?
因为写了@synthesize name = _name,此时已经将persion.name与_name关联起来,_name并不是一个独立的成员变量。如果不用@synthesize name = _name,改为定义成员变量_name仍然可行,因为Xcode会默认将persion.name与自定义成员变量_name关联。
更深一层思考:
如果将@synthesize name = _name 改为 @synthesize name = _na 并且实现_na的getter和setter。
@implementation Persion
@synthesize name = _na;
- (void)set_na:(NSString *)name {
NSLog(@"%s",__func__);
_na = name;
}
- (NSString *)_na {
NSLog(@"%s",__func__);
return _na;
}
+ (BOOL)accessInstanceVariablesDirectly {
NSLog(@"%s",__func__);
return [super accessInstanceVariablesDirectly];
}
- (id)valueForKey:(NSString *)key {
NSLog(@"%s",__func__);
return [super valueForKey:key];
}
- (void)setValue:(id)value forKey:(NSString *)key {
NSLog(@"%s",__func__);
return [super setValue:value forKey:key];
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Persion *persion = [[Persion alloc] init];
[persion setValue:@"zyy" forKey:@"_na"];
[persion valueForKey:@"_na"];
NSLog(@"%@",persion.name);
}
@end
输出
2016-10-27 18:11:17.368 KVC[9330:394402] -[Persion setValue:forKey:]
2016-10-27 18:11:17.369 KVC[9330:394402] -[Persion set_na:]
2016-10-27 18:11:17.369 KVC[9330:394402] -[Persion valueForKey:]
2016-10-27 18:11:17.369 KVC[9330:394402] -[Persion _na]
2016-10-27 18:11:17.369 KVC[9330:394402] zyy
输出结果正确。因为persion.name与_na已经关联,而且实现了对应的getter和setter,不会执行+ (BOOL)accessInstanceVariablesDirectly。
思考:
如果在此基础上将@synthesize name = _na改为成员变量_na,KVC还能正常运行吗?
显然不能,因为Xcode不会将persion.name与自定义成员变量_na关联起来,这样KVC就无法匹配到正确的key值
这个问题总算解释清楚了,现在回到第二版本
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Persion *persion = [[Persion alloc] init];
[persion setValue:@"zyy" forKey:@"_name"];
[persion valueForKey:@"_name"];
NSLog(@"%@",persion.name);
}
@end
输出
2016-10-27 18:20:19.576 KVC[9621:406427] -[Persion setValue:forKey:]
2016-10-27 18:20:19.577 KVC[9621:406427] +[Persion accessInstanceVariablesDirectly]
2016-10-27 18:20:19.577 KVC[9621:406427] -[Persion valueForKey:]
2016-10-27 18:20:19.577 KVC[9621:406427] +[Persion accessInstanceVariablesDirectly]
2016-10-27 18:20:19.578 KVC[9621:406427] zyy
分析一下 setValue:(id)value forKey:(NSString *)key 与 accessInstanceVariablesDirectly 的调用顺序究竟是怎样的?
可见,实际顺序是先到- (void)setValue:(id)value forKey:(NSString )key执行super然后跳到+ (BOOL)accessInstanceVariablesDirectly,最后再回到- (void)setValue:(id)value forKey:(NSString )key