[url]http://blog.ablepear.com/2011/12/objective-c-tuesdays-more-nsarray.html[/url]
上次我们讲了C数组和NSArray排序,今天我们继续讲讲使用NSSortDescriptors为NSArray排序.
NSArray要求指定比较函数来完成排序.当为一些简单类型(NSString, NSDate)NSArray排序的时候,比较函数十分容易编写,并且一些普通对象都有比较函数,像-caseInsensitiveCompare: 和 -localizedCompare:.
当为复杂类型NSArray排序的时候,编写比较函数就更麻烦并且更易出错.这是Person类的接口:
这是Person要使用的Address类的接口:
我们有一个Person的NSArray,我们要按country,lastName,firstName来给它排序.这是用比较block的一种实现方式:
多字段比较的通用模式是简单的,依次检查每个字段,如果比较结果不是0,停止并返回比较结果;如果所有字段都相等,返回0(或者用更具描述性的NSOrderedSame).如果你需要深入子孙对象去检查字段,这会变得非常繁琐.
幸运的是有简单的方法解决这个问题.NSArray有一个叫-sortedArrayUsingDescriptors:的方法,它接收一组NSSortDescriptor 对象作为参数.每个NSSortDescriptor 对象指定一个key路径和排序方向(升序或者降序)。数组中NSSortDescriptors的顺序决定每个字段的优先级.如果你不熟悉KVC(Key Value Coding),你可能没有遇到过key路径.KVC允许你用字符型的字段名称来get,set对象字段.访问子孙对象的字段,你需要使用.分隔的key组成的key路径.KVC有很多有意思的东西,但今天我们只涉及构建一组NSSortDescriptors:
注意byCountry 排序描述符用key路径@"address.country":,它首先取得Person对象的address属性值,然后取得address的country属性值.Key路径可以任意深.
使用排序描述符组是简单的:
这使用创建复杂的排序规则变得容易,并且你不再受限于字段的缺省比较函数.你可以为一个比较函数指定一个selector:
或者为了更专门的比较,你可以传递NSComparator的block:
用NSSortDescriptors 指定复杂排序规则是比较易读易写易维护的声明代码,并且大多数情况你你都应该考虑使用NSSortDescriptor,而不是自己编写比较函数.
上次我们讲了C数组和NSArray排序,今天我们继续讲讲使用NSSortDescriptors为NSArray排序.
NSArray要求指定比较函数来完成排序.当为一些简单类型(NSString, NSDate)NSArray排序的时候,比较函数十分容易编写,并且一些普通对象都有比较函数,像-caseInsensitiveCompare: 和 -localizedCompare:.
当为复杂类型NSArray排序的时候,编写比较函数就更麻烦并且更易出错.这是Person类的接口:
// Person.h
@interface Person : NSObject
@property (strong) Address *address;
@property (strong) NSDate *birthdate;
@property (copy) NSString *firstName;
@property (copy) NSString *lastName;
@end
这是Person要使用的Address类的接口:
// Address.h
@interface Address : NSObject
@property (copy, nonatomic) NSString *street;
@property (copy, nonatomic) NSString *city;
@property (copy, nonatomic) NSString *state;
@property (copy, nonatomic) NSString *country;
@property (copy, nonatomic) NSString *postalCode;
@end
我们有一个Person的NSArray,我们要按country,lastName,firstName来给它排序.这是用比较block的一种实现方式:
// sort Person objects by lastName, firstName
Person *frodo = [Person new];
[frodo setFirstName:@"Frodo"];
[frodo setLastName:@"Baggins"];
// ...
[[frodo address] setCountry:@"Shire"];
Person *bilbo = [Person new];
[bilbo setFirstName:@"Bilbo"];
[bilbo setLastName:@"Baggins"][bilbo address]setCountry:@"Shire"];
Person *legolas = [Person new];
[legolas setFirstName:@"Legolas"];
[legolas setLastName:@"Greenleaf"];
// ...
[[legolas address] setCountry:@"Mirkwood"];
NSArray *people = [NSArray arrayWithObjects:frodo, bilbo, legolas, nil];
NSArray *sortedPeople = [people sortedArrayUsingComparator:^(id item1, id item2) {
Person *person1 = item1;
Person *person2 = item2;
// NSComparisonResult is a typedef for int
NSComparisonResult result = [[[person1 address] country] compare:[[person2 address] lastName]];
if (result) {
return result;
}
result = [[person1 lastName] compare:[person2 lastName]];
if (result) {
return result;
}
result = [[person1 firstName] compare:[person2 firstName]];
if (result) {
return result;
}
return NSOrderedSame; // NSOrderedSame == 0
}];
// sortedPeople contains:
// Legolas Greenleaf (Mirkwood)
// Bilbo Baggins (Shire)
// Frodo Baggins (Shire)
多字段比较的通用模式是简单的,依次检查每个字段,如果比较结果不是0,停止并返回比较结果;如果所有字段都相等,返回0(或者用更具描述性的NSOrderedSame).如果你需要深入子孙对象去检查字段,这会变得非常繁琐.
幸运的是有简单的方法解决这个问题.NSArray有一个叫-sortedArrayUsingDescriptors:的方法,它接收一组NSSortDescriptor 对象作为参数.每个NSSortDescriptor 对象指定一个key路径和排序方向(升序或者降序)。数组中NSSortDescriptors的顺序决定每个字段的优先级.如果你不熟悉KVC(Key Value Coding),你可能没有遇到过key路径.KVC允许你用字符型的字段名称来get,set对象字段.访问子孙对象的字段,你需要使用.分隔的key组成的key路径.KVC有很多有意思的东西,但今天我们只涉及构建一组NSSortDescriptors:
NSSortDescriptor *byCountry = [NSSortDescriptor sortDescriptorWithKey:@"address.country"
ascending:YES];
NSSortDescriptor *byLastName = [NSSortDescriptor sortDescriptorWithKey:@"lastName"
ascending:YES];
NSSortDescriptor *byFirstName = [NSSortDescriptor sortDescriptorWithKey:@"firstName"
ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:byCountry, byLastName, byFirstName, nil];
注意byCountry 排序描述符用key路径@"address.country":,它首先取得Person对象的address属性值,然后取得address的country属性值.Key路径可以任意深.
使用排序描述符组是简单的:
NSArray *sortedPeople = [people sortedArrayUsingDescriptors:sortDescriptors];
// sortedPeople contains:
// Legolas Greenleaf (Mirkwood)
// Bilbo Baggins (Shire)
// Frodo Baggins (Shire)
这使用创建复杂的排序规则变得容易,并且你不再受限于字段的缺省比较函数.你可以为一个比较函数指定一个selector:
// specify a method to call on the lastName object
NSSortDescriptor *byLastName = [NSSortDescriptor sortDescriptorWithKey:@"lastName"
ascending:YES
selector:@selector(caseInsensitiveCompare:)];
或者为了更专门的比较,你可以传递NSComparator的block:
// sort descriptor using length of last name
NSSortDescriptor *byLastNameLength = [NSSortDescriptor sortDescriptorWithKey:@"lastName"
ascending:YES
comparator:^(id item1, id item2) {
NSString *lastName1 = item1;
NSString *lastName2 = item2;
// cast result to NSComparisonResult so that the
// compiler infers the correct return type
return (NSComparisonResult) ([lastName1 length] - [lastName2 length]);
}];
用NSSortDescriptors 指定复杂排序规则是比较易读易写易维护的声明代码,并且大多数情况你你都应该考虑使用NSSortDescriptor,而不是自己编写比较函数.