KVC/KVO 进阶(二)KVC方法执行顺序

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

Created with Raphaël 2.1.0 - (void)setValue:(id)value forKey:(NSString *)key 执行super 跳到 +(BOOL)accessInstanceVariablesDirectly - (void)setValue:(id)value forKey:(NSString *)key

猜你喜欢

转载自blog.csdn.net/u014641631/article/details/52948330
今日推荐