4

I'm developing an application where I need to both calculate things (multiple seconds operations) and write things (sync data with a server) on a background thread.

Because of this I use two NSManagedObjectContexts (MOC), a child and a parent, and these must always be in sync. To make sure they are in sync I always edit/add data to the child MOC so it gets pushed to the main MOC with the following pattern:

[childMOC performBlock:^{

    MyObject *myObject = *create new object in childMOC*

    [childMOC save:&error];

    [mainMOC performBlock:^{
        [mainMOC save:&error];
        // Is this mandatory to make it work correctly?
        // [childMOC performBlock:^{
        //     [childMOC refreshObject:myObject mergeChanges:NO];
        // }];
    }];
}];

After a while I seemed to have two versions of the same object in the background context, one with a temporary ID and one with a permanent ID. If I e.g. added child objects to the "real" one (by passing fresh permanent ID from main to child MOC) I didn't see these objects when I retrieved my object in the background MOC cause it is the old temporary one that is cached.

I've seen the pattern above been used a lot, but it seems strange that no one mentioned this temporary/permanent ID problem.

  1. It doesn't feel right that it can be two versions of the same object within a context. If I pass an NSManagedObjectID to the child MOC and retrieve that, shouldn't the child MOC update my existing object instead of creating a new one and leave my old temporary as cached default?

  2. Do I need to use the commented row on each place I create an object?

  3. Or maybe it works with mergeChangesFromContextDidSaveNotification, will that give the same effect?

4

2 に答える 2

3

My solution was to:

1) Use the background MOC as the parent MOC and the main MOC as a child. As a bonus I don't need to save the main MOC to get the permanent IDs.

[DC.backgroundMOC performBlock:^{
    // Add, save and update managed objects
    [DC saveContext]; // The changes is being pushed to the main context
}];

2) Use NSManagedObjectContextDidSaveNotification to keep the main MOC up to date (the main MOC is updating the UI)

- (void) backgroundMOCSaved:(NSNotification*)notification {
    [mainMOC performBlock:^{
        [mainMOC mergeChangesFromContextDidSaveNotification:notification];
    }];
}
于 2012-07-29T20:27:52.800 に答える
1

I had this problem and the solution was making sure all operations on the parent MOC are done with performBlock:, even the initial setup:

    parentManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    [parentManagedObjectContext performBlock:^{
        [parentManagedObjectContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
        [parentManagedObjectContext setPersistentStoreCoordinator:coordinator];
    }];

Once I did this, my child MOCs started picking up changes.

于 2012-07-02T22:05:58.710 に答える