KVC之-(id)valueForKey:(NSString *)key的实现原理与验证
KVC之-(id)valueForKey:(NSString *)key的实现原理与验证
2.-(id)valueForKey:(NSString *)key
的实现原理与验证;
#功能:使用一个字符串类型的属性标示符,获取一个属性的值,支持普通对象和NSSet,NSArray
集合对象,如果是NSArray
对象就则返回不可变数组,如果是NSet
就返回不可变NSSet
.
这个方法的默认实现如下:
1.查找读访问器(gette
r)方法中与key
相匹配的方法,匹配模式为-get<Key>
, -<key>
或-is<Key>
,按照这样的顺序如果找到一个方法,就执行它.如果返回结果类型是一个对象的类型的指针就简单返回,如果返回结果类型是可以通过NSNumber
转换支持的原始类型,先把转换为NSNumber
然后在返回,否则就转换为一个NSValue
对象返回.
2.否则(简单的getter
方法没有找到),那么就查找接受者的类中与 -countOf<Key>
and -indexIn<Key>OfObject:
and -objectIn<Key>AtIndex:
(NSSet类中定义的私有方法) and also -<key>AtIndexes:
(对应与 -[NSOrderedSet objectsAtIndexes:]). 如果找到了-countOf<Key>
, -indexIn<Key>OfObject:
以及这两个 -objectIn<Key>AtIndex:
, -<key>AtIndexes:
方法中一个,那么将组合使用 -countOf<Key>
, -indexIn<Key>OfObject:
, -objectIn<Key>AtIndex:
, and -<key>AtIndexes:
获取集合中的原始对象,然后给原始对象发送-valueForKey:
消息,把结果存放入一个能够相应NSOrderedSet
所有方法的集合代理对象
中,返回这个代理对象
.如果接收者类中实现了 -get<Key>:range:
当为获取最佳性能则使用该方法
3.否则(1和2都没有满足),那么就接收者类中寻找名字与-countOf<Key>
and -objectIn<Key>AtIndex:
(定义在 NSArray
类中的私有方法) and -<key>AtIndexes:
(对应与 -[NSArray objectsAtIndexes:
]). 如果找到了-countOf<Key>
方法 和 -objectIn<Key>AtIndex:
(定义在 NSArray
类中的私有方法) 与 -<key>AtIndexes:
中的其中一个,那么,通过 -countOf<Key>
, -objectIn<Key>AtIndex:
, and -<key>AtIndexes:
给原始对象发送 -valueForKey:
消息,把返回结果放入一个能够响应NSArray
所有方法的代理对象
中,然后返回该代理对象
.如果接收器的类对象实现,一个名字与-get<Key>:range:
.匹配可选方法,那么为了最好性能讲使用该方法.
4.否则(1,2,3都没有满足[没有简单的getter方法,没有有序Set的访问方法也没有数组的访问方法]),查找接收者对象的类中查找下面三个方法的组合,他们名字匹配模式为-countOf<Key>
,-enumeratorOf<Key>
, and -memberOf<Key>:
(对应定义在NSSet类中一个私有方法).如果上面三个方法都找到了,组合使用-countOf<Key>, -enumeratorOf<Key>, -memberOf<Key>:
这三个方法,给原始对象发送-valueForKey:
消息,把返回结果放入一个能够响应NSSet的所有方法的代理对象
中,然后返回这个代理对象
5.否则(1,2,3,4都没有满足[没有简单的访问器方法也没有集合的访问器方法]),如果接受器类的 +accessInstanceVariablesDirectly
方法返回YES
,依照匹配模式 _<key>, _is<Key>, <key>, or is<Key>
去匹配实例变量名称,如果有一个实例变量找到了,就返回这个实例变量的值,如果结果类型是一个对象的类型的指针就简单返回,如果返回结果类型是可以通过NSNumber
转换支持的原始类型,先把转换为NSNumber
然后在返回,否则就转换为一个NSValue
对象返回.
6.否则(1,2,3,4,5都没有满足[没有简单的访问器方法,没有集合访问方法,没有实例变量]),那么将会执行-valueForUndefinedKey:
方法,默认产生一个NSUndefinedKeyException
的异常,但是你可以重写该方法.
验证
Person.h
typedef struct {
int day;
int month;
int year;
} Date;
@interface Person : NSObject
{
NSString *_name;
int _age;
Date *birthday;
// NSString *address;
// NSString *_address;
// NSString *isAddress;
NSString *_isAddress;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic,assign) int age;
@end
Person.m
@implementation Person
- (int) age
{
NSLog(@"%s------%d",__func__,_age);
return _age;
}
- (NSString *) name
{
NSLog(@"%s----------%@",__func__,_name);
return _name;
}
@end
验证
1.查找读访问器(gette
r)方法中与key
相匹配的方法,匹配模式为-get<Key>
, -<key>
, or -is<Key>
,按照这样的顺序如果找到一个方法,就执行它.
修改name的getter方法:
- (NSString *) getName
{
NSLog(@"%s----------%@",__func__,_name);
return _name;
}
输出结果为:
2015-08-15 23:53:16.771 company[1253:77763] -[Person getName]----------小明
再次修改name的getter方法::
- (NSString *) name
{
NSLog(@"%s----------%@",__func__,_name);
return _name;
}
输出结果
2015-08-15 23:54:36.060 company[1275:78595] -[Person name]----------小明
再次修改name的getter方法:
- (NSString *) isName
{
NSLog(@"%s----------%@",__func__,_name);
return _name;
}
输出结果,为空,也就是说根本没有执行isName方法
修改name属性的声明为
@property (nonatomic, copy,getter=isName) NSString *name;
输出结果为
2015-08-15 23:58:59.271 company[1387:81935] -[Person isName]----------小明
2.如果返回结果类型是一个对象的类型的指针就简单返回,如果返回结果类型是可以通过NSNumber
转换支持的原始类型,先把转换为NSNumber
然后在返回,否则就转换为一个NSValue
对象返回.
测试代码
p.name = @"小明";
NSString *name = [p valueForKey:@"name"];
NSLog(@"%@",[name class]);
NSLog(@"%@",name);
p.age = 10;
NSNumber *age = [p valueForKey:@"age"];
NSLog(@"%@",[age class]);
NSLog(@"%@",age);
p.birthday = (Date){1990,5,1};
NSValue *birthday = [p valueForKey:@"birthday"];
NSLog(@"%@",[birthday class]);
NSLog(@"%@",birthday);
输出结果
2015-08-16 00:03:03.420 company[1440:84446] -[Person isName]----------小明
2015-08-16 00:03:03.421 company[1440:84446] __NSCFConstantString
2015-08-16 00:03:03.421 company[1440:84446] 小明
2015-08-16 00:03:03.421 company[1440:84446] -[Person age]------10
2015-08-16 00:03:03.421 company[1440:84446] __NSCFNumber
2015-08-16 00:03:03.421 company[1440:84446] 10
2015-08-16 00:03:03.421 company[1440:84446] NSConcreteValue
2015-08-16 00:03:03.421 company[1440:84446] <c6070000 05000000 01000000>
3.否则(简单的getter
方法没有找到),那么就查找接受者的类中与 -countOf<Key>
and -indexIn<Key>OfObject:
and -objectIn<Key>AtIndex:
(NSSet类中定义的私有方法) and also -<key>AtIndexes:
(对应与 -[NSOrderedSet objectsAtIndexes:]). 如果找到了-countOf<Key>
, -indexIn<Key>OfObject:
以及这两个 -objectIn<Key>AtIndex:
, -<key>AtIndexes:
方法中一个,那么将组合使用 -countOf<Key>
, -indexIn<Key>OfObject:
, -objectIn<Key>AtIndex:
, and -<key>AtIndexes:
获取集合中的原始对象,然后给原始对象发送-valueForKey:
消息,把结果存放入一个能够相应NSOrderedSet
所有方法的集合代理对象
中,返回这个代理对象
.如果接收者类中实现了 -get<Key>:range:
当为获取最佳性能则使用该方法
此处我使用系统自带的类进行验证
测试代码:
Person *p = [[Person alloc] init];
p.name = @"小明";
Person *p1 = [[Person alloc] init];
[p1 setValue:@"小明" forKey:@"name"];
Person *p2 = [[Person alloc] init];
[p2 setValue:@"小花" forKey:@"name"];
Person *p3 = [[Person alloc] init];
[p3 setValue:@"happy" forKey:@"name"];
NSOrderedSet *set = [[NSOrderedSet alloc] initWithObjects:p1,p2,p3, nil];
NSOrderedSet *names = [set valueForKey:@"name"];
NSLog(@"%@",[names class]);
NSLog(@"%@",names);
输出结果:
2015-08-16 00:35:39.134 company[1687:109689] -[Person isName]----------小明
2015-08-16 00:35:39.134 company[1687:109689] -[Person isName]----------小花
2015-08-16 00:35:39.134 company[1687:109689] -[Person isName]----------happy
2015-08-16 00:35:39.134 company[1687:109689] __NSOrderedSetI
2015-08-16 00:35:39.134 company[1687:109689] {(
"\U5c0f\U660e",
"\U5c0f\U82b1",
happy
)}
这里的names
类为一个能够响应NSOrderedSet
所有方法的的代理对象
,请注意这里所说的代理对象
,与代理设计模式
不是同一个概念,
这里代理就相当于中介,比如你想租某个房东租房子,但是你找不到这个房东,只能通过房产中介,传递信息给房东,这个中介
就可以理解为代理对象
4.否则(1,2,3都没有满足[没有简单的getter方法,没有有序Set的访问方法也没有数组的访问方法]),查找接收者对象的类中查找下面三个方法的组合,他们名字匹配模式为-countOf<Key>
,-enumeratorOf<Key>
, and -memberOf<Key>:
(对应定义在NSSet类中一个私有方法).如果上面三个方法都找到了,组合使用-countOf<Key>, -enumeratorOf<Key>, -memberOf<Key>:
这三个方法,给原始对象发送-valueForKey:
消息,把返回结果放入一个能够响应NSSet的所有方法的代理对象
中,然后返回这个代理对象
测试代码:
Person *p = [[Person alloc] init];
p.name = @"小明";
Person *p1 = [[Person alloc] init];
[p1 setValue:@"小明" forKey:@"name"];
Person *p2 = [[Person alloc] init];
[p2 setValue:@"小花" forKey:@"name"];
Person *p3 = [[Person alloc] init];
[p3 setValue:@"happy" forKey:@"name"];
NSArray *set = [[NSArray alloc] initWithObjects:p1,p2,p3, nil];
NSArray *names = [set valueForKey:@"name"];
NSLog(@"%@",[names class]);
NSLog(@"%@",names);
输出结果:
2015-08-16 00:33:21.741 company[1670:108148] -[Person isName]----------小明
2015-08-16 00:33:21.741 company[1670:108148] -[Person isName]----------小花
2015-08-16 00:33:21.741 company[1670:108148] -[Person isName]----------happy
2015-08-16 00:33:21.741 company[1670:108148] __NSArrayI
2015-08-16 00:33:21.741 company[1670:108148] (
"\U5c0f\U660e",
"\U5c0f\U82b1",
happy
)
这里的names
类为一个能够响应NSArray
所有方法的的代理对象
4.否则(1,2,3都没有满足[没有简单的getter方法,没有有序Set的访问方法也没有数组的访问方法]),查找接收者对象的类中查找下面三个方法的组合,他们名字匹配模式为-countOf<Key>
,-enumeratorOf<Key>
, and -memberOf<Key>:
(对应定义在NSSet类中一个私有方法).如果上面三个方法都找到了,组合使用-countOf<Key>, -enumeratorOf<Key>, -memberOf<Key>:
这三个方法,给原始对象发送-valueForKey:
消息,把返回结果放入一个能够响应NSSet的所有方法的代理对象
中,然后返回这个代理对象
测试代码:
Person *p1 = [[Person alloc] init];
[p1 setValue:@"小明" forKey:@"name"];
Person *p2 = [[Person alloc] init];
[p2 setValue:@"小花" forKey:@"name"];
Person *p3 = [[Person alloc] init];
[p3 setValue:@"happy" forKey:@"name"];
NSSet *set = [[NSSet alloc] initWithObjects:p1,p2,p3, nil];
NSSet *names = [set valueForKey:@"name"];
NSLog(@"%@",[names class]);
NSLog(@"%@",names);
输出结果:
2015-08-16 00:29:50.879 company[1646:106351] -[Person isName]----------happy
2015-08-16 00:29:50.879 company[1646:106351] -[Person isName]----------小花
2015-08-16 00:29:50.879 company[1646:106351] -[Person isName]----------小明
2015-08-16 00:29:50.879 company[1646:106351] __NSSetI
2015-08-16 00:29:50.879 company[1646:106351] {(
happy,
"\U5c0f\U660e",
"\U5c0f\U82b1"
)}
这里的names
类为一个能够响应NSSet
所有方法的的代理对象
5.否则(1,2,3,4都没有满足[没有简单的访问器方法也没有集合的访问器方法]),如果接受器类的 +accessInstanceVariablesDirectly
方法返回YES
,依照匹配模式 _<key>, _is<Key>, <key>, or is<Key>
去匹配实例变量名称,如果有一个实例变量找到了,就返回这个实例变量的值,如果结果类型是一个对象的类型的指针就简单返回,如果返回结果类型是可以通过NSNumber
转换支持的原始类型,先把转换为NSNumber
然后在返回,否则就转换为一个NSValue
对象返回.
Person类中,分别使用下面address进行测试
@interface Person : NSObject
{
// NSString *address;
// NSString *_address;
// NSString *isAddress;
NSString *_isAddress;
}
测试代码
Person *p = [[Person alloc] init];
[p setValue:@"金燕龙大厦" forKey:@"address"];
NSString *address = [p valueForKey:@"address"];
NSLog(@"%@",address);
输出结果均为
2015-08-16 00:41:16.387 company[1776:113187] 金燕龙大厦
6.否则(1,2,3,4,5都没有满足[没有简单的访问器方法,没有集合访问方法,没有实例变量]),那么将会执行-valueForUndefinedKey:
方法,默认产生一个NSUndefinedKeyException
的异常,但是你可以重写该方法.
NSString *工具 = [p valueForKey:@"工具"];
NSLog(@"工具=%@",工具);
运行结果: 产生一个NSUnknownKeyException的异常
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Person 0x7fb2b2727b50> valueForUndefinedKey:]: this class is not key value coding-compliant for the key 工具.'
在Person.m文件中重写-valueForUndefinedKey: 方法
- (id)valueForUndefinedKey:(NSString *)key
{
NSLog(@"%s",__func__);
NSLog(@"key == %@",key);
return @"黄瓜";
}
再次运行程序输出结果为:
2015-08-16 00:53:01.568 company[1841:119255] -[Person valueForUndefinedKey:]
2015-08-16 00:53:01.568 company[1841:119255] key == 工具
2015-08-16 00:53:01.568 company[1841:119255] 工具=黄瓜
KVC之-(id)valueForKey:(NSString *)key的实现原理与验证的更多相关文章
- KVC之-setValue:forKey:方法实现原理与验证
KVC之-setValue:forKey:方法实现原理与验证 - (void)setValue:(id)value forKey:(NSString *)key方法,实现原理与验证 功能:使用一个字符 ...
- KVC和KVO的理解(底层实现原理)
1.KVC,即是指 NSKeyValueCoding,一个非正式的Protocol,提供一种机制来间接访问对象的属性.而不是通过调用Setter.Getter方法访问.KVO 就是基于 KVC 实现的 ...
- KVC与Runtime结合使用(案例)及其底层原理
一.KVC 的用法和实践 用法 KVC(Key-value coding)键值编码,顾名思义.额,简单来说,是可以通过对象属性名称(Key)直接给属性值(value)编码(coding)“编码”可以理 ...
- - (BOOL)setResourceValue:(id)value forKey:(NSString *)key error:(NSError **)error
如果我们的APP需要存放比较大的文件的时候,同时又不希望被系统清理掉,那我么我们就需要把我们的资源保存在Documents目录下,但是我们又不希望他会被iCloud备份,因此就有了这个方法 [URL ...
- - (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key; 方法浅析
转载自:http://blog.csdn.net/ronaldo_carry/article/details/49070119 将viewdidload里面的代码全部注释掉 - (void)viewD ...
- ehcache object key的实现原理
这几天为了设计缓存机制,查阅了非常多缓存方面的资料,作为没有实战经验的小白自然被各种性能报告.内存机制.集群方式搞得一头雾水. 但查了这些资料后.对各个cache的特点有了感性的了解. ehcache ...
- 虚拟dom?diff算法?key?Vue原理的核心三问?打包教你搞定。
为什么需要虚拟DOM 先介绍浏览器加载一个HTML文件需要做哪些事,帮助我们理解为什么我们需要虚拟DOM.webkit引擎的处理流程,如下图所示: 所有浏览器的引擎工作流程都差不多,如上图大致分5步: ...
- xcode KVC:Key Value Coding 键值编码
赋值 // 能修改私有成员变量 - (void)setValue:(id)value forKey:(NSString *)key; - (void)setValue:(id)value forKey ...
- KVC 和 KVO
KVC 键值编码 全称是Key-value coding,翻译成键值编码.它提供了一种使用字符串而不是访问器方法去访问一个对象实例变量的机制. 1.通过key(成员变量的名称)设置 ...
随机推荐
- mydate97的时间格式设置
<script type="text/javascript" src="./WdatePicker.js"></script> < ...
- [GLSL]着色器周记02——火焰特效 【转】
http://www.cnblogs.com/tkgamegroup/p/4214081.html 这周学了好多.包括伪随机数.柏林噪声.先说伪随机数.伪随机数我们用的是周期函数而不是那种由前一项乘一 ...
- 用户手册是Yasm汇编
本文档的用户手册是Yasm汇编. 它是介绍和通用所有Yasm用户参考. 英文的参考:http://www.cnblogs.com/coryxie/p/3959888.html 1 .介绍 Yasm b ...
- DotNetBar ComboBoxEx
DotNetBar ComboBoxEx using System; using System.ComponentModel; using System.Drawing; using System.R ...
- CEdit自动换行和状态栏添加
CEdit自动换行在对话框的属性中是可以直接设置的. Auto HScroll设置为False Auto VScroll设置为True Mulitline设置为True Want Return设置为T ...
- PHP的抽象类、接口的区别和选择[转载]
区别: 1.对接口的使用是通过关键字implements.对抽象类的使用是通过关键字extends.当然接口也可以通过关键字extends继承. 2.接口中不可以声明成员变量(包括类静态变量),但是可 ...
- C#秘密武器之多线程——基础
多线程概述 什么是进程? 当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源.而一个进程又是由多个线程所组成的. 什么是线程? 线程是程序中的一个执行流,每个线程 ...
- js中,{}初始化数据类型object;for in 的用法;delete的用法
var choices = {}; //此数据表示的是:object{} for(var i=0;i<10;i++){ choices[i+1] = [data[i].testPlan,test ...
- 使用python语言编写脚本控制freeswitch总结
1. 在Linux环境下已经安装了freeswitch,(没安装freeswitch,请安装说明文档) 2. 进入源代码目录 cd libs/esl 目录下 首先安装 yum install p ...
- 如何利用ps去批量完成一套任务
作为前端开发人员,不说设计你会不会,ps作为一个工具来说,前端开发人员还是要熟悉才行的 做了一个项目,客户自己上传了图片,他表示上传非常慢,我们表示不解,为何那么慢,网络问题吗,经过看了她的图片,发现 ...