Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Data: Interrupt executing NSFetchRequest

Been searching high and low for this:

In Core Data, is there a way to interrupt/stop/cancel and executing NSFetchRequest?

I'm implementing an incremental search in an iPhone app, and I've tried every way of optimizing it, but it's not enough (I have 42,000 records), so I have to run it in an NSOperation. When a new character is typed, I need to cancel the previous fetchRequest, but [nsoperation cancel] does nothing.

Another approach might be to move the field I'm searching on into some other interruptible index, maybe in memory, or maybe a separate sqlite3 database, which appears to be interruptible with sqlite_interrupt.

like image 545
mclin Avatar asked Jan 09 '11 09:01

mclin


4 Answers

I actually had the same issue as you. To solve it I use a performSelector:afterDelay: method

It's a bit tricky, but when the user types fast 3 letters quickly I don't want to send 3 requests but only one when the user finished typing. I set a small delay of 0.5 or more.

And use this code :

[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self performSelector:@selector(fetchSearch:) withObject:searchQuery afterDelay:0.5];

If the user types fast, it will cancel the previous requests and send only the last one.

like image 55
kschaeffler Avatar answered Nov 04 '22 01:11

kschaeffler


The direct answer to your question is no. Your best option, with your current design is to do them in an operation but then you have the thread barrier to worry about and slow you down. Keep in mind that if your operations are not running on the main thread then you need a separate NSManagedObjectContext for the operation or you are going to run into threading issues.

Better question: Why are you doing a new fetch for each character?

If you already have the fetch results from a previous search and the user did not remove a character, just take the existing results and run your predicate against the NSArray. That will further refine the search instead of going to disk each time. Since it is in memory it will be extremely fast.

Consider the following options when implementing a search field:

  • Hit the disk only on the first character
  • Hit the disk only if a character is deleted from the search box
  • Consider preloading the objectID and the searchable property to avoid disk hits.

Depending on what you are searching (and there are ways to renormalize the Core Data store to improve searching) you can preload quite a bit into memory. Even with 42K records if the search properties are small enough you can load them all into memory.

Chances are you need to test for that use case anyway if the user starts with pressing "A".

What part of the NSFetchRequest is slow? Hitting the SQLite database or loading the data into memory? Based on your answer you can improve the search performance directly.

like image 28
Marcus S. Zarra Avatar answered Nov 04 '22 01:11

Marcus S. Zarra


My solution was to let the query operations keep running, but to check if they'd been cancelled before sending the event to update the UI.

This isn't perfect because you can still have a lot of query operations running, the results for which will never be used, but it is still quite a bit faster.

like image 45
mclin Avatar answered Nov 04 '22 02:11

mclin


Short answer - no, not directly, sorry.

Long answer - Yes but you have to do quite a bit or work to do it

You will need to run it in a background thread (which it sounds like you're already doing via NSOperation)

In your fetch, set the limit to get 20 results at a time and run it in a loop. Each time through the loop, add the results it gets to an array. Each time that the fetch gets each set of results, check to see if you want to cancel the request.

like image 2
deanWombourne Avatar answered Nov 04 '22 01:11

deanWombourne