【问题标题】:Filter huge NSArray过滤巨大的 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 来处理过滤器。如果用户在过滤时插入另一个字符串,则中止旧的。

标签: ios nsarray nspredicate


【解决方案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。这样,在添加新操作之前,每个操作都可以根据需要取消。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-23
    • 2020-03-26
    • 2019-04-02
    • 2013-08-26
    • 2013-01-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多