I'm trying to speed up my app search , it get lags when there is a lot of data.
so i'm trying to split search Predicate on UI by using dispatch_async
not dispatch_sync
cause no different if I use it.
The problem is when i use dispatch_async
, the app crash sometimes because [__NSArrayI objectAtIndex:]: index "17" beyond bounds
.
I now this happened because lets say the first one still work and reload the tableView and continue search will change the array size depend on result so in this case "CRASH" :(
this is my code:
dispatch_async(myQueue, ^{
searchArray = [PublicMeathods searchInArray:searchText array:allData];
} );
if(currentViewStyle==listViewStyle){
[mytable reloadData];
}
and i've tried this :
dispatch_async(myQueue, ^{
NSArray *tmpArray = [PublicMeathods searchInArray:searchText array:allData];
dispatch_sync(dispatch_get_main_queue(), ^{
searchArray = tmpArray;
[mytable reloadData];
});
});
but in this case the lags still there.
Update -1- :
The search Predicate takes just 2ms :) after hard work :) but the keyboard still lags when the user searches, so the only thing I do after get result is reload table "change in UI" this what I think make it lags,
So what I search for split this two operation "typing on keyboard & refresh UI".
Update -2- :
@matehat https://stackoverflow.com/a/16879900/1658442
and
@TomSwift https://stackoverflow.com/a/16866049/1658442
answers work like a charm :)
One solution might be to voluntarily induce a delay between searches to let the user type and let the search be performed asynchronously. Here's how:
First make sure your queue is created like this :
dispatch_queue_t myQueue = dispatch_queue_create("com.queue.my", DISPATCH_QUEUE_CONCURRENT);
Have this ivar defined in your class (and set it to FALSE
upon initialization):
BOOL _scheduledSearch;
Write down this macro at the top of your file (or anywhere really, just make sure its visible)
#define SEARCH_DELAY_IN_MS 100
And instead of your second snippet, call this method:
[self scheduleSearch];
Whose implementation is:
- (void) scheduleSearch {
if (_scheduledSearch) return;
_scheduledSearch = YES;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)((double)SEARCH_DELAY_IN_MS * NSEC_PER_MSEC));
dispatch_after(popTime, myQueue, ^(void){
_scheduledSearch = NO;
NSString *searchText = [self textToSearchFor];
NSArray *tmpArray = [PublicMeathods searchInArray:searchText array:allData];
dispatch_async(dispatch_get_main_queue(), ^{
searchArray = tmpArray;
[mytable reloadData];
});
if (![[self textToSearchFor] isEqualToString:searchText])
[self scheduleSearch];
});
}
[self textToSearchFor]
is where you should get the actual search text from.
Here's what it does :
_scheduledSearch
ivar to TRUE
and tells GCD to schedule a search in 100 ms_scheduledSearch
ivar is reset to FALSE
, so the next request is handled.You can play with different values for SEARCH_DELAY_IN_MS
to make it suit your needs. This solution should completely decouple keyboard events with workload generated from the search.