문제

I have 3 MOC.

  1. MainThread MOC to display stuff (with undomanager)
  2. Background-save MOC to save data to disc (connected to store)
  3. Backgorund-update MOC to download data from server, parse it and save later

They are parent-child relation.

  1. Background-update -> 1. MainThread -> 2. Background-save (store)

Now when I download data from background I need to disable undomanager on mainthread so they are not undo - that can be a case that user is editing something at the same time.

Now question is if that is correct. I have that code in background-update thread

 //create child background context which is child of 1. MainThread
 NSManagedObjectContext* context = [[AppManager sharedAppManager] createChildManagedObjectContext];
 //I'M DOING ALL CHANGES ON DATA HERE
 [context.parentContext.undoManager disableUndoRegistration]; //disable undo on main thread
 [context save:nil]; //save changes to background thread
 [context.parentContext save:nil]; //save changes to main thread
 [context.parentContext processPendingChanges]; //process changes on main thread
 [context.parentContext.parentContext save:nil]; //save data to disc on 3. save-thread
 [context.parentContext.undoManager enableUndoRegistration]; //enable undo again

With blocks it looks like that:

[context.parentContext performBlockAndWait:^{
            [context.parentContext.undoManager disableUndoRegistration];

            [context performBlockAndWait:^{
                [context save:nil];
            }];

            [context.parentContext save:nil];
            [context.parentContext processPendingChanges];

            [context.parentContext performBlockAndWait:^{
                [context.parentContext.parentContext save:nil];
            }];

            [context.parentContext.undoManager enableUndoRegistration];
        }];

I'm asking because occasionally I'm getting some inconsistency crashes and I cannot really find a reason for those.

도움이 되었습니까?

해결책

First, some basic observations about the posted code.

  1. You should only user performBlockAndWait when absolutely necessary... which is almost never.

  2. You are calling performBlockAndWait on a child context, while inside performBlockAndWait on the parent context. You should never do that.

  3. Using performBlockAndWait to call save: on a context that has a patent context is almost guaranteed to not do what you think it does. All it does is save up to the parent context.

  4. You are calling performBlockAndWait while inside a performBlockAndWait on the same context. Nothing wrong with that since that call is reentrant. However, it is another clue that there is something wrong with your CD stack management.

Now, some suggestions that may help.

In your situation, I would suggest a change to your MOC hierarchy. Keep the private queue MOC as a parent to the main queue MOC. That allows your DB changes to be done asynchronously. Nothing wrong there. Just remember that you have to cascade the save: call to the parent or schedule a save because saving the main MOC will only copy its data up the stack to the parent context, and will not touch the underlying database.

However, I would take that background MOC and remove it as a child of the main MOC. Now, you don't have to worry about the undo manager at all, you can leave it alone.

Speaking of the undo manager, I have found that the best undo manager is a child context. I would just create a context that is a child of the main context, do all my changes in there. If the changes are abandoned, just delete the context. Everything is undone. You can install an undo manager on that context for incremental undo management.

Now, how to handle that background context that is doing some type of asynchronous updates (probably from some web service). I suggest either:

  1. Set it's parent to be the same as the main MOC. You will need to refresh the main MOC for changes to the parent. This has the drawback that any updates to the database are synchronized through the same parent MOC, leaving more opportunity for the main MOC to wait while fetching.

  2. Connect it directly to the persistent store coordinator, and use notifications to merge the changes.

Finally, revisit your design and see if you can get by with asynchronous calls. You really should be able to get away with calling performBlock and only call performBlockAndWait in extremely rare situations.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top