(转)Objective-C 使用KVC

KVC :Key Value Coding,直译是:键值编码。

 

Book类的代码,Book.h

1 #import <Foundation/Foundation.h>
2
3 @interface Book : NSObject {
4 NSString *name;
5
6 }
7
8 @end

Book.m

1 #import "Book.h"
2
3 @implementation Book
4
5 @end

这个类太简单,只有一个实例变量name。而且,按照以前掌握的技术,没有办法给这个变量赋值了。

下面KVC登场,在main方法中给Book实例的name属性赋值并获取该属性的值:

 1 int main (int argc, const char * argv[]) { 
2 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
3
4 Book *book = [[Book alloc] init];
5 [book setValue:@"《Objective-C 入门》" forKey:@"name"];
6 NSString *name = [book valueForKey:@"name"];
7 NSLog(@"book name: %@", name];
8
9 [pool drain];
10 return 0;
11 }

这里会发现Objective-C的KVC很类似于Java中通过反射到类实例变量的方式。比如valueForKey方法先尝试在

Book实例上找到getName方法,如果找到就调用如果没有找到,则查找实例是否有name变量或者_name变量

如果还没找到,会抛出类似下面的异常

Terminating app due to uncaught exception ‘NSUnknownKeyException’, reason: ‘[<Book 0x10010c730> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key name1.’

下面把代码做一点修改,首先创建了一个新类Author,图书的作者,Author.h

1 #import <Cocoa/Cocoa.h>
2
3 @interface Author : NSObject {
4 NSString *name;
5 }
6
7 @end

也有个name属性,表示作者的姓名。Author.m

1 #import "Author.h"
2
3 @implementation Author
4
5 @end

然后,将author属性添加到Book类中,即每个Book实例都有一个author属性。头文件:

 1 #import <Foundation/Foundation.h>
2
3 @class Author;
4
5 @interface Book : NSObject {
6 NSString *name;
7 Author *author;
8 }
9
10 @end

实现文件还是什么都没有:

1 #import "Book.h"
2
3 @implementation Book
4
5 @end

在main方法中,通过KVC方式获取book的author的name属性:

 1 int main (int argc, const char * argv[]) { 
2 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
3
4 Book *book=[[[Book alloc] init] autorelease];
5 [book setValue:@"《Objective C入门》" forKey:@"name"];
6 NSString *name=[book valueForKey:@"name"];
7 NSLog(@"book name: %@",name);
8
9 Author *author=[[[Author alloc] init] autorelease];
10 [author setValue:@"Marshal Wu" forKey:@"name"];
11 [book setValue:author forKey:@"author"];
12 NSString *authorName=[book valueForKeyPath:@"author.name"];
13 NSLog(@"author name: %@",authorName);
14
15 [pool drain];
16 return 0;
17 }

可以看到,写法很类似JSP的EL表达式:${book.author.name}

在ObjC的世界里叫Path,路径。当然,你也可以:

[book setValue:@"zhangsan" forKeyPath:@"author.name"];

通过路径设置属性。

 

KVC还有一个很重要的特点,自动装箱拆箱功能。这在ObjC中是仅有的,其他情况下均需要使用比如NSNumber来手动拆装箱的。

比如Book类头文件做了下面的增加:

 1 #import <Foundation/Foundation.h>
2
3 @class Author;
4
5 @interface Book : NSObject {
6 NSString *name;
7 Author *author;
8 float price;
9 }
10
11 @end

实现文件还是没有动,不提了。main方法增加了对price赋值和获取值的调用,使用KVC方式:

 1 int main (int argc, const char * argv[]) { 
2 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
3
4 Book *book=[[[Book alloc] init] autorelease];
5 [book setValue:@"《Objective C入门》" forKey:@"name"];
6 NSString *name=[book valueForKey:@"name"];
7 NSLog(@"book name: %@",name);
8
9 Author *author=[[[Author alloc] init] autorelease];
10 [author setValue:@"Marshal Wu" forKey:@"name"];
11 [book setValue:author forKey:@"author"];
12 NSString *authorName=[book valueForKeyPath:@"author.name"];
13 NSLog(@"author name: %@",authorName);
14
15 [book setValue:@"zhangsan" forKeyPath:@"author.name"];
16
17 [book setValue:@"10.4" forKey:@"price"];
18 NSLog(@"book price is %@",[book valueForKey:@"price"]);
19
20
21 [pool drain];
22 return 0;
23 }

可以看到给price输入的是NSString类型,但是没有问题,因为KVC方式会根据字符串自动转型为适当的数值。

再看打印price属性,%@是打印对象,而price属性是float基本型,这里KVC肯定做了自动装箱的处理,将基本型转为NSNumber对象。

 

KVC还具备对集合的操作能力。比如,图书可以有相关图书,这是个1对多的关系。

可以用集合来表示,这里用NSArray表示,在Book类的头文件中改动:

 1 #import <Foundation/Foundation.h>
2
3 @class Author;
4
5 @interface Book : NSObject {
6 NSString *name;
7 Author *author;
8 float price;
9 NSArray *relativeBooks;
10 }
11
12 @end

如果想得到相关图书的价格NSArray,可以使用KVC方式,见main方法:

 1 int main (int argc, const char * argv[]) { 
2 NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
3
4 Book *book=[[[Book alloc] init] autorelease];
5 [book setValue:@"《Objective C入门》" forKey:@"name"];
6 NSString *name=[book valueForKey:@"name"];
7 NSLog(@"book name: %@",name);
8
9 Author *author=[[[Author alloc] init] autorelease];
10 [author setValue:@"Marshal Wu" forKey:@"name"];
11 [book setValue:author forKey:@"author"];
12 NSString *authorName=[book valueForKeyPath:@"author.name"];
13 NSLog(@"author name: %@",authorName);
14
15 [book setValue:@"zhangsan" forKeyPath:@"author.name"];
16
17 [book setValue:@"10.4" forKey:@"price"];
18 NSLog(@"book price is %@",[book valueForKey:@"price"]);
19
20 Book *book1=[[[Book alloc] init] autorelease];
21 [book1 setValue:@"5.0" forKey:@"price"];
22
23 Book *book2=[[[Book alloc] init] autorelease];
24 [book2 setValue:@"4.0" forKey:@"price"];
25
26 NSArray *books=[NSArray arrayWithObjects:book1,book2,nil];
27 [book setValue:books forKey:@"relativeBooks"];
28 NSLog(@"relative books price: %@",[book valueForKeyPath:@"relativeBooks.price"]);
29
30 [pool drain];
31 return 0;
32 }

增加的代码见黑体部分。日志将打印出相关图书的价格列表:

2011-05-26 19:27:57.456 ReleaseMemoDemo[10042:a0f] book name: 《Objective C入门》 
2011-05-26 19:27:57.461 ReleaseMemoDemo[10042:a0f] author name: Marshal Wu 
2011-05-26 19:27:57.462 ReleaseMemoDemo[10042:a0f] book price is 10.4 
2011-05-26 19:27:57.463 ReleaseMemoDemo[10042:a0f] relative books price: ( 5, 4 )

KVC还能对集合做运算,比如想得到相关图书的个数、相关图书的价格总和、相关图书的平均价格、价格的最大值和价格的最小值,见下面的代码:

1 NSArray *books=[NSArray arrayWithObjects:book1,book2,nil]; 
2 [book setValue:books forKey:@"relativeBooks"];
3 NSLog(@"relative books price: %@",[book valueForKeyPath:@"relativeBooks.price"]);
4 NSLog(@"relative books count: %@",[book valueForKeyPath:@"relativeBooks.@count"]);
5 NSLog(@"relative books price sum: %@",[book valueForKeyPath:@"relativeBooks.@sum.price"]);
6 NSLog(@"relative books price avg: %@",[book valueForKeyPath:@"relativeBooks.@avg.price"]);
7 NSLog(@"relative books price avg: %@",[book valueForKeyPath:@"relativeBooks.@max.price"]);
8 NSLog(@"relative books price avg: %@",[book valueForKeyPath:@relativeBooks.@min.price]);

相关日志:

2011-05-26 19:45:27.786 ReleaseMemoDemo[10289:a0f] relative books price: ( 
    5, 
    4 
) 
2011-05-26 19:45:27.787 ReleaseMemoDemo[10289:a0f] relative books count: 2 
2011-05-26 19:45:27.788 ReleaseMemoDemo[10289:a0f] relative books price sum: 9 
2011-05-26 19:45:27.788 ReleaseMemoDemo[10289:a0f] relative books price avg: 4.5 
2011-05-26 19:45:27.789 ReleaseMemoDemo[10289:a0f] relative books price avg: 5 
2011-05-26 19:45:27.789 ReleaseMemoDemo[10289:a0f] relative books price avg: 4

另外,如果想获得没有重复的价格集合,可以这样:

 1 Book *book1=[[[Book alloc] init] autorelease]; 
2 [book1 setValue:@"5.0" forKey:@"price"];
3
4 Book *book2=[[[Book alloc] init] autorelease];
5 [book2 setValue:@"4.0" forKey:@"price"];
6
7 Book *book3=[[[Book alloc] init] autorelease];
8 [book3 setValue:@"4.0" forKey:@"price"];
9
10 NSArray *books=[NSArray arrayWithObjects:book1,book2,book3,nil];
11 [book setValue:books forKey:@"relativeBooks"];
12
13 NSLog(@"relative books price: %@",[book valueForKeyPath:@"relativeBooks.price"]);
14 NSLog(@"relative books distinct price: %@",[book valueForKeyPath:@"relativeBooks.@distinctUnionOfObjects.price"]);
15
16 NSLog(@"relative books count: %@",[book valueForKeyPath:@"relativeBooks.@count"]);
17 NSLog(@"relative books price sum: %@",[book valueForKeyPath:@"relativeBooks.@sum.price"]);
18 NSLog(@"relative books price avg: %@",[book valueForKeyPath:@"relativeBooks.@avg.price"]);
19 NSLog(@"relative books price avg: %@",[book valueForKeyPath:@"relativeBooks.@max.price"]);
20 NSLog(@"relative books price avg: %@",[book valueForKeyPath:@relativeBooks.@min.price]);

这里增加了book3实例,它的价格和book2相同。在使用@distinctUnionOfObjects后,发现效果是消除重复的价格:

011-05-26 19:55:41.123 ReleaseMemoDemo[10378:a0f] book price is 10.4 
2011-05-26 19:55:41.124 ReleaseMemoDemo[10378:a0f] relative books price: ( 
    5, 
    4, 
    4 
) 
2011-05-26 19:55:41.124 ReleaseMemoDemo[10378:a0f] relative books distinct price: ( 
    4, 
    5 
)

KVC还可以在一个语句中为实例的多个属性赋值:

1 Book *book4=[[Book alloc] init]; 
2 NSArray *bookProperties=[NSArray arrayWithObjects:@"name",@"price",nil];
3 NSDictionary *bookPropertiesDictionary=[book4 dictionaryWithValuesForKeys:bookProperties];
4 NSLog(@"book values: %@",bookPropertiesDictionary);
5
6 NSDictionary *newBookPropertiesDictionary=[NSDictionary dictionaryWithObjectsAndKeys:@"《Objective C入门》",@"name",
7 @"20.5",@"price",nil];
8 [book4 setValuesForKeysWithDictionary:newBookPropertiesDictionary];
9 NSLog(@"book with new values: %@",[book4 dictionaryWithValuesForKeys:bookProperties]);

 

另外,还有两个比较高级的内容:

  • nil和覆盖setNilValueForKey方法
  • 覆盖valueForUndefinedKey方法

可自行看reference了解。 



 

转自:http://marshal.easymorse.com/tech/objc-%E4%BD%BF%E7%94%A8kvc

posted @ 2012-03-16 14:23  流浪的剑客  阅读(522)  评论(0)    收藏  举报