过滤巨大的 NSArray

Posted

技术标签:

【中文标题】过滤巨大的 NSArray【英文标题】:Filter huge NSArray 【发布时间】:2012-12-08 21:45:51 【问题描述】:

我正在使用 NSPredicate 过滤 NSArray,并将过滤后的数组用于我的 UITableView。 当用户在 UITextField 中输入文本时,我正在使用此过滤。所以每次 UITextField 中的文本发生变化时,我都会调用我的过滤器函数。

看起来像这样:

NSArray *hugeArray = ...;
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == %@", input];
_resultArray = [hugeArray filteredArrayUsingPredicate:predicate];
[_myTableView reloadData];

当我使用包含大量对象的 NSArray 时,输入会变得非常慢(UI 中的完整输入会变慢)。 是否有可能获得更好的性能或在后台运行过滤命令?

不应阻止在 UITextField 中写入内容。当UITableView在输入后很短的时间后刷新时可能没问题。

【问题讨论】:

我建议你使用 NSOperationqueue 来处理过滤器。如果用户在过滤时插入另一个字符串,则中止旧的。 【参考方案1】:

NSPredicate 注重灵活性而不是速度。对于内存中的NSArray(即不是核心数据关系),通常只需使用循环即可获得更好的性能。

如果还是太慢,那么有几种方法:

合并您的请求。请参阅Is there a simple way (in Cocoa/ios) to queue a method call to run once in the next run loop? 您可以创建一个合并蹦床,以便每隔几百毫秒更新您的列表。这样,如果用户输入速度很快,您就不会重新过滤每个字符的列表。

过滤更聪明。如果您刚刚过滤了“bo”,而您现在想要过滤“bob”,那么您知道它是之前列表的子集。您不必重新过滤所有内容。为此编写一个好的算法需要一点工作,但可以显着提高性能。

NSOperationQueue 上执行过滤(比 GCD 更容易取消,但 GCD 也可以),并让 UI 使用 KVO 来通知过滤后的数组何时发生变化。

在过滤时跟踪实际更改(添加/删除)。如果你能帮忙的话,你不应该在你的表格视图上调用reloadData。您应该执行插入和删除 (insertRowsAtIndexPaths:)。这样可以避免不断搅动您的细胞,而且通常看起来也更好。同样,代码更复杂,但改进可能是巨大的。

【讨论】:

感谢您的提示。我会试试看。【参考方案2】:

如果您仍想使用谓词并且对象顺序不重要(意味着对象的索引无关紧要),您可以将NSArray 转换为NSSet过滤集合(使用NSPredicate)比数组快得多

NSSet *hugeSet = [NSSet setWithArray:hugeArray]
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == %@", input];
NSSet *filteredSet = [hugeSet filteredSetUsingPredicate: predicate];

Apple Documentation 中有关 NSSet、NSArray 和 NSDictionary 的更多信息

【讨论】:

【参考方案3】:

我认为在后台运行它是解决方案。在另一个队列上执行查询,然后在主队列上重新加载表。使用 GCD,它看起来像这样......

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name == %@", input];
        _resultArray = [hugeArray filteredArrayUsingPredicate:predicate];

          dispatch_async(dispatch_get_main_queue(), ^
                [_myTableView reloadData];
          );
    );

【讨论】:

这需要小心。如果在每次击键时都这样做,那么将会出现巨大的问题。每次新的击键都应该启动后台搜索,但只有在停止当前后台搜索之后。如果用户更新文本并开始新的搜索,则完成当前搜索没有任何意义。 您应该过滤到一个临时数组并在主线程上分配_resultArray 的新值,因为表视图可能会在后台线程仍在运行时访问其数据源。 @rmaddy,如果这是一个非常长的操作,则为 true。我不知道有什么方法可以停止过滤,你呢? @MartinR 使用原子属性也可以解决这个问题,不是吗?例如self.resultArray = @combinatorial 我不会为此使用谓词。一个可以快速退出的简单for 循环会更好。我会使用NSOperationQueue 而不是 GCD。这样,在添加新操作之前,每个操作都可以根据需要取消。

以上是关于过滤巨大的 NSArray的主要内容,如果未能解决你的问题,请参考以下文章

MYSQL搜索条件的微小差异会产生巨大的差异,无法绕过它

如何向 GET 请求发送巨大的参数列表

具有巨大性能问题的不同***记录

如何处理巨大的表格[关闭]

使用 OpenCV 时巨大的 apk 文件大小

协同过滤-基于物品的过滤