iPhone Developer Wall of Shame Thanks For The Memory. Not.
Mar 18

If your iPhone app needs to store or access a lot of data locally you should take a look at Core Data. It’s not a panacea, but I find that it’s particularly useful when you need to search a lot of data that may result in a very long list to present in a table view. For this use case the NSFetchedResultsController is your friend. It handles many of the nasty issues of memory management, ensuring that you have enough data in memory to populate the visible portion of the table view instead of loading the entire result set into memory at once.

Your code to perform a fetch using NSFetchedResultsController may look something like this:

NSPredicate *predicate =[NSPredicate predicateWithFormat:@"name contains[c] %@", searchText];
[self.fetchedResultsController.fetchRequest setPredicate:predicate];
NSError *error = nil;
[self.fetchedResultsController performFetch:&error];

If you’re using a UISearchDisplayController you may be tempted to call this code each time the user enters a new character in the search field. There may be obvious performance reasons why you cannot want to do this. If it takes a few seconds to perform each fetch after each character then it will make for very slow data entry that will frustrate the user.

But there’s a more subtle reason why this is a bad idea, which I recently ran into.

NSFetchedResultsController optionally caches results and the cache content may get out of sync if you just change the NSPredicate like the code above does. One way to get around this is to delete the cache before you set the new predicate:

[NSFetchedResultsController deleteCacheWithName:nil];
[self.fetchedResultsController.fetchRequest setPredicate:predicate];

I’ve seen the first code section in many Core Data code examples, but I’ve never seen anyone mention the need to delete the cache before you perform another fetch with a different predicate. Now it turns out that you don’t have to do this in the current iPhone SDK, because it “just happens to work” anyway. But this behavior may, or may not, change in an upcoming NDA-covered SDK release. Consider this an advance warning.

written by Nick \\ tags: , , ,

6 Responses to “Delete the NSFetchedResultsController Cache Before Changing the NSPredicate”

  1. David Says:

    Thank you!! I’ve just been fighting with strange searching bugs… it does just happen to work (perfectly, every time) without deleting the cache in 3.1.3… and strangely not in some hypothetical SDK release I might happen to be porting to at the moment. But calling deleteCacheWithName: fixes it!

  2. Matt Says:

    Wow. Thanks. Working on iPad, basically porting over an app from iPhone. Suddenly when I change the query (predicate), and in a major way in my case, I was getting the same results back…

    Now get this. And I have no explanation for this: It persisted the cache between starts of the app. Something simply not right. And this was EVERY TIME, not a sometimes.

    In any case, I cleared my cache by name as mentioned and everything was happy… I’ll assume this is a new persistence feature I’m not yet versed in … I can only relearn so much at a time.

  3. jim Says:

    You are so right! The new SDK installed, and voila, accepted apps now blow up over this issue. I always wondered about how the cache “knew” what I wanted it to do, but it worked so I didn’t go deeper into it. This new behavior is a pain but actually makes more sense (to me, at least). Thanks for your post.

  4. Ross Says:

    Fantastic ! I installed XCode 3.2.3 yesterday, and was having these problems with an App I am developing. I went back to previous App versions which had been working under 3.2.2 and they were broken too ….

    At this point ( late last night ) I was beginning to question my sanity, and consider rewriting the App using SQL :-)

    I’ve changed all my ‘cacheName:@”Root”‘ to ‘cacheName:nil”‘ and it’s all working :-) Many thanks.

  5. GammaPoint Says:

    From Apple docs:
    Important: You must not modify fetchRequest after invoking this method. For example, you must not change its predicate or the sort orderings.

    Source: http://developer.apple.com/library/ios/#documentation/CoreData/Reference/NSFetchedResultsController_Class/Reference/Reference.html

    So what is the official way?

  6. JC Says:

    Thanks, cache deletion works.

    Updated on Apple doc, mentioning on this too.

    Modifying the Fetch Request
    You cannot simply change the fetch request to modify the results. If you want to change the fetch request, you must:

    1. If you are using a cache, delete it (using deleteCacheWithName:).
    Typically you should not use a cache if you are changing the fetch request.
    2. Change the fetch request.
    3. Invoke performFetch:.
    https://developer.apple.com/library/ios/#documentation/CoreData/Reference/NSFetchedResultsController_Class/Reference/Reference.html

Leave a Reply