首先,关于您提供的代码的几点说明:
1) 看起来您可能会在用户键入时将多个搜索排队,并且这些都必须在相关搜索(最近的搜索)使用所需结果集更新显示之前运行完成。
2)您展示的第二个片段是线程安全方面的正确模式。第一个片段在搜索完成之前更新 UI。第一个片段可能会发生崩溃,因为当主线程从它读取时,后台线程正在更新 searchArray,这意味着您的数据源(由 searchArray 支持)处于不一致的状态。
你没有说你是否使用a UISearchDisplayController
,这真的没关系。但如果你是,一个常见的问题是没有实施- (BOOL) searchDisplayController: (UISearchDisplayController *) controller shouldReloadTableForSearchString: (NSString *) filter
并返回 NO。通过实现此方法并返回 NO,您将关闭在每次更改搜索词时重新加载 tableView 的默认行为。相反,您有机会开始异步搜索新术语,并[tableview reloadData]
仅在获得新结果后更新 UI ( )。
无论您是否使用UISearchDisplayController
,在实现异步搜索时都需要考虑一些事项:
1) 理想情况下,如果搜索不再有用(例如搜索词已更改),您可以中断正在进行的搜索并取消它。您的“searchInArray”方法似乎不支持这一点。但是,如果您只是扫描一个阵列,这很容易做到。
1a) 如果您无法取消您的搜索,您仍然需要在搜索结束时查看您的结果是否相关。如果没有,则不要更新 UI。
2) 搜索应该在后台线程上运行,以免阻塞主线程和 UI。
3) 一旦搜索完成,它需要在主线程上更新 UI(和 UI 的数据源)。
我将示例项目(这里,在 Github 上)放在一起,它对大量单词执行非常低效的搜索。UI 在用户输入他们的术语时保持响应,并且生成的搜索会在它们变得不相关时自行取消。示例的重点是以下代码:
- (BOOL) searchDisplayController: (UISearchDisplayController *) controller
shouldReloadTableForSearchString: (NSString *) filter
{
// we'll key off the _currentFilter to know if the search should proceed
@synchronized (self)
{
_currentFilter = [filter copy];
}
dispatch_async( _workQueue, ^{
NSDate* start = [NSDate date];
// quit before we even begin?
if ( ![self isCurrentFilter: filter] )
return;
// we're going to search, so show the indicator (may already be showing)
[_activityIndicatorView performSelectorOnMainThread: @selector( startAnimating )
withObject: nil
waitUntilDone: NO];
NSMutableArray* filteredWords = [NSMutableArray arrayWithCapacity: _allWords.count];
// only using a NSPredicate here because of the SO question...
NSPredicate* p = [NSPredicate predicateWithFormat: @"SELF CONTAINS[cd] %@", filter];
// this is a slow search... scan every word using the predicate!
[_allWords enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
// check if we need to bail every so often:
if ( idx % 100 == 0 )
{
*stop = ![self isCurrentFilter: filter];
if (*stop)
{
NSTimeInterval ti = [start timeIntervalSinceNow];
NSLog( @"interrupted search after %.4lf seconds", -ti);
return;
}
}
// check for a match
if ( [p evaluateWithObject: obj] )
{
[filteredWords addObject: obj];
}
}];
// all done - if we're still current then update the UI
if ( [self isCurrentFilter: filter] )
{
NSTimeInterval ti = [start timeIntervalSinceNow];
NSLog( @"completed search in %.4lf seconds.", -ti);
dispatch_sync( dispatch_get_main_queue(), ^{
_filteredWords = filteredWords;
[controller.searchResultsTableView reloadData];
[_activityIndicatorView stopAnimating];
});
}
});
return FALSE;
}
- (BOOL) isCurrentFilter: (NSString*) filter
{
@synchronized (self)
{
// are we current at this point?
BOOL current = [_currentFilter isEqualToString: filter];
return current;
}
}