KVC之-(id)valueForKey:(NSString *)key的实现原理与验证

2.-(id)valueForKey:(NSString *)key的实现原理与验证;

#功能:使用一个字符串类型的属性标示符,获取一个属性的值,支持普通对象和NSSet,NSArray集合对象,如果是NSArray对象就则返回不可变数组,如果是NSet就返回不可变NSSet.

这个方法的默认实现如下:


1.查找读访问器(getter)方法中与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

  1. typedef struct {
  2. int day;
  3. int month;
  4. int year;
  5. } Date;
  6. @interface Person : NSObject
  7. {
  8. NSString *_name;
  9. int _age;
  10. Date *birthday;
  11. // NSString *address;
  12. // NSString *_address;
  13. // NSString *isAddress;
  14. NSString *_isAddress;
  15. }
  16. @property (nonatomic, copy) NSString *name;
  17. @property (nonatomic,assign) int age;
  18. @end

Person.m

  1. @implementation Person
  2. - (int) age
  3. {
  4. NSLog(@"%s------%d",__func__,_age);
  5. return _age;
  6. }
  7. - (NSString *) name
  8. {
  9. NSLog(@"%s----------%@",__func__,_name);
  10. return _name;
  11. }
  12. @end

验证

1.查找读访问器(getter)方法中与key相匹配的方法,匹配模式为-get<Key>, -<key>, or -is<Key>,按照这样的顺序如果找到一个方法,就执行它.
修改name的getter方法:

  1. - (NSString *) getName
  2. {
  3. NSLog(@"%s----------%@",__func__,_name);
  4. return _name;
  5. }

输出结果为:

  1. 2015-08-15 23:53:16.771 company[1253:77763] -[Person getName]----------小明

再次修改name的getter方法::

  1. - (NSString *) name
  2. {
  3. NSLog(@"%s----------%@",__func__,_name);
  4. return _name;
  5. }

输出结果

  1. 2015-08-15 23:54:36.060 company[1275:78595] -[Person name]----------小明

再次修改name的getter方法:

  1. - (NSString *) isName
  2. {
  3. NSLog(@"%s----------%@",__func__,_name);
  4. return _name;
  5. }

输出结果,为空,也就是说根本没有执行isName方法
修改name属性的声明为

  1. @property (nonatomic, copy,getter=isName) NSString *name;

输出结果为

  1. 2015-08-15 23:58:59.271 company[1387:81935] -[Person isName]----------小明

2.如果返回结果类型是一个对象的类型的指针就简单返回,如果返回结果类型是可以通过NSNumber转换支持的原始类型,先把转换为NSNumber然后在返回,否则就转换为一个NSValue对象返回.

测试代码

  1. p.name = @"小明";
  2. NSString *name = [p valueForKey:@"name"];
  3. NSLog(@"%@",[name class]);
  4. NSLog(@"%@",name);
  5. p.age = 10;
  6. NSNumber *age = [p valueForKey:@"age"];
  7. NSLog(@"%@",[age class]);
  8. NSLog(@"%@",age);
  9. p.birthday = (Date){1990,5,1};
  10. NSValue *birthday = [p valueForKey:@"birthday"];
  11. NSLog(@"%@",[birthday class]);
  12. NSLog(@"%@",birthday);

输出结果

  1. 2015-08-16 00:03:03.420 company[1440:84446] -[Person isName]----------小明
  2. 2015-08-16 00:03:03.421 company[1440:84446] __NSCFConstantString
  3. 2015-08-16 00:03:03.421 company[1440:84446] 小明
  4. 2015-08-16 00:03:03.421 company[1440:84446] -[Person age]------10
  5. 2015-08-16 00:03:03.421 company[1440:84446] __NSCFNumber
  6. 2015-08-16 00:03:03.421 company[1440:84446] 10
  7. 2015-08-16 00:03:03.421 company[1440:84446] NSConcreteValue
  8. 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: 当为获取最佳性能则使用该方法

此处我使用系统自带的类进行验证

测试代码:
  1. Person *p = [[Person alloc] init];
  2. p.name = @"小明";
  3. Person *p1 = [[Person alloc] init];
  4. [p1 setValue:@"小明" forKey:@"name"];
  5. Person *p2 = [[Person alloc] init];
  6. [p2 setValue:@"小花" forKey:@"name"];
  7. Person *p3 = [[Person alloc] init];
  8. [p3 setValue:@"happy" forKey:@"name"];
  9. NSOrderedSet *set = [[NSOrderedSet alloc] initWithObjects:p1,p2,p3, nil];
  10. NSOrderedSet *names = [set valueForKey:@"name"];
  11. NSLog(@"%@",[names class]);
  12. NSLog(@"%@",names);

输出结果:

  1. 2015-08-16 00:35:39.134 company[1687:109689] -[Person isName]----------小明
  2. 2015-08-16 00:35:39.134 company[1687:109689] -[Person isName]----------小花
  3. 2015-08-16 00:35:39.134 company[1687:109689] -[Person isName]----------happy
  4. 2015-08-16 00:35:39.134 company[1687:109689] __NSOrderedSetI
  5. 2015-08-16 00:35:39.134 company[1687:109689] {(
  6. "\U5c0f\U660e",
  7. "\U5c0f\U82b1",
  8. happy
  9. )}

这里的names 类为一个能够响应NSOrderedSet所有方法的的代理对象,请注意这里所说的代理对象,与代理设计模式不是同一个概念,
这里代理就相当于中介,比如你想租某个房东租房子,但是你找不到这个房东,只能通过房产中介,传递信息给房东,这个中介
就可以理解为代理对象


4.否则(1,2,3都没有满足[没有简单的getter方法,没有有序Set的访问方法也没有数组的访问方法]),查找接收者对象的类中查找下面三个方法的组合,他们名字匹配模式为-countOf<Key>,-enumeratorOf<Key>, and -memberOf<Key>: (对应定义在NSSet类中一个私有方法).如果上面三个方法都找到了,组合使用-countOf<Key>, -enumeratorOf<Key>, -memberOf<Key>:这三个方法,给原始对象发送-valueForKey:消息,把返回结果放入一个能够响应NSSet的所有方法的代理对象中,然后返回这个代理对象

测试代码:
  1. Person *p = [[Person alloc] init];
  2. p.name = @"小明";
  3. Person *p1 = [[Person alloc] init];
  4. [p1 setValue:@"小明" forKey:@"name"];
  5. Person *p2 = [[Person alloc] init];
  6. [p2 setValue:@"小花" forKey:@"name"];
  7. Person *p3 = [[Person alloc] init];
  8. [p3 setValue:@"happy" forKey:@"name"];
  9. NSArray *set = [[NSArray alloc] initWithObjects:p1,p2,p3, nil];
  10. NSArray *names = [set valueForKey:@"name"];
  11. NSLog(@"%@",[names class]);
  12. NSLog(@"%@",names);

输出结果:

  1. 2015-08-16 00:33:21.741 company[1670:108148] -[Person isName]----------小明
  2. 2015-08-16 00:33:21.741 company[1670:108148] -[Person isName]----------小花
  3. 2015-08-16 00:33:21.741 company[1670:108148] -[Person isName]----------happy
  4. 2015-08-16 00:33:21.741 company[1670:108148] __NSArrayI
  5. 2015-08-16 00:33:21.741 company[1670:108148] (
  6. "\U5c0f\U660e",
  7. "\U5c0f\U82b1",
  8. happy
  9. )

这里的names 类为一个能够响应NSArray所有方法的的代理对象


4.否则(1,2,3都没有满足[没有简单的getter方法,没有有序Set的访问方法也没有数组的访问方法]),查找接收者对象的类中查找下面三个方法的组合,他们名字匹配模式为-countOf<Key>,-enumeratorOf<Key>, and -memberOf<Key>: (对应定义在NSSet类中一个私有方法).如果上面三个方法都找到了,组合使用-countOf<Key>, -enumeratorOf<Key>, -memberOf<Key>:这三个方法,给原始对象发送-valueForKey:消息,把返回结果放入一个能够响应NSSet的所有方法的代理对象中,然后返回这个代理对象

测试代码:
  1. Person *p1 = [[Person alloc] init];
  2. [p1 setValue:@"小明" forKey:@"name"];
  3. Person *p2 = [[Person alloc] init];
  4. [p2 setValue:@"小花" forKey:@"name"];
  5. Person *p3 = [[Person alloc] init];
  6. [p3 setValue:@"happy" forKey:@"name"];
  7. NSSet *set = [[NSSet alloc] initWithObjects:p1,p2,p3, nil];
  8. NSSet *names = [set valueForKey:@"name"];
  9. NSLog(@"%@",[names class]);
  10. NSLog(@"%@",names);

输出结果:

  1. 2015-08-16 00:29:50.879 company[1646:106351] -[Person isName]----------happy
  2. 2015-08-16 00:29:50.879 company[1646:106351] -[Person isName]----------小花
  3. 2015-08-16 00:29:50.879 company[1646:106351] -[Person isName]----------小明
  4. 2015-08-16 00:29:50.879 company[1646:106351] __NSSetI
  5. 2015-08-16 00:29:50.879 company[1646:106351] {(
  6. happy,
  7. "\U5c0f\U660e",
  8. "\U5c0f\U82b1"
  9. )}

这里的names 类为一个能够响应NSSet所有方法的的代理对象


5.否则(1,2,3,4都没有满足[没有简单的访问器方法也没有集合的访问器方法]),如果接受器类的 +accessInstanceVariablesDirectly 方法返回YES,依照匹配模式 _<key>, _is<Key>, <key>, or is<Key> 去匹配实例变量名称,如果有一个实例变量找到了,就返回这个实例变量的值,如果结果类型是一个对象的类型的指针就简单返回,如果返回结果类型是可以通过NSNumber转换支持的原始类型,先把转换为NSNumber然后在返回,否则就转换为一个NSValue对象返回.

Person类中,分别使用下面address进行测试

  1. @interface Person : NSObject
  2. {
  3. // NSString *address;
  4. // NSString *_address;
  5. // NSString *isAddress;
  6. NSString *_isAddress;
  7. }

测试代码

  1. Person *p = [[Person alloc] init];
  2. [p setValue:@"金燕龙大厦" forKey:@"address"];
  3. NSString *address = [p valueForKey:@"address"];
  4. NSLog(@"%@",address);

输出结果均为

  1. 2015-08-16 00:41:16.387 company[1776:113187] 金燕龙大厦

6.否则(1,2,3,4,5都没有满足[没有简单的访问器方法,没有集合访问方法,没有实例变量]),那么将会执行-valueForUndefinedKey:方法,默认产生一个NSUndefinedKeyException的异常,但是你可以重写该方法.

  1. NSString *工具 = [p valueForKey:@"工具"];
  2. NSLog(@"工具=%@",工具);

运行结果: 产生一个NSUnknownKeyException的异常

  1. 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: 方法

  1. - (id)valueForUndefinedKey:(NSString *)key
  2. {
  3. NSLog(@"%s",__func__);
  4. NSLog(@"key == %@",key);
  5. return @"黄瓜";
  6. }

再次运行程序输出结果为:

  1. 2015-08-16 00:53:01.568 company[1841:119255] -[Person valueForUndefinedKey:]
  2. 2015-08-16 00:53:01.568 company[1841:119255] key == 工具
  3. 2015-08-16 00:53:01.568 company[1841:119255] 工具=黄瓜

KVC之-(id)valueForKey:(NSString *)key的实现原理与验证的更多相关文章

  1. KVC之-setValue:forKey:方法实现原理与验证

    KVC之-setValue:forKey:方法实现原理与验证 - (void)setValue:(id)value forKey:(NSString *)key方法,实现原理与验证 功能:使用一个字符 ...

  2. KVC和KVO的理解(底层实现原理)

    1.KVC,即是指 NSKeyValueCoding,一个非正式的Protocol,提供一种机制来间接访问对象的属性.而不是通过调用Setter.Getter方法访问.KVO 就是基于 KVC 实现的 ...

  3. KVC与Runtime结合使用(案例)及其底层原理

    一.KVC 的用法和实践 用法 KVC(Key-value coding)键值编码,顾名思义.额,简单来说,是可以通过对象属性名称(Key)直接给属性值(value)编码(coding)“编码”可以理 ...

  4. - (BOOL)setResourceValue:(id)value forKey:(NSString *)key error:(NSError **)error

    如果我们的APP需要存放比较大的文件的时候,同时又不希望被系统清理掉,那我么我们就需要把我们的资源保存在Documents目录下,但是我们又不希望他会被iCloud备份,因此就有了这个方法 [URL ...

  5. - (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key; 方法浅析

    转载自:http://blog.csdn.net/ronaldo_carry/article/details/49070119 将viewdidload里面的代码全部注释掉 - (void)viewD ...

  6. ehcache object key的实现原理

    这几天为了设计缓存机制,查阅了非常多缓存方面的资料,作为没有实战经验的小白自然被各种性能报告.内存机制.集群方式搞得一头雾水. 但查了这些资料后.对各个cache的特点有了感性的了解. ehcache ...

  7. 虚拟dom?diff算法?key?Vue原理的核心三问?打包教你搞定。

    为什么需要虚拟DOM 先介绍浏览器加载一个HTML文件需要做哪些事,帮助我们理解为什么我们需要虚拟DOM.webkit引擎的处理流程,如下图所示: 所有浏览器的引擎工作流程都差不多,如上图大致分5步: ...

  8. xcode KVC:Key Value Coding 键值编码

    赋值 // 能修改私有成员变量 - (void)setValue:(id)value forKey:(NSString *)key; - (void)setValue:(id)value forKey ...

  9. KVC 和 KVO

    KVC 键值编码    全称是Key-value coding,翻译成键值编码.它提供了一种使用字符串而不是访问器方法去访问一个对象实例变量的机制.        1.通过key(成员变量的名称)设置 ...

随机推荐

  1. 五、Springboot 之 自定义配置文件及读取配置文件

    说明:建议所有的类应该与spring-boot启动程序同级,不如扫描不到 1.核心配置文件是指在resources根目录下的application.properties或application.yml ...

  2. 高性能WEB开发:Javascript自身执行效率

    Javascript中的作用域链.闭包.原型继承.eval等特性,在提供各种神奇功能的同时也带来了各种效率问题,用之不慎就会导致执行效率低下. 1.全局导入 我们在编码过程中多多少少会使用到一些全局变 ...

  3. EffectiveJava(30) -- 全面解析enum类型

    --在大多数项目中,我们会经常使用int类型来声明final类型的常量,它在不考虑安全的情况下确实能满足我们绝大多数的需求.但是在JDK1.5版本发布之后,声明一组固定的常量组成合法值的类型就建议使用 ...

  4. 利用内存分析工具(Memory Analyzer Tool,MAT)分析java项目内存泄露

    转载:http://blog.csdn.net/wanghuiqi2008/article/details/50724676 一.开发环境: 操作系统:ubuntu 14.04 IDE:Eclipse ...

  5. Node.js monly图片批量下载爬虫1.00

    此爬虫又用到了iconv转码,代码如下: //====================================================== // mmonly图片批量下载爬虫1.00 ...

  6. 2013年八月GBin1月刊

    2013年八月GBin1月刊 推荐十款来自极客标签的超棒前端特效[第十二期] 本周,我们带来了极客社区推荐的10款前端特效,仍然是非常有趣的小动态效果的页面生成.喜欢的可以直接将我们的在线调试代码插入 ...

  7. Office 如何下载网页的视频 JWPlayer的内嵌视频

    右击页面空白处,查看页面源代码 在里面搜索mp4或者swf,video,一般网页中的视频都是这些格式,仔细找一定能找到对应的地址 然后复制到迅雷下载即可

  8. asp 按钮 调用ajax时 会出现返回错误,尽量使用html按钮进行调用

    asp 按钮 调用ajax时 会出现返回错误,尽量使用html按钮进行调用

  9. Android中的线程池概述

    线程池 Android里面,耗时的网络操作,都会开子线程,在程序里面直接开过多的线程会消耗过多的资源,在众多的开源框架中也总能看到线程池的踪影,所以线程池是必须要会把握的一个知识点; 线程运行机制 开 ...

  10. ES6 set 应用场景

    1.数组去重 let arr = [3, 5, 2, 2, 5, 5]; let unique = [...new Set(arr)]; // [3, 5, 2] 2.并集(Union).交集(Int ...