我一直在使用基于块的方法对NSArray
进行排序......但是,我注意到了与排序相关的错误,因此开始调查。
背景:我正在处理NSArray
个EKReminder
个对象,这些对象具有creationDate
属性。我想通过降序 creationDate
(最新提醒,首先)对提醒进行排序。
这是我的之前的代码:
// NSArray* fetchedReminders... contents pulled from reminder calendars...
NSArray* sortedArray = [fetchedReminders sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
NSDate* first = [(EKReminder*)a creationDate];
NSDate* second = [(EKReminder*)b creationDate];
return [second compare:first];
}];
我相信那段代码是正确的。但是,我最终在数据库中提到了一些null
作为创建日期的提醒。这引入了一个错误 - 结果排序不正确。 null
值既不在开头也不在结尾,并且似乎数组中的空值与此比较方法相混淆,因为许多提醒都不按顺序排列。
因此,我尝试更换基于块的方法,转而使用sortedArrayUsingDescriptors
。这是当前的代码:
// NSArray* fetchedReminders... contents pulled from reminder calendars...
NSSortDescriptor* sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"creationDate" ascending:NO];
NSArray* sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray* sortedArray = [fetchedReminders sortedArrayUsingDescriptors:sortDescriptors];
这很有效。
(使用当前数据,101个提醒,其中6个具有null
个创建日期,并且它们全部放在最后。其他提醒按正确的顺序排列。)
首先,我是否对sortedArrayUsingComparator
方法做错了什么?
如果没有,是否期望这些不同的方法以不同的方式处理null
?
在任何情况下,如果您的数据中可能包含NSSortDescriptor
,这会使null
方法成为首选方法吗?
答案 0 :(得分:2)
您在此处遇到的核心问题是,您未在传递给sortedArrayUsingComparator:
的块中手动处理空值。根据调用块的值,first
可以是nil
,second
可以是nil
,或者两者都可以是nil
。
发送给nil
的任何邮件都会返回等效的0
值(例如,返回float
的方法,当发送给nil
时返回0.0f
,方法发送到int
时返回nil
返回0
,返回发送到nil
的对象的方法返回nil
)。这意味着您有以下情况:
[<non-nil> compare:<non-nil>]
(返回有效值)[<non-nil> compare:nil]
(未定义的行为,根据https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSDate_Class/#//apple_ref/occ/instm/NSDate/compare:)[nil compare:<non-nil>]
(返回0;调用nil
)[nil compare:nil]
(返回0,调用nil
)这意味着,当调用数组中的值时,会返回一些非语义的值(例如[nil compare:[NSDate date]]
返回0
,相当于NSOrderedSame
,显然不是真的),更不用说未定义调用返回的结果。
实际上,这些无效值将被分类到数组中的奇怪位置。如果您对某个值为nil
时应该发生的事情有一些已定义的行为,那么您将获得一致的行为。
以下代码使排序行为保持一致(并且应该为您提供与上述排序描述符方法类似的行为):
NSArray* sortedArray = [fetchedReminders sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
NSDate* first = [(EKReminder*)a creationDate];
NSDate* second = [(EKReminder*)b creationDate];
if (!first && !second) {
// nils have the same relative ordering
return NSOrderedSame;
} else if (!first) {
// second is not nil; send first toward the end of the array
return NSOrderedDescending;
} else if (!second) {
// first is not nil; send second toward the end of the array
return NSOrderedAscending;
} else {
// Neither is nil; this is valid
return [second compare:first];
}
}];