美文网首页
kvc 查找顺序 与kvo 本质

kvc 查找顺序 与kvo 本质

作者: 过眼云烟1102 | 来源:发表于2018-05-04 18:53 被阅读59次

    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 本质

          本文链接:https://www.haomeiwen.com/subject/cxkjrftx.html