【问题标题】:Sorting NSArray, NSComparator block vs. NSSortDescriptor, with null values使用空值对 NSArray、NSComparator 块与 NSSortDescriptor 进行排序
【发布时间】:2016-02-02 22:59:21
【问题描述】:

我一直在使用基于块的方法对NSArray 进行排序...但是,我注意到了一个与排序相关的错误,因此开始调查。

背景:我正在处理 NSArrayEKReminder 对象,它们具有 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 值既不在开头也不在结尾,而且数组中的空值似乎与这种比较方法混为一谈,因为许多提醒不按顺序排列。

NSSortDescriptor

所以,我尝试更换基于块的方法以支持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 方法上做错了吗?

如果不是,这些不同的方法是否会以不同的方式处理nulls?

无论如何,如果您的数据中可能包含nulls,这是否会使NSSortDescriptor 方法成为首选方法?

【问题讨论】:

  • 我觉得你最好解释一下。 “NS”集合(NSArray、NSDictionary、NSSet、NSTree 以及它们的可变对应物)中的任何条目都不能有任何 null。 Cocoa 在尝试将 nil 值或键插入其集合时崩溃。您可能指的是另一个问题 --- 数组中的 NSNull 对象,应视为特殊情况。

标签: ios objective-c sorting nsarray


【解决方案1】:

您在这里遇到的核心问题是您没有手动处理传递给sortedArrayUsingComparator: 的块中的空值。根据调用块的值,first 可以是 nilsecond 可以是 nil,或者两者都可以是 nil

发送到nil 的任何消息都返回等效的0 值(例如,当发送到nil 时返回float 的方法返回0.0f,当发送到nil 时返回int 的方法返回0,返回发送到nil的对象的方法返回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];
    }
}];

【讨论】:

  • 很好的答案,谢谢。现在似乎很明显,我没有显式处理nil ;) 那么,NSSortDescriptor 类具有(默认)比较逻辑,它与您的代码类似,上面?这样它就可以处理nil...?
  • @GavinHope 是的,就是这样。排序描述符旨在以紧凑的方式为您提供此行为,因此您不必自己编写。
猜你喜欢
  • 2011-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多