views:

52

answers:

2

My app involves a main screen with several sorting/viewing options of a set of data. Depending on what the user chooses, I may list them, e.g. alphabetically, N most recent, or grouped somehow.

I started the app as a Core Data Table-based navigation app; my app delegate sets up the Core Data stack (unchanged generated code), gives the NSManagedObjectContext to the controller for the initial screen, and it passes it to the UITableViewController implementing my "list of entities".

Since my three different views of the same data all end up showing a table listing out the data, I expanded this class to have three different NSFetchedResultsControllers, each with the one UITableViewController instance as their delegate. Before pushing this view controller on the stack, I call a method to switch which NSFetchedResultsController to use, e.g.

-(void)configureForMostRecent {
  self.activeFetchedResultsController = self.mostRecentResultsController;
}

Now I am getting random crashes from Core Data, e.g. NSInternalInconsistencyException and other things like that. Sometimes, I use the app and everything's fine, other times, it crashes almost instantly.

So, my instinct is that my design is just a Bad Idea(tm).

Should I basically stick to a "One UITableViewController to one NSFetchedResultsController" sort of model and just use other coding styles to reduce boilerplate?

+1  A: 

You could use one fetch controller, adjusting the fetch predicate and refetching as needed.

EDIT

Following my example case:

[NSFetchedResultsController deleteCacheWithName:@"MyObjectsCache"];
NSPredicate *_predicate = nil;
if (condition) {
    _predicate = [NSPredicate predicateWithFormat:mySearchPredicateString];
    self.currentTableView = searchDisplayController.searchResultsTableView;
}
else {
    _predicate = [NSPredicate predicateWithFormat:myDefaultPredicateString];
    self.currentTableView = tableView;
}

[fetchedResultsController.fetchRequest setPredicate:_predicate];

NSError *_error = nil;
if (![fetchedResultsController performFetch:&_error]) {
    // handle error 
}
Alex Reynolds
The generated code for the fetch controller property getter essentially caches the object, never calling performFetch again. Are you saying that I should perform the fetch every time as needed?
davetron5000
Yes, re-fetch depending on a condition test. Might be worth testing, in any case. I use this approach for switching between search results and a "full" set of fetch results.
Alex Reynolds
So, since NSFetchedResultsController seems fairly immutable, do you just release the current one and re-create another when you need to switch?
davetron5000
See edited answer...
Alex Reynolds
The docs say not to mess with the fetch request after creating the NSFetchedResultsController. Does deleting the cache basically let you get around that? (Thanks for the quick answers BTW)
davetron5000
I wrap the fetch in a `@synchronized` block and I haven't yet observed instability. But in re-reading the documentation, you are indeed correct. It may be advised to release and reinstantiate the fetch results controller with a new predicate.
Alex Reynolds
+1  A: 

Using multiple NSFetchedResultsController instances is a perfectly valid design based on the description you have given so far.

Are you trying to use the same cache for each of these NSFetchedResultsController instances? Are you calling -reloadData on the table whenever you switch to a different NSFetchedResultsController? Both of those could be causing the crash you are seeing.

Update

The delegate is not an issue but not calling -reloadData is going to be a killer. The delegate methods really are there just to update the UITableView when the NSFetchedResultsController changes. The fact that a reference to is passed into those delegate methods is a hint that they are designed to handle multipleNSFetchedResultController` instances calling into them.

Marcus S. Zarra
I was using different caches, however was not calling reloadData. I was also leaving each NSFetchedResultsController's delegate as the UITableViewController; this smells funny to me, too
davetron5000
OK, that makes sense. One thing I also noticed was that by using caches (different for each controller), I was getting crashes when I deleted rows; it was complaining about a few possibilities, one of which seemed cache related (sorry do not have the actual error message in front of me)
davetron5000
I ended up reworking things so that I'm reloading data and not caching every time. Both this and @Alex Reynold's answers were helpful; I'm marking this as "The Answer" since Alex's has some code that the docs say is a bit dangerous.
davetron5000