Speed up search using dispatch_async?

2019-02-09 02:26发布

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 :)

8条回答
\"骚年 ilove
2楼-- · 2019-02-09 03:11

i believe your crash is indeed solved by the embedding of the display of the UI element for which searchArray is the backing element in a call to GrandCentralDispatch inside of the other call (as you show in your updated original post). that is the only way to make sure you are not causing the elements of the array to change behind the scenes while the display of the items associated with it is taking place.

however, i believe if you are seeing lag, it is not so much caused by the processing of the array at 2ms or the reload that takes 30ms, but rather by the time it takes GCD to get to the internal dispatch_sync call on the main queue.

if, by this point, you have managed to get the processing of your array down to only 2ms in the worst case (or even if you've managed to get it down to less than 30ms, which is about the time it takes to process a frame in the main run loop at 30 fps), then you should consider abandoning GCD altogether in your effort to process this array. taking 2ms on the main queue to process your array is not going to cause any buggy behavior.

you may have lag elsewhere (i.e. if you are incrementing search results by trying to go out to the net to get the results, you may want to do the call and then process the response on your separate dispatch queue), but for the times you are talking about, this bit of processing doesn't need to be split out onto separate queues. for any hard-core processing that takes over 30ms, you should consider GCD.

查看更多
Explosion°爆炸
3楼-- · 2019-02-09 03:16

I found a simple solution with the same spirit of the solution presented by Matehad (wait some time and perform a search only if the user doesn't input anything else). Here it is:

Declare 2 global counters and a global string:

int keyboardInterruptionCounter1 = 0, int keyboardInterruptionCounter2 = 0 and NSString *searchTextGlobal

On the searchBar function do this:

-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{

keyboardInterruptionCounter1++;

    searchTextGlobal = searchText;//from local variable to global variable

    NSTimeInterval waitingTimeInSec = 1;//waiting time according to typing speed.

    //waits for the waiting time
    [NSTimer scheduledTimerWithTimeInterval:waitingTimeInSec target:self  selector:@selector(timerSearchBar:) userInfo:nil repeats:NO];

}

-(void)timerSearchBar:(NSTimer *)timer{

    keyboardInterruptionCounter2++;

    // enters only if nothing else has been typed.
    if (keyboardInterruptionCounter2 == keyboardInterruptionCounter1) {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
                                             (unsigned long)NULL), ^(void) {

        //do the search with searchTextGlobal string

    dispatch_async(dispatch_get_main_queue(), ^{
         //update UI
     });

    });

    }
}

Explanation: The search is performed only if both counters are the same, this only happens if the user has typed and waited .52 sec without typing anything else. Instead, if the users types fast enough, then no query is done. The solution can be done with or without threading.

查看更多
登录 后发表回答