Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
830 views
in Technique[技术] by (71.8m points)

ios - core data changes don't merge

I have set up a nonhierarchical dual-MOC architecture (one for the main thread, one for a private thread) with save notifications for merging changes:

- (NSManagedObjectContext *)managedObjectContext
{
   if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        NSManagedObjectContext* mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        [mainContext setPersistentStoreCoordinator:coordinator];
        [mainContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];

        _managedObjectContext = mainContext;

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(contextDidSaveMainQueueContext:)
                                                     name:NSManagedObjectContextDidSaveNotification
                                                   object:_managedObjectContext];
    }
    return _managedObjectContext;
}

- (NSManagedObjectContext *)privateManagedObjectContext
{
    if (_privateManagedObjectContext != nil) {
        return _privateManagedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {

        NSManagedObjectContext* privateContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        [privateContext setPersistentStoreCoordinator:coordinator];
        [privateContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];

        _privateManagedObjectContext = privateContext;

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(contextDidSavePrivateQueueContext:)
                                                     name:NSManagedObjectContextDidSaveNotification
                                                   object:_privateManagedObjectContext];
    }
    return _privateManagedObjectContext;

}

- (void)contextDidSavePrivateQueueContext:(NSNotification *)notification
{
    @synchronized(self) {
        [self.managedObjectContext performBlock:^{
            [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
        }];
    }
}

- (void)contextDidSaveMainQueueContext:(NSNotification *)notification
{
    @synchronized(self) {
        [self.privateManagedObjectContext performBlock:^{
           [self.privateManagedObjectContext mergeChangesFromContextDidSaveNotification:notification];          
        }];
    }
}


Now, I've updated some object A on the main thread in the main MOC, and now I'm in some method and working within a block on my private MOC queue/thread:

[self.privateManagedObjectContext performBlockAndWait:^{
   ...

I save my main-thread MOC:

[self.managedObjectContext performBlockAndWait:^{
    NSError *contextError;
    if (![self.managedObjectContext save:&contextError]) NSLog(@"ERROR SAVING MOC : %@ ; %@", contextError, [contextError userInfo]);
}];

The save is successful (verified) and triggers the save notification (verified), which executes the merger.

But the MOCs remain inconsistent: when I then fetch object A from my main MOC and log the number of objects in its relationship R, I find that the number of objects differs from when I fetch object A from my private MOC and log the number of objects in relationship R.

A bit downstream of this, in a related chain of events, I go to save my private MOC. The app pauses (only if I have enabled an all-exceptions or all-objective-c-exceptions breakpoint), and execution can be resumed without any apparent harm. This issue is described here: core data MOC save pauses execution and fails to save (w/o error or crash). I have a feeling it's related, but I suspect this failure to properly merge is a more fundamental problem.

Other notes:

  • I have tried every merge policy
  • I found that if I attempt to execute the merger using performBlockAndWait, the app hangs indefinitely; I don't know whether that is the expected behavior.
  • I've read every question I can find about this and tried everything I can think of.

Is there something wrong with this code? What else can I try? Thanks!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

What does your merge code look like? If you are getting an indefinite block with a -performBlockAndWait during the merge then that means you were already on the thread for that context when the notification was received. It would help to see your code though.

Also seeing your code for the observer construction for the notification would help.

Why are you not doing a parent/child construct here when you are doing a main thread and private thread contexts?

As for the risk of multiple NSPersistentStoreCoordinator instances being initialized, that is not a risk. Even if you built 100 PSC instances it would still work just fine against a SQLite file. SQLite is designed for multi-user access. I do not see how that can be your issue.

Update

Ok, I don't recommend merging changes from main back to private. Your private queues should be used once and thrown away, even without a parent/child design. Merging in both directions can get messy very quickly and I suspect you are getting the same data merged back and forth. Putting in some logging will confirm that.

I would also suggest using parent/child. This type of situation is what it is designed for and will dramatically improve your merge performance. It is a fairly small code change (set PSC in the private, turn off observers, good to go) to test and verify the performance change.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...