NSArray and KVC (muti-value per key)

来源:互联网 发布:海岛奇兵火箭炮手数据 编辑:程序博客网 时间:2024/06/06 11:43

NSArray and KVC

Evenmore on simplifying code with generic programming! NSArray's-valueForKey: has a feature that may not be immediately obvious.You can use it to return an array of values buried within a tree ofobjects. Here's a working example:NSMutableArray* tree = [NSMutableArray array];
NSDictionary   * p =nil;   // parent
NSDictionary   * c =nil;   //child     
NSNumber      * n = nil;   // value

int i;
for ( i = 0; i < 10; i++ )
{
  n = [NSNumber numberWithInt:i];
  c = [NSDictionarydictionaryWithObject: n
                                  forKey:@"someKey"];
  p = [NSDictionarydictionaryWithObject: c
                                  forKey:@"storage"];
  [tree addObject: p];
}

NSLog (@"%@", tree);

// here's the important part!

NSArray * justValues;
justValues = [tree valueForKeyPath: @"storage.someKey"];

NSLog (@"%@", justValues);


The first NSLog spits this out -- just a two-level propertylist:NSLog(@"%@", tree);
(
    {storage= {someKey = 0; }; }, 
    {storage= {someKey = 1; }; }, 
    {storage= {someKey = 2; }; }, 
    {storage= {someKey = 3; }; }, 
    {storage= {someKey = 4; }; }, 
    {storage= {someKey = 5; }; }, 
    {storage= {someKey = 6; }; }, 
    {storage= {someKey = 7; }; }, 
    {storage= {someKey = 8; }; }, 
    {storage= {someKey = 9; }; }
)

The second NSLog spits out an array of values collected by askingeach contained object for the value at@"storage.someKey":NSArray* justValues;
justValues = [tree valueForKeyPath: @"storage.someKey"];
NSLog (@"%@", justValues);


(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)



I find this really convenient in Core Data when working with asorted array of  managed objects-- you can pull out an array of values for just oneattribute.

注:

ObjC:使用KVC

KVC是什么?即:Key-Value Coding,直译是:键值编码。

还是没明白什么意思?先看看下面的代码。

Book类的代码,头文件:

#import <Foundation/Foundation.h>

@interface Book : NSObject { 
    NSString*name;

}

@end

 

实现文件:

#import "Book.h"

@implementation Book

@end

 

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

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

int main (int argc, const char * argv[]) { 
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    Book*book=[[Book alloc] init]; 
    [booksetValue:@"《Objective C入门》"forKey:@"name"]; 
    NSString*name=[book valueForKey:@"name"]; 
    NSLog(@"bookname: %@",name); 
    
    [pooldrain]; 
    return0; 
}

这里会发现ObjC的KVC很类似Java中通过反射得到类实例变量的方式。比如valueForKey方法先尝试在Book实例上找getName方法,如果找到就调用。如果没有找到,则查找实例是否有name变量或者_name变量。如果还没找到,会抛出类似下面的异常:

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

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

#import <Cocoa/Cocoa.h>

@interface Author : NSObject { 
    NSString*name; 
}

@end

 

也有个name属性,表示作者的姓名。实现文件什么也没写:

#import "Author.h"

@implementation Author

@end

 

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

#import <Foundation/Foundation.h>

@class Author;

@interface Book : NSObject { 
    NSString*name; 
    Author*author; 
}

@end

 

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

#import "Book.h"

@implementation Book

@end

 

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

int main (int argc, const char * argv[]) { 
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    Book*book=[[[Book alloc] init] autorelease]; 
    [booksetValue:@"《Objective C入门》"forKey:@"name"]; 
    NSString*name=[book valueForKey:@"name"]; 
    NSLog(@"bookname: %@",name); 
    
    Author*author=[[[Author alloc] init]autorelease]; 
    [authorsetValue:@"Marshal Wu" forKey:@"name"]; 
    [booksetValue:author forKey:@"author"]; 
    NSString*authorName=[bookvalueForKeyPath:@"author.name"]; 
   NSLog(@"author name: %@",authorName); 
    
    [pooldrain]; 
    return0; 
}

可以看到,写法很类似JSP的EL表达式:

${book.author.name}

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

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

通过路径设置属性。

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

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

#import <Foundation/Foundation.h>

@class Author;

@interface Book : NSObject { 
    NSString*name; 
    Author*author; 
    floatprice; 
}

@end

 

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

int main (int argc, const char * argv[]) { 
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    Book*book=[[[Book alloc] init] autorelease]; 
    [booksetValue:@"《Objective C入门》"forKey:@"name"]; 
    NSString*name=[book valueForKey:@"name"]; 
    NSLog(@"bookname: %@",name); 
    
    Author*author=[[[Author alloc] init]autorelease]; 
    [authorsetValue:@"Marshal Wu" forKey:@"name"]; 
    [booksetValue:author forKey:@"author"]; 
    NSString*authorName=[bookvalueForKeyPath:@"author.name"]; 
   NSLog(@"author name: %@",authorName); 
    
    [booksetValue:@"zhangsan"forKeyPath:@"author.name"]; 
    
    [booksetValue:@"10.4" forKey:@"price"]; 
    NSLog(@"bookprice is %@",[book valueForKey:@"price"]); 
    
    
    [pooldrain]; 
    return0; 
}

 

可以看到给price输入的是NSString类型,但是没有问题,因为KVC方式会根据字符串自动转型为适当的数值。再看打印price属性,%@是打印对象,而price属性是float基本型,这里KVC肯定做了自动装箱的处理,将基本型转为NSNumber对象。

KVC还具备对集合的操作能力。比如,图书可以有相关图书,这是个1对多的关系。可以用集合来表示,这里用NSArray表示,在Book类的头文件中改动:

#import <Foundation/Foundation.h>

@class Author;

@interface Book : NSObject { 
    NSString*name; 
    Author*author; 
    floatprice; 
    NSArray*relativeBooks; 
}

@end

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

int main (int argc, const char * argv[]) { 
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    Book*book=[[[Book alloc] init] autorelease]; 
    [booksetValue:@"《Objective C入门》"forKey:@"name"]; 
    NSString*name=[book valueForKey:@"name"]; 
    NSLog(@"bookname: %@",name); 
    
    Author*author=[[[Author alloc] init]autorelease]; 
    [authorsetValue:@"Marshal Wu" forKey:@"name"]; 
    [booksetValue:author forKey:@"author"]; 
    NSString*authorName=[bookvalueForKeyPath:@"author.name"]; 
   NSLog(@"author name: %@",authorName); 
    
    [booksetValue:@"zhangsan"forKeyPath:@"author.name"]; 
    
    [booksetValue:@"10.4" forKey:@"price"]; 
    NSLog(@"bookprice is %@",[book valueForKey:@"price"]); 
    
   Book*book1=[[[Book alloc] init] autorelease]; 
    [book1setValue:@"5.0" forKey:@"price"]; 
    
    Book*book2=[[[Book alloc] init] autorelease]; 
    [book2setValue:@"4.0" forKey:@"price"]; 
    
    NSArray*books=[NSArrayarrayWithObjects:book1,book2,nil]; 
    [booksetValue:books forKey:@"relativeBooks"]; 
   NSLog(@"relative books price: %@",[bookvalueForKeyPath:@"relativeBooks.price"]); 
    
    [pooldrain]; 
    return0; 
}

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

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 is10.4 
2011-05-26 19:27:57.463 ReleaseMemoDemo[10042:a0f]relative books price: ( 
   5, 
   
)

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

NSArray *books=[NSArrayarrayWithObjects:book1,book2,nil]; 
[book setValue:booksforKey:@"relativeBooks"]; 
NSLog(@"relative books price: %@",[bookvalueForKeyPath:@"relativeBooks.price"]); 
NSLog(@"relative books count: %@",[bookvalueForKeyPath:@"relativeBooks.@count"]); 
NSLog(@"relative books price sum: %@",[bookvalueForKeyPath:@"relativeBooks.@sum.price"]); 
NSLog(@"relative books price avg: %@",[bookvalueForKeyPath:@"relativeBooks.@avg.price"]); 
NSLog(@"relative books price avg: %@",[bookvalueForKeyPath:@"relativeBooks.@max.price"]); 
NSLog(@"relative books price avg: %@",[bookvalueForKeyPath:@relativeBooks.@min.price]);

相关日志:

2011-05-26 19:45:27.786 ReleaseMemoDemo[10289:a0f] relative booksprice: ( 
   5, 
   

2011-05-26 19:45:27.787 ReleaseMemoDemo[10289:a0f] relative bookscount: 2 
2011-05-26 19:45:27.788 ReleaseMemoDemo[10289:a0f] relative booksprice sum: 9 
2011-05-26 19:45:27.788 ReleaseMemoDemo[10289:a0f] relative booksprice avg: 4.5 
2011-05-26 19:45:27.789 ReleaseMemoDemo[10289:a0f] relative booksprice avg: 5 
2011-05-26 19:45:27.789 ReleaseMemoDemo[10289:a0f] relative booksprice avg: 4

 

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

Book *book1=[[[Book alloc] init]autorelease]; 
[book1 setValue:@"5.0" forKey:@"price"];

Book *book2=[[[Book alloc] init]autorelease]; 
[book2 setValue:@"4.0" forKey:@"price"];

Book *book3=[[[Book alloc] init]autorelease]; 
[book3 setValue:@"4.0" forKey:@"price"];

NSArray *books=[NSArrayarrayWithObjects:book1,book2,book3,nil]; 
[book setValue:books forKey:@"relativeBooks"];

NSLog(@"relative books price: %@",[bookvalueForKeyPath:@"relativeBooks.price"]); 
NSLog(@"relative books distinct price: %@",[bookvalueForKeyPath:@"relativeBooks.@distinctUnionOfObjects.price"]);

NSLog(@"relative books count: %@",[bookvalueForKeyPath:@"relativeBooks.@count"]); 
NSLog(@"relative books price sum: %@",[bookvalueForKeyPath:@"relativeBooks.@sum.price"]); 
NSLog(@"relative books price avg: %@",[bookvalueForKeyPath:@"relativeBooks.@avg.price"]); 
NSLog(@"relative books price avg: %@",[bookvalueForKeyPath:@"relativeBooks.@max.price"]); 
NSLog(@"relative books price avg: %@",[bookvalueForKeyPath:@relativeBooks.@min.price]);

 

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

011-05-26 19:55:41.123 ReleaseMemoDemo[10378:a0f] book price is10.4 
2011-05-26 19:55:41.124 ReleaseMemoDemo[10378:a0f] relative booksprice: ( 
   5, 
   4, 
   

2011-05-26 19:55:41.124 ReleaseMemoDemo[10378:a0f] relative booksdistinct price: ( 
   4, 
   
)

 

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

Book *book4=[[Book alloc] init]; 
NSArray *bookProperties=[NSArrayarrayWithObjects:@"name",@"price",nil]; 
NSDictionary *bookPropertiesDictionary=[book4dictionaryWithValuesForKeys:bookProperties]; 
NSLog(@"book values: %@",bookPropertiesDictionary);

NSDictionary *newBookPropertiesDictionary=[NSDictionarydictionaryWithObjectsAndKeys:@"《ObjectiveC入门》",@"name", 
                                          @"20.5",@"price",nil]; 
[book4setValuesForKeysWithDictionary:newBookPropertiesDictionary]; 
NSLog(@"book with new values: %@",[book4dictionaryWithValuesForKeys:bookProperties]);

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

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

可自行看reference了解。


2、(muti-value perkey)的其他实现方式

 

  1. Abstract all data associated with code intoa separate class (perhaps called Person)and use instances of this class as values in the dictionary.

  2. Use more than one layer of dictionaries:

    NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];NSMutableDictionary *firstOne = [NSMutableDictionary dictionary];[firstOne setObject:forename forKey:@"forename"];[firstOne setObject:surname forKey:@"surname"];[firstOne setObject:reminderDate forKey:@"reminderDate"];[dictionary setObject:firstOne forKey:[NSNumber numberWithInt:code]];// repeat for each entry.
0 0
原创粉丝点击