更多NSArray排序

[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.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,而不是自己编写比较函数.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值