简体   繁体   中英

Why am I getting a merge error when saving an NSManagedObjectContext this 'clean'?

thanks in advance for any help. I've spent today battling this and I think that there is something seriously wrong with my understanding of how the framework works.

I'm working on a core data application where the entities have a parent/child relationship. The application creates an NSManagedObjectContext (MOC) on startup. When the application is run for the first time it uses an async block to import the contents of a plist into a second MOC (the root node is obtained from the main MOC using the URI and -managedObjectIDForURIRepresentation:), just before the block finishes it saves the second context.

In my data controller I subscribe to NSManagedObjectContextDidSaveNotification and the following code is run when the notification is sent:

- (void)backgroundContextDidSave:(NSNotification *)notification {
    if(![notification.object isEqual:self.managedObjectContext]){
        if (![NSThread isMainThread]) {
            [self performSelectorOnMainThread:@selector(backgroundContextDidSave:)
                                   withObject:notification
                                waitUntilDone:NO];
            return;
    }       
        [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];        ;
    }
}

I've done a sanity check on this code and sure enough, when the second MOC saves, this is called from the thread executing the block, it is then deferred, and run from the main thread. The notification object contains all objects imported in the second MOC, including the two we're going to be dealing with next.

When this has finished I run the following code which is in a method of the NSManagedObject subclass the objects belong to, it is simply meant to remove a child from its parent:

TreeEntry *oldParent=self.parent; //keep a pointer to the old parent around so we can delete self from the children

// These next four lines are a sanity check to make sure that both objects are on the same MOC we're saving
NSManagedObjectContext *selfContext=self.managedObjectContext;
NSManagedObjectContext *parentContext=self.parent.managedObjectContext;
NSManagedObjectContext *sharedContext=[[DataController sharedDataController] managedObjectContext];
assert([selfContext isEqual:parentContext] && [selfContext isEqual:sharedContext]);

// now we fault the two objects to make sure we can not possibly have them or any changes 
// to them in the state of the main MOC, by this time the second MOC is long gone
[sharedContext refreshObject:self.parent mergeChanges:NO];
[sharedContext refreshObject:self mergeChanges:NO]; 

// up to this point, sharedContex.insertedObjects, sharedContext.updatedObects and sharedContext.deletedObjects
// have all contained no objects at all. None of the above was necessary as the MOC held no changes at all
[sharedContext saveChanges]; // we save it to, well, just to make sure I guess, I may be going crazy

// Now we carry out two changes to the objects, problem occurs if only one change is carried out,
// I'm showing both to show that there relationship is being kept consistent and valid
self.parent=nil;
[oldParent removeChild:self];


// When the next line is run the save fails with a merge conflict
[sharedContext saveChanges];

The last save fails with a Cocoa error 133020, which is a merge fail. The two NSMergeConflicts in the error relate to the entries we're dealing with (self and self.parent).

I just don't understand how that can be. The objects have no state when they are modified so they MUST be getting loaded from the store. Two simple changes are made and then when they are saved straight afterwards a merge conflict occurs. How can that be? nothing else has messed with the store and we've just loaded the objects from it.

I know I can change the merge policy but I don't want to do it without understanding what's going on.

Any ideas? I'm sure it's just that my mental model if what's going on is wrong, but I've not been able to set it right all day!

Ok, it WAS a fundamental misunderstanding on my part of how the framework works, or to be more accurate, the NSManagedStoreCoordinator cache.

When I save the background context the changes go to disk, but apparently the NSManagedStoreCoordinator (which both contexts share) doesn't update or invalidate its cache.

When I refresh the objects in the main MOC the data used to re-populate them comes from the cache, which still has the old data. It doesn't re-load from the disk. The solution was to use [MOC setStalenessInterval:0.0] to force re-loading from disk.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM