如何在 iOS 应用中更快地搜索本地数据?

Posted

技术标签:

【中文标题】如何在 iOS 应用中更快地搜索本地数据?【英文标题】:How to make search of local data faster in iOS app? 【发布时间】:2012-12-03 20:01:39 【问题描述】:

我有一个预填充的 Coredata 文件,我想在 ios 应用程序中进行搜索。该文件有这样的索引词 ~(70,000 行)~10 MB 文件。

ad
adam
arm
apple
..
..
zen
zener

我已经从文件中构建了 Coredata 并查询了 SQLLite 例如:给我所有以“a”开头的单词。在模拟器上搜索大约需要 300 毫秒,但在 iPad 设备上大约需要 2 秒或更长时间?我怎样才能让它更快?其他搜索引擎类型的应用在设备上是如何做到的?

//////////////////////////////////////////////////////////////////////
//This will get list of words begining with letters from search word
//////////////////////////////////////////////////////////////////////
-(void) FetchWords

NSString *entityName=@"KeyWord";
NSString *sortKey = @"word";
NSArray *fetchColumns = [[NSArray alloc] initWithObjects:@"word", nil];

//init fetch request
NSFetchRequest *req = [[NSFetchRequest alloc] init];

NSString *query = self.searchBar.text;

//
//split search phrase into words - searches words with any order and not case senitive - remove last space if any
//
NSMutableArray *words = [[query componentsSeparatedByString:@" "] mutableCopy];
if([words[words.count-1] isEqualToString:@""])
    [words removeObjectAtIndex:[words count]-1];

if(query.length)


    NSMutableArray *subpredicates = [NSMutableArray array];

    if(words.count >1)
    
       [self GetMatchingCategoryCodes];
      //  NSLog(@"%@",categories);


        for (NSString *code in categories)
        
            //intersection between last word and previous categories already filtered for previous words in search

           NSPredicate *pred = [NSPredicate predicateWithFormat:@"word BEGINSWITH[cd] %@ AND code BEGINSWITH [cd] %@",words[words.count-1],code]; 
           [subpredicates addObject:pred];
        

      req.predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates];

    
    else
    
       for(NSString *token in words)
       
           NSPredicate *pred = [NSPredicate predicateWithFormat:@"word BEGINSWITH[cd] %@",token];
          [subpredicates addObject:pred]; 
       
       req.predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates];

    



//setup request
NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:context];
[req setEntity:entity];
[req setFetchBatchSize:100];

//sort descriptors
NSSortDescriptor *desc = [[NSSortDescriptor alloc] initWithKey:sortKey ascending:YES];
NSArray *descriptors = [NSArray arrayWithObject:desc];
[req setSortDescriptors:descriptors];

//for unique values - ignore repeatition
req.propertiesToFetch = fetchColumns;
req.returnsDistinctResults = YES;
req.resultType = NSDictionaryResultType;


//execute request
NSError *error;
Codes = [context executeFetchRequest:req error:&error];

//  NSLog(@"Fetched=%d",Codes.count);


【问题讨论】:

向我们展示执行查询的代码。 【参考方案1】:

您可以尝试延迟加载并将输出限制为 50 个或更少的项目,因为它们无论如何都不会在屏幕上可见。当用户在结果列表中滚动时,您会根据需要获取更多信息。

【讨论】:

我是否使用 [req setFetchBatchSize:TheNumber]; 来执行此操作? req.fetchLimit = 50; req.fetchOffset = (根据位置)【参考方案2】:

如果您在表格视图中显示这些单词,您当然应该考虑使用 NSFetchedResultsController 并启用缓存。

【讨论】:

【参考方案3】:

优化方法不止一种。

例如,使用 CONTAINS 会减慢搜索速度。 Apple 建议对搜索词使用单独的实体 - 称为 searchWords(或其他),并在其中存储您正在搜索的所有词,将其设为小写并删除重音。这些单独的实体具有将它们链接回原始对象的关系。确保 word 字段已编入索引。

此外,只有输入到搜索字段中的第一个字母才应该打到磁盘上。在第一个字母之后,您应该只优化内存中已有内容的搜索结果。

您应该使用您已有的谓词过滤内存中的数组(从第一个字母搜索),并避免执行获取请求,因为它是不必要的。

此外,如果您要搜索已经在内存中的数据(例如存在于 NSFetchedResultsController 中),那么您的整个搜索应该只命中内存中的对象。去磁盘是不必要的,而且非常浪费。如果不使用 ResultsController,您可以将数据缓存在一个数组中,仅在您第一次点击后在内存中执行搜索。这也应该加快您的请求。

因此,使用启用了批量大小和缓存的 FetchedResultsController 也应该可以改善您的搜索。

顺便说一句:您可以在后台线程上执行那些“密集”搜索以避免键盘滞后。但是你必须确保一次只执行一个后台请求并且只获取最新的结果。将线程标记为较低的优先级可确保 UI 性能良好。

这里有一个关于通过示例代码提高 coredata 搜索速度的 wwdc:Apple WWDC Core DataOptimizing Core Data Performance on iPhone OS (Slides)Optimizing Core Data Performance on iPhone OS (Video)

【讨论】:

我使用 BEGINsWITH 因为那是我需要的。单词被索引。但是,我没有尝试减少获取次数,就像@n0oitaf 也建议的那样。 这只是一个例子,我不知道 beginwith 的确切性能,但由于磁盘搜索和区分大小写,它也可能很慢 - 请参阅我的编辑 你知道视频的名字吗?

以上是关于如何在 iOS 应用中更快地搜索本地数据?的主要内容,如果未能解决你的问题,请参考以下文章

如何更快地在 byte[] 中搜索字节?

如何在 Visual Studio 中更快地键入“0”?

Pandas:如何更快地应用数据框?

Pandas:如何更快地应用数据框?

iOS 15 如何让 App 启动更快?

如何更快地将单个行插入 MySQL 表?