过滤巨大的 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的主要内容,如果未能解决你的问题,请参考以下文章