I have an iPad app where I am using the NSUndoManager with Core Data. Things usually work well, except that there is a semi-reproducible bug when I undo/redo several times. I am only working on the main thread (at least, I have disabled MagicalRecords from using an NSManagedObject on a secondary thread. The problem always occurs if I try to undo/redo an insertion of an NSManagedObject to the context.
So I have something like this:
if (!self.undoManager.isUndoing && !self.undoManager.isRedoing)
{
[self.undoManager undo];
}
else
{
NSLog(@"gotcha!");
}
And after several times, I get the following exception. It happens on a secondary thread, which makes me think Core Data is doing something in the background.
CoreData: error: Serious application error. Exception was caught during Core Data
change processing. This is usually a bug within an observer of
NSManagedObjectContextObjectsDidChangeNotification. _registerUndoObject:: NSUndoManager
0xcea2d60 is in invalid state, must begin a group before registering undo
with userInfo (null) 2012-07-25 15:42:26.850 TT[3972:3c07] *** Terminating app due to
uncaught exception 'NSInternalInconsistencyException', reason: '_registerUndoObject::
NSUndoManager 0xcea2d60 is in invalid state, must begin a group before registering undo
Sometimes I am also getting EXEC_BAD_ACCESS, other times just the exception above.
Any idea what could be causing this?
Edit: clarified situation for Mundi (see comments)
Here is the solution that stopped all my crashes: Apparently Magical Records uses privateQueue concurrency by default, and if your code isn't that thread safe, I guess things don't work out. What I did is change it from NSPrivateQueueConcurrencyType to NSMainQueueConcurrencyType for the following method:
+ (NSManagedObjectContext *) MR_contextWithoutParent;
{
NSManagedObjectContext *context = [[self alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
return context;
}
I also found another detail that worked here: Core Data deleteObject: sets attributes to nil
What I had to do was save the managed object context before adding/deleting a managed object. Kind of weird and inefficient, but saves lots of trouble.