Objective-C基础教程学习笔记(十六)键/值编码
来源:互联网 发布:吉他扒谱软件 编辑:程序博客网 时间:2024/05/16 10:04
现在回过头来看一下间接机制。许多编程技术都基于间接机制,包括整个面向对象编程领域。本章将介绍另一种间接机制,这种机制不属于Objective-C语言的特性,而是Cocoa提供的一种特性。
到目前为止,我们已经介绍了通过直接调用方法、属性的点表示法或设置实例变量来直接更改对象状态。许多人将键/值编码亲切地称为KVC,它是一种间接更改对象状态的方式,其实现方法是使用字符串描述要更改的对象状态部分。
一些更高级的Cocoa特性,例如Core Data 和Cocoa Bindings,在基础机制中包含了KVC。
KVC简介
键/值编码中的基本调用包括-valueForKey:和-setValue:forKey:。以字符串的形式向对象发送消息,这个字符串是我们关注的属性的关键。
因此,我们可以请求car的名称:
NSString *name = [car valueForKey:@”name”];
这行代码可以获得name值。
valueForKey:首先查找以键-key或-isKey命名的getter方法。对于 这两种调用,valueForKey:查找-name。如果不存在getter方法,它将在对象内部 查找名为_key或key的实例变量。如果我们没有通过@synthesize提供存取方法,valueForKey将会查找实例变量_name和name。
最后一点非常重要:-valueForKey在Objective-C运行中使用元数据打开对象并进入其中查找需要的信息。在C或C++语言中不能执行这种操作。通过使用KVC,可以获取不存在getter方法的对象值,无需通过对象指针直接访问实例变量。
对于 KVC,Cocoa自动放入和取出标量值。也就是说,当使用setValueForKey时,它自动将标量值(int、float和struct)放入NSNumber或NSValue中;当使用-setValueForKey时它自动将标量值从这些对象中取出。仅KVC具有这种自动包装功能。常规方法调用和属性语法不具备该功能。
除用于检索值外,还可以使用-setValue:forKey:按名称设置值:
[car setValue:@”Harold” forKey:@”name”];
这个方法的工作方式和-valueForKey:相同。它首先查找name的setter方法,例如-setName,并使用参数@”Harold”调用它。如果不存在setter方法,它将在类中查找名为name或_name的实例变量,然后为它赋值。
编译器和苹果公司都以下划线开头的形式保存实例变量名,如果你尝试在其它地方使用下划线,可能会出现严重的错误。这条规则实际上不是强制的,但如果不遵循它,你可能会遇到某种风险。
如果在调用-setValue:forKey:之前设置一个标量值,你需要将它包装起来:
[car setValue:[NSNumber numberWithFloat:2500.4] forKey:@”mileage”];
路径
除了通过键设置值外,键/值编码还支持指定键路径,像文件系统路径一样,你可以遵循一系列关系 来指定该路径。
为了更深入了解这项功能,不妨加大引擎的马力。向Engine添加一个新的实例变量:
@interface Engine:NSObject<NSCopying>{
int horsepower;
}
@end // Engine
我们没有添加任何存取方法或特性。通过,我们希望为关注的对象属性使用存取方法或特性。但这里我们避免使用它们,以便真实地展示KVC直接深入到对象中的功能。
为了以非零马力启动引擎,我们添加一个init方法:
-(id) init{
if(self = [super init]){
horsepower = 145;
}
return self;
} //init
以下代码:
NSLog(@“horsepower is %@”,[engine valueForKey:@”horsepower”]);
[engine setValue:[NSNumber numberWithInt:150] forKey:@”horsepower”];
NSLog(@”horsepower is %@”,[engine valueForKey:@”horsepower”]);
输出:
horsepower is 145
horsepower is 150
如果表示这些键路径呢?可以指定以点分隔的不同属性名称。这样,通过查询car的”engine.horsepower”,就能够获取马力值。我们实际尝试一下使用-valueForKeypath和-setValueForKeyPath方法访问键路径。将以下消息发送给car,而不发送给engine:
[car setValue:[NSNumber numberWithInt:155] forKeyPath:@”engine.horsepower”];
NSLog(@”horsepower is %@”,[car valueForKeyPath:@”engine.horsepower”]);
这些键路径的深度是任意的,具体取决于对象图的复杂度,可以使用诸如“car.interior.airconditioner.fan.velocity”这样的键路径。在某种程序上,使用键路径比使用一系列嵌套方法调用更容易访问对象。
整体操作
关于KVC非常棒的一点是,如果向NSArray请求一个键值,它实际上会查询数组中的每个对象来查找这个键值,然后将查询结果打包到另一个数组中并返回给你。这种方法也适用于通过键路径访问的对象内容的数组。
在KVC中,通常认为嵌入到其它对象中的NSArray具有一对多的关系。例如,汽车与多个轮胎存在联系。因此,我们可以说Car与Tire之间存在一对多的关系 .如果键路径中含有一个数组属性,则该键路径的其余部分将被发送给数组的每个对象。
Car具有一个轮胎数组,并且每个轮胎都有自己的空气压力。通过一次调用,我们可以获取所有的轮胎压力值:
NSArray *pressures = [car valueForKeyPath:@”tires.pressure”];
执行以下调用
NSLog(@”pressures %@”,pressures);
输出结果:
pressures (
34,
34,
34,
34
)
除了让我们知道轮胎状态之外,这里还发生了什么呢?valueForKeyPath:将路径分解并从左向右进行处理。首先,它向car请求轮胎信息。获取轮胎信息后,它通过键路径的其余部分请求tires对象的valueForKeyPath:,在本例中为“pressure”。NSArray实现valueForKeyPath:方法,循环查找其内容并将消息发送给每个对象。因此,使用“pressure”作为键路径,NSArray向内部的每个轮胎发送一个valueForKeyPath:,结果会将轮胎压力值打包到NSNumber中并返回。
然而,不能在键路径中为这些数组使用索引,例如,通过使用“tires[0].pressure”获取第一个轮胎的压力值。
流畅的计算
键路径不仅能引用对象值,还可以引用一些运算符来进行一些运算,例如获取一组值的平均值或返回这组值中的最小值和最大值。
例如,通过以下代码来计算汽车的数量:
NSNumber *count;
count = [garage valueForKeyPath:@”cars.@count”];
该程序会输出cars数组的长度。
我们将键路径”cars.@count”拆开,cars用来获取cars属性,它是来自garage的NSArray类型的值。我们知道,它是一个NSMutableArray,但如果我们不打算更改任何内容,可以将它视为NSArray。接下来是@count,其中的@符号意味着后面将进行一些运算。对编译器来说,@“blah”是一个字符串,@interface用于引入一个类。此处的@count用于通知KVC机制计算键路径左侧的结果。
我们还可以计算某些特定值的总和,例如,车队行驶的总英里数:
NSNumber *sum;
sum = [garage valueForKeyPath:@”cars.@sum.mileage”];
这项功能是如何实现的?@sum运算符将键路径分成两部分。第一部分可以看成多对关系的键路径,在本例中代表cars数组。另一部分可以看成包含一对多关系的任何键路径。它被当作用于关系中每个对象的键路径。mileage被发送到cars描述的关系中的每个对象,然后将这些结果值相加。
如果需要得到平均每辆汽车行驶的距离,可以将总数再除以汽车数量。但是还有一种更简单的方法:
NSNumber *avgMileage;
avgMileage = [garage valueForKeyPath:@”cars.@avg.mileage”];
如果没有键/值编码这个优点,我们必须对汽车编写一个循环,查找每辆汽车的行驶距离,将它们累加,然后再除以汽车数量——虽然不是很难,但还是需要一小段代码。
还有@min和@max运算符。
KVC不是免费的
KVC能非常轻松地处理集合。那么 ,为什么不使用KVC来处理所有对象,并且抛弃存取方法和代码编写?天下没有免费的午餐,除非你在某个硅谷技术大公司工作。KVC需要解析字符串来计算你需要的答案,因此速度比较慢。此处,编译器还无法对它进行错误检查。你可能想要处理karz.@avg.millage:,但编译器不能判断它是否是错误的键路径。因此,当你尝试使用它时,就会出现运行时错误。
此外,你无法添加自己的运算符。
- Objective-C基础教程学习笔记(十六)键/值编码
- Objective-C基础教程学习笔记
- objective-c基础教程(笔记)
- Objective-C学习笔记(十六)——成员变量
- 读《Objective-C基础教程》学习笔记
- Objective-C基础教程学习笔记 内存管理
- 《Objective-C基础教程》学习笔记第二章
- 《Objective-C基础教程》学习笔记第八章
- Objective-C基础教程学习笔记(附…
- Objective-C学习笔记第十六章键/值编码
- python基础教程学习笔记十六
- Objective-C学习笔记(二十六)——成员变量的继承学习
- 《Objective-C基础教程》学习笔记第三-六章
- 《Objective-C基础教程》学习笔记第九-十章
- 《Objective-C基础教程》学习笔记第十一-十三章
- 《Objective-C基础教程》学习小结
- [学习笔记—Objective-C]《Objective-C-基础教程 第2版》第二章~第七章
- [学习笔记—Objective-C]《Objective-C-基础教程 第2版》第八章 Foudation Kit 介绍
- IBMPower服务器绿色节能方案简述
- 关于onInterceptTouchEvent()和onTouchEvent()!
- hadoop集群搭建--于虚拟机中
- Oracle学习(一)
- struts.xml 配置
- Objective-C基础教程学习笔记(十六)键/值编码
- [Android] ListView中getView的原理+如何在ListView中放置多个item
- Linux大文件传输
- uva 11300 (代数分析)
- 哈希表中线性探测再散列法及等概率条件下平均查找长度
- 图解数据结构 -------二叉查找树及平衡二叉查找树
- Notification中Intent携带数据重复问题
- git 常用命令(含删除文件)
- SVN操作手册