【发布时间】:2015-03-06 07:56:47
【问题描述】:
NSArray 和NSMutableArray 分别提供了多种使用sortedArrayUsing... 和sortUsing... 方法对它们进行排序的方法,但是这些方法似乎都没有提供在排序开始后终止排序的方法。
对于相对较小的数组,或者比较逻辑很简单的时候,这可能没什么大不了的,但是对于较大的数组或者比较逻辑不简单的时候,我希望能够取消一个已经在进行中的排序.
简单的用例示例
根据用户的模糊搜索字符串对一组匹配的结果进行排序。当用户在搜索字段中键入时,结果会在后台线程上获取并在呈现给用户之前进行排序。如果在用户更改搜索字符串之前提取和排序操作没有完成,那么它应该被取消并开始一个新的提取和排序操作。问题是,如果 fetch-and-sort 操作已经到了排序阶段并调用了上面的NSArray 排序方法之一,那么就无法取消它。相反,下一个 fetch-and-sort 操作将等待现在陈旧的排序操作完成。
到目前为止,我已经提出了两种可能的解决方案,但似乎都不是那么优雅。
尝试的解决方案 #1
允许在任何陈旧的提取和排序操作完成之前启动较新的提取和排序操作。我只是使用一些内部状态来跟踪哪个是最新操作,并且随着操作完成,如果它们不是主要操作,那么它们的结果将被丢弃。
这是可行的,但它会很快导致多个未完成的排序操作同时运行,无论它们是否需要。这可以通过限制最大并发操作数来缓解,但我只是添加了一个任意限制。待处理的陈旧操作可以在它们执行之前被取消,但我仍然会遇到不需要排序工作的情况。
尝试的解决方案 #2
滚动我自己的快速排序或合并排序实现并将isCancelled 标志添加到这些例程中,以便它们可以快速展开和终止。这是有效的,并且工作得相当好,但是当不需要取消排序操作时,运行时间比使用 NSArray 方法之一慢约 15-20%。我想,部分原因是调用 objectAtIndex 和 exchangeObjectAtIndex 等方法的开销,我假设内部排序例程可以绕过,具体取决于 NSArray 在内部存储相关对象的方式。
在 2015 年针对 AppKit 和 NSArray 之类的东西推出我自己的排序实现也感觉错误。
半尝试的解决方案
保留一个先前排序的数组并重新使用它进行过滤:这对于我试图这样做的讨论并不有效,假设我必须排序的数组是始终未排序,并且与先前排序的数组没有关系。
从
NSArray移回 C 样式数组:这工作得很好,性能也很好,但我只能用ARC玩一堆游戏,而且它的复杂性总体实现要高得多,因为归根结底,我总是在处理NSObjects。在NSArray和 C 样式数组之间返回和值的成本也非零。
总结
所以,所有这些都回到最初的问题:“如何取消正在进行的NSArray 排序方法?”
技术说明
对于那些好奇为什么这是一个问题开始的人,我正在尝试使用像 localizedStandardCompare 这样的比较方法对 500,000 到 1,000,000 个字符串之间的某个位置进行排序,这比直接的 NSString 比较慢得多。与排序的总时间相比,各种sortUsing... 方法之间的运行时间差异相对来说微不足道。
【问题讨论】:
-
题外话:如果你有一个包含 1.000.000 个对象的大数组,如果你想对其进行排序,不要使用 nsarray,使用 ccarray github.com/manucorporat/CCArray 它比nsarray
-
我认为编写自己的版本是唯一的方法。为了提高性能,也许您可以在一个类别中实现它并通过访问类变量来获得一些优势(不是最干净的 OO 解决方案,因为变量没有记录)。
-
我认为这不可能,NSObject 提供了一种延迟取消请求的方法。此外,由于排序方法是同步运行的,因此您只能在它们完成后调用取消操作。在我看来,你应该使用枚举块创建你自己的实现或包装在 NSOperation 中。
-
分块排序,然后合并。您可以在操作之间取消。
标签: ios objective-c macos cocoa appkit