【问题标题】:iOS - searching in a large NSArray is slowiOS - 在大型 NSArray 中搜索很慢
【发布时间】:2014-10-11 10:39:09
【问题描述】:

我正在构建一个定制的 UITableViewController,它显示 iPhone 中的所有联系人,其行为类似于 ABPeoplePickerNavigationController。这意味着它还支持搜索联系人。我正在使用代码 here 进行此操作。

我已经使用Search Bar and Search Display Controller 实现了搜索功能,并且我遵循了这个tutorial by appcoda

由于我的 NSArray 是 ABRecordRef 的数组,我的 filterContentForSearchText: scope: 方法是这样的:

- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
    NSPredicate *resultPredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
        ABRecordRef person = (__bridge ABRecordRef)evaluatedObject;
        NSString * fullname = [self getFullnameOfRecord:person];

        NSPredicate *tmpPredicate = [NSPredicate predicateWithFormat:@"self contains[c] %@", searchText];
        if ([tmpPredicate evaluateWithObject:fullname]) {
            return YES;
        } else {
            NSLog(@"tmpPredicate didn't match");
            return NO;
        }
    }];

    searchResults = [self.allContacts filteredArrayUsingPredicate:resultPredicate];
}

搜索结果很好,但是由于这是一个非常大的数组,所以它工作起来很慢。有没有办法提高这种搜索机制的性能?

更新:正如@Eiko 建议的那样,我尝试用以下代码替换内部 NSPredicate:

NSRange range = [fullname rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (range.length > 0) {
    return YES;
} else {
    return NO;
}

但它并没有提高性能。

【问题讨论】:

    标签: ios performance nspredicate uisearchbardisplaycontrol


    【解决方案1】:

    您应该尝试使用分析器找到最薄弱的线, 但我认为问题在于每次都会为每个条目评估谓词块。

    我建议你为 ABRecordRef 创建自己的包装类(比如说 RecordWrapper),它包含指向 ABRecordRef 的链接以及整个数据并缓存一些常用和重要的值(例如 fullName),你可以在加载时获取它一次联系人列表。

    如果你有一个 RecordWrapper* 对象数组,你可以通过调用来过滤

    NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:@"fullName contains[c] %@", searchText];
    searchResults = [self.allContactsWrappers filteredArrayUsingPredicate:resultPredicate];
    

    这应该会显着提高过滤速度。

    【讨论】:

    • 我可以为ABRecordRef添加子类或添加类别吗?因为尝试在 Xcode 中添加新文件时,我在选项ABRecordRef 中看不到。另外,缓存它是什么意思?我怎样才能缓存这个?
    • @roi.holtzman 只需创建继承自 NSObject 的类,该类具有 ABRecordRef 对象的属性。它还将具有 fullName NSString* 属性。您创建一次此类的对象,指定 ABRecordRef,将相应的数据提取到 fullName 和其他需要的属性。然后,您将拥有具有简单 NSString 属性的简单基础对象。
    • 谢谢,您的回答确实提高了过滤速度。
    【解决方案2】:

    “非常大的数组”?我猜联系人列表相当小,通常

    话虽如此,谓词的东西可能会很贵,对于this 的回答,简单的枚举可能是最快的。不过,我建议在真实设备上进行测试(和配置文件)。

    我想创建谓词可能是一项代价高昂的操作(它们是否需要编译?),因此您可以重用它,或者更好的是,只需自己对 fullname 进行小“包含检查”(只需执行使用rangeOfString:options:) 搜索,同时省略谓词。

    【讨论】:

    • 确实数组大约有 1k 个元素。我尝试使用您提供的内容:NSRange range = [fullname rangeOfString:searchText options:NSCaseInsensitiveSearch];,然后检查是否range.length > 0。但这并没有提高性能。也许这与我对ABRecordRef 的操作有关,将其变成NSString *
    • 您是否测量过基线,即访问所有地址记录,但对它们什么都不做?你不能比这更快。
    • 是的。缓慢是在搜索文本字段中输入文本时。
    • 是的,但是如果您正在迭代记录但对它们不做任何事情,它的速度有多快?在过滤时,您可以访问每一个,而在滚动和显示过程中却没有。
    • 是的,这就是我问的原因:触摸所有地址簿条目的速度有多快?这是一个简单的测试......如果在没有过滤的情况下迭代所有项目很慢,您需要寻找其他方法,例如缓存访问的属性。过滤 1000 个元素应该很快,但是为了获取全名,可能需要进行后台工作。我真的建议从 Instruments 开始,看看是什么在消耗时间。
    猜你喜欢
    • 2012-04-01
    • 2014-12-27
    • 2012-12-17
    • 1970-01-01
    • 2014-08-08
    • 2012-11-20
    • 1970-01-01
    • 1970-01-01
    • 2022-11-23
    相关资源
    最近更新 更多