8

This is quite funny. In my application I create thousands of entry in the database (in another thread, I'm using MagicalRecord). Everything seems working fine (from a background/foreground/context point of view).

When, in the main thread, I try to fetch the "just inserted" data, I discovered the following behaviour:

- (NSArray *) familiesInCompany:(Company *) company {
  NSPredicate *predicate1 = [NSPredicate predicateWithFormat:@"company == %@", company];
  NSPredicate *predicate2 = [NSPredicate predicateWithFormat:@"company.name == %@", company.name];

  NSArray *first = [Family MR_findAllSortedBy:@"name" ascending:YES withPredicate:predicate1];
  NSArray *second = [Family MR_findAllSortedBy:@"name" ascending:YES withPredicate:predicate2];
  NSArray *third = [Family MR_findByAttribute:@"company" withValue:company andOrderBy:@"name" ascending:YES];

  return second;
}

Now what I get is:

  • first: is an empty array
  • second: contains all the Family objects, as expected
  • third: is an empty array.

By debugging the SQL statement I get the following:

The "first" statement:

CoreData: annotation: total fetch execution time: 0.0000s for 0 rows.

The "second" statement":

CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZNAME, t0.ZCOMPANY FROM ZFAMILY t0 JOIN ZCOMPANY t1 ON t0.ZCOMPANY = t1.Z_PK WHERE t1.ZNAME = ? ORDER BY t0.ZNAME

CoreData: annotation: sql connection fetch time: 0.0005s

CoreData: annotation: total fetch execution time: 0.0007s for 2 rows.

The "third" statement:

CoreData: annotation: total fetch execution time: 0.0000s for 0 rows.

The hilarious thing is that I close the application (I mean really manually terminate it) and and I open it back, all the three "fetching" statements work.

Why the first and the third fetch statements seem to never being executed? How to dig into the problem?

Community
  • 1
  • 1
Fabiano Francesconi
  • 1,769
  • 1
  • 19
  • 35
  • Having essentially the same problem I think, with an NSFetchedResultsController implementation. Recently created records do not show up in the fetch result, but have been written to the persistent store when I check directly - the correct count even shows up in the relationship NSSet, but the NSPredicate doesn't see them. Weird. Using MagicalRecord 2 - used a 1.x version for a previous project and had no trouble, so I may just change course and use an older version. – Rob Reuss Jun 26 '12 at 23:34
  • Naive (and kind of off-topic) question: how are you logging the sql statements? I am really curious on how to do that in my app for better understanding predicates – Felipe Sabino Sep 04 '12 at 18:34
  • 1
    @FelipeSabino check http://stackoverflow.com/questions/6428630/xcode4-and-core-data-how-to-enable-sql-debugging and http://d.pr/i/Lszs – Fabiano Francesconi Sep 05 '12 at 09:27
  • @FabianoFrancesconi awesome, tks! – Felipe Sabino Sep 06 '12 at 18:25

1 Answers1

8

I had this same issue, this is what I figured out, and how I resolved it.

Magical Record has a root NSManagedObjectContext as a parent of the default NSManagedObjectContext. When I create an NSFetchedResultsController in the default context, everything seems to work fine, just like you.

The problem is that all the new NSManagedObject's come back with their still-temporary ObjectID's. So, in my case, I was using an NSPredicate to scope a query on an associated table. I didn't just call the association method because I didn't want to load everything into memory and wanted NSFetchedResultsController to handle changes, for me.

With the temporary ObjectID the query finds zero results and that's exactly what it displays.

Apparently the child context (default) doesn't get the benefit of the transformation to non-temporary ID's, even though it's been persisted to the backing store.

Even worse badness happened when I tried to force the issue with obtainPermanentIDsForObjects:error:. Core Data complained that it could not satisfy a fault for my instance. Nevermind, there's no way it was actually a fault. Simply refreshing the object had no effect, either. I suspect this is a Core Data bug that hardly no one tickles because they just use the association methods to get an NSSet.

My fix was to use the parent context for the NSFetchedResultsController, just like in this question, Magical Record, saving, and NSFetchedResultsController.

I was already wrapping the default in a new child context upon edit and, therefore, copying the instances into that editing context with createInContext, so I didn't have to do any extra work beyond just adding .parentContext to the argument.

Incidentally, this only ever happened on new instances of the source of the association. Once an instance was there from startup, it had a non-temporary ObjectID and never had the issue.

Community
  • 1
  • 1
Otto
  • 18,761
  • 15
  • 56
  • 62
  • Sorry I think I missed something. Your advice is to create the "resources" using the "currentContext.parentContext"? – Fabiano Francesconi Jul 11 '12 at 11:10
  • Not quite, that's what I pass to the Magical Record fetch* method. Object creation (and modification) takes place in a child context with MR_defaultContext with it's parent. – Otto Jul 11 '12 at 18:46
  • Example code, [Model fetchAllSortedBy:@"qqq" ascending:NO withPredicate:predicate groupBy:@"qqq" delegate:self inContext:self.managedObjectContext.parentContext]; In this case, self.managedObjectContext is the same as MR_defaultContext. – Otto Jul 11 '12 at 18:47
  • I ran into a similar issue during my tests using an in-memory persistent store, and the problem went away as soon as I removed all context saves. Presumably that meant the temporary ObjectID was still the correct one and it didn't matter it won't get refreshed on the default context after a save. – Otto Jul 11 '12 at 18:49
  • I think that something has to do with merging contexts. That is to say: the `- (void) MR_mergeChangesOnMainThread:(NSNotification *)notification` method is never called even when I call a MR_saveNestedContexts on a new context created via `MR_contextThatPushesChangesToDefaultContext` – Fabiano Francesconi Jul 12 '12 at 08:25
  • This is so stupid. If I use "company.families" I get all the Family objects. If I do `[Family MR_findAllSortedBy:@"name" ascending:YES withPredicate:[NSPredicate predicateWithFormat:@"company == %@", company] inContext:company.managedObjectContext]` it won't. What's wrong with MagicalRecord!? – Fabiano Francesconi Jul 12 '12 at 09:57
  • I'm not sure it's Magical Record. They're just using the pattern that Apple suggests in all the WWDC videos I've watched. I just think we're using Core Data in a slightly weird way... most people probably just use the association method and call it a day. – Otto Jul 12 '12 at 22:32
  • I accept your answer as correct since it's the only thing left to do :( – Fabiano Francesconi Jul 13 '12 at 11:05