kvc 查找顺序 与kvo 本质

kvc

KVC键值查找

搜索单值成员

setValue:forKey:搜索方式

1、首先搜索setKey:方法。(key指成员变量名,首字母大写)

2、上面的setter方法没找到,如果类方法accessInstanceVariablesDirectly返回YES。那么按 _key,_isKey,key,iskey的顺序搜索成员名。(NSKeyValueCodingCatogery中实现的类方法,默认实现为返回YES)

3、如果没有找到成员变量,调用setValue:forUnderfinedKey:

valueForKey:的搜索方式

1、首先按getKey,key,isKey的顺序查找getter方法,找到直接调用。如果是BOOL、int等内建值类型,会做NSNumber的转换。

2、上面的getter没找到,查找countOfKey、objectInKeyAtindex、KeyAtindexes格式的方法。如果countOfKey和另外两个方法中的一个找到,那么就会返回一个可以响应NSArray所有方法的代理集合的NSArray消息方法。

3、还没找到,查找countOfKey、enumeratorOfKey、memberOfKey格式的方法。如果这三个方法都找到,那么就返回一个可以响应NSSet所有方法的代理集合。

4、还是没找到,如果类方法accessInstanceVariablesDirectly返回YES。那么按 _key,_isKey,key,iskey的顺序搜索成员名。

5、再没找到,调用valueForUndefinedKey。

*** 今天主要来分辨一下这几个方法的执行顺序,也许会对你有很大的帮助。

-setter-getter-(void)setValue:forKey:-(id)valueForKey:+(BOOL)accessInstanceVariablesDirectly//参考KVC、KVO探识(二) 

#import@interfacemodel:NSObject@property(nonatomic,copy)NSString*name;@end

model.m

#import "model.h"

@implementationmodel@synthesizename=_name;//如果重写了setter和getter方法,@property就不会生成name属性和_name属性。所以在这里将name和_name,并且手动生成。-(NSString*)name{NSLog(@"%s",__func__);return_name;}-(void)setName:(NSString*)name{    _name=name;NSLog(@"%s",__func__);}+(BOOL)accessInstanceVariablesDirectly{NSLog(@"%s",__func__);return[superaccessInstanceVariablesDirectly];//    return YES;}-(void)setValue:(id)value forKey:(NSString*)key{NSLog(@"%s",__func__);    [supersetValue:value forKey:key];}-(id)valueForKey:(NSString*)key{NSLog(@"%s",__func__);//return nil;return[supervalueForKey:key];}@end

此时在ViewController.m中进行调用:

ViewController.m

#import"ViewController.h"#import"model.h"@interfaceViewController()@end@implementationViewController- (void)viewDidLoad {    [superviewDidLoad];    model *modell=[[model alloc]init];    [modell setValue:@"lxh"forKey:@"name"];    [modell valueForKey:@"name"];NSLog(@"%@",modell.name);}@end

输出结果如下:

2016-03-2815:16:07.449TestKVC[673:12775]-[model setValue:forKey:]2016-03-2815:16:11.640TestKVC[673:12775]-[model setName:]2016-03-2815:16:11.640TestKVC[673:12775]-[model valueForKey:]2016-03-2815:16:11.640TestKVC[673:12775]-[model name]2016-03-2815:16:11.640TestKVC[673:12775]-[model name]2016-03-2815:16:11.641TestKVC[673:12775]lxh

问题:(先思考,后面讲解)

1.为什么没有调用:+(BOOL)accessInstanceVariablesDirectly?

注意:

经过自己打断点测试: [super setValue:value forKey:key];方法中调用了model的setter方法。同理:[super valueForKey:key];方法调用了

model的getter方法。

情况二:

model.h

#import@interfacemodel:NSObject@property(nonatomic,copy)NSString*name;@end

model.m

#import "model.h"

@implementationmodel@synthesizename=_name; -(NSString*)name{NSLog(@"%s",__func__);return_name;}-(void)setName:(NSString*)name{    _name=name;NSLog(@"%s",__func__);}+(BOOL)accessInstanceVariablesDirectly{NSLog(@"%s",__func__);return[superaccessInstanceVariablesDirectly];//    return YES;}-(void)setValue:(id)value forKey:(NSString*)key{NSLog(@"%s",__func__);    [supersetValue:value forKey:key];}-(id)valueForKey:(NSString*)key{NSLog(@"%s",__func__);//return nil;return[supervalueForKey:key];}@end

此时在ViewController.m中进行调用:

ViewController.m

#import"ViewController.h"#import"model.h"@interfaceViewController()@end@implementationViewController- (void)viewDidLoad {    [superviewDidLoad];    model *modell=[[model alloc]init];    [modell setValue:@"lxh"forKey:@"_name"];    [modell valueForKey:@"_name"];NSLog(@"%@",modell.name);}@end

输出结果如下:

2016-03-2815:23:43.665TestKVC[703:15739]-[model setValue:forKey:]2016-03-2815:23:48.416TestKVC[703:15739]+[model accessInstanceVariablesDirectly]2016-03-2815:23:57.708TestKVC[703:15739]-[model valueForKey:]2016-03-2815:23:57.708TestKVC[703:15739]+[model accessInstanceVariablesDirectly]2016-03-2815:23:57.709TestKVC[703:15739]-[model name]2016-03-2815:23:57.709TestKVC[703:15739]lxh

问题:

1.为什么此时没有调用setter、getter方法,而是调用了+[model accessInstanceVariablesDirectly]?

2.为什么第二种情况没有调用setter、getter(现有的)方法,也能输出正确结果?

注意:

经过打断点发现:[super setValue:value forKey:key];方法中调用了+accessInstanceVariablesDirectly这个方法。同理:[super valueForKey:key];方法也调用了+accessInstanceVariablesDirectly。

对比前两种情况,你会感觉很迷惑,但是请坚持住,接着往下看

第三种情况:

model.h

#import@interfacemodel:NSObject@property(nonatomic,copy)NSString*name;@end

model.m

#import "model.h"

@implementationmodel@synthesizename=_name;//如果重写了setter和getter方法,@property就不会生成name属性和_name属性。所以在这里将name和_name,并且手动生成。-(NSString*)name{NSLog(@"%s",__func__);return_name;}-(void)setName:(NSString*)name{    _name=name;NSLog(@"%s",__func__);}+(BOOL)accessInstanceVariablesDirectly{NSLog(@"%s",__func__);return[superaccessInstanceVariablesDirectly];//    return YES;}-(void)setValue:(id)value forKey:(NSString*)key{NSLog(@"%s",__func__);    [supersetValue:value forKey:key];}-(void)set_name:(id)name{    _name=name;NSLog(@"%s",__func__);}-(id)_name{NSLog(@"%s",__func__);return_name;}-(id)valueForKey:(NSString*)key{NSLog(@"%s",__func__);//return nil;return[supervalueForKey:key];}@end

此时在ViewController.m中进行调用:

ViewController.m

#import"ViewController.h"#import"model.h"@interfaceViewController()@end@implementationViewController- (void)viewDidLoad {    [superviewDidLoad];    model *modell=[[model alloc]init];    [modell setValue:@"lxh"forKey:@"_name"];    [modell valueForKey:@"_name"];NSLog(@"%@",modell.name);}@end

输出结果如下:

2016-03-2815:38:40.383TestKVC[793:21114]-[model setValue:forKey:]2016-03-2815:38:42.575TestKVC[793:21114]-[model set_name:]2016-03-2815:38:43.833TestKVC[793:21114]-[model valueForKey:]2016-03-2815:38:43.834TestKVC[793:21114]-[model _name]2016-03-2815:38:43.834TestKVC[793:21114]-[model name]2016-03-2815:38:43.834TestKVC[793:21114]lxh

问题:

1.为什么此时调用了setter、getter方法,但是是set_name和_name?

2.为什么此时model的name依旧已经被赋值过了?

对比前两种情况,你会感觉很迷惑,但是请坚持住,接着往下看

看完第三种情况貌似更疯了,不要着急,马上做分析:

分析(现象分析)

1.对比三种情况你会发现:只要调用了setter、getter方法,就不会去调用:+(BOOL)accessInstanceVariablesDirectly方法,反之也是如此。

2.并且赋值阶段都是有[super setValue:value forKey:key];去实现调用的。取值同样是由[super setValue:value forKey:key];去实现的。

分析(深层次)

[super setValue:value forKey:key]方法内部实现赋值有两种方式:一种是调用setter,一种是直接赋值例如:_name=name;

步骤如下:

首先:①如果是采用name去设置的话,[super setValue:value forKey:key]会去查找是否有对应的setName: 方法,如果有,就调用setName:去进行赋值。

如果没有(例如key:_name)则会调用:+(BOOL)accessInstanceVariablesDirectly方法去进行匹配(注意此时该方法的返回值默认是Yes,只有YES才去匹配name和_name)

作者:Peak_One

链接:https://www.jianshu.com/p/8c5d313e50ac

來源:

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

-----------------------------------------------kvo -----------------


kvc 查找顺序 与kvo 本质_第1张图片

你可能感兴趣的:(kvc 查找顺序 与kvo 本质)