iOS - 在大型NSArray中搜索速度很慢

时间:2014-08-18 13:31:28

标签: ios performance nspredicate uisearchbardisplaycontrol

我正在构建一个自定义的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;
}

但它并没有改善表现。

2 个答案:

答案 0 :(得分:1)

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

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

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

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

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

答案 1 :(得分:0)

“非常大的阵列”?我猜联系人列表相当小,通常是< 1k元素。

话虽这么说,谓词的东西可能是溢价,而this回答简单的累积可能是最快的。我建议在真实设备上测试(和配置文件)。

我想创建谓词可能是一项代价高昂的操作(他们需要编译吗?),所以你可以重用它,甚至更好,只需在fullname自己做一点“包含检查”(只是使用rangeOfString:options:进行搜索,完全省略谓词。