2012-07-25 90 views
1

我有一个iPad应用程序,我正在使用与核心数据NSUndoManager。事情通常很好,除了当我多次撤消/重做时有一个半可重现的错误。我只在主线程上工作(至少,我已禁用MagicalRecords在辅助线程上使用NSManagedObject。如果我尝试撤销/重做NSManagedObject到上下文的插入,问题总是会发生。CoreData抛出异常与NSUndoManager

我有这样的事情:

if (!self.undoManager.isUndoing && !self.undoManager.isRedoing) 
{ 
    [self.undoManager undo]; 
} 
else 
{ 
    NSLog(@"gotcha!"); 
} 

而且几次之后,我得到以下异常它发生在一个辅助线程,这让我觉得核心数据在后台做一些

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 

有时候我也是开玩笑g EXEC_BAD_ACCESS,其他时候只是上面的例外。

任何想法可能会造成这种情况?

编辑:为澄清曼迪情况(见注释)

+0

您需要给予更多的上下文,而不仅仅是对“撤销”的调用。 – Mundi 2012-07-25 20:54:21

+0

当撤消发生时,我重建UI,这是一个非常昂贵的操作(从原始的NSManagedObjects重建大量的自定义UIViews)。这最终会被优化。在这里很难提供一段代码(这是一个大型项目) - 对你有用吗? – 2012-07-25 20:56:27

+0

你在撤消什么? – Mundi 2012-07-25 22:02:33

回答

0

这里是停在我所有的崩溃的解决方案: 显然神奇纪录默认使用privateQueue并发性,如果你的代码是不是线程安全的,我想的东西不工作了。我所做的是从NSPrivateQueueConcurrencyType改变NSMainQueueConcurrencyType以下方法:

+ (NSManagedObjectContext *) MR_contextWithoutParent; 
{ 
    NSManagedObjectContext *context = [[self alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
return context; 
} 

我还发现了另一个细节,在这里工作: Core Data deleteObject: sets attributes to nil

我有什么需要做的就是将之前保存的管理对象上下文/删除一个管理对象。有点怪异和低效率,但节省很多麻烦。

0

标准的方式来撤销管理对象的插入是

[self.managedObjectContext deleteObject:theManagedObject]; 
// If you have saved already, you would need to save again. 

无需使用undo机制。这样,你的代码变得

  • 更具可读性(你说你明确地想要做什么)和
  • 不易出错(无不可预见的更复杂的API事件)。
+0

如果我对撤销操作的其余部分使用了撤消管理器,那么如何拦截删除操作并处理该自定义操作?似乎我将不得不跟踪我自己的撤销/重做堆栈 – 2012-07-26 21:18:09

+0

好吧,我误解了你在做什么。怎么样,在“陷阱!”部分,你只需在另一个线程上调度撤销? – Mundi 2012-07-26 21:22:45

+0

它似乎再也不会碰到陷阱部分了。我试图找出一个很好的方式来知道Core Data何时处理完毕。我在撤消之前尝试调用processChanges,但这似乎没有帮助。我甚至尝试了执行一个performSelect:withDelay:0以在撤消管理器当前撤销/重做的情况下跳到下一个运行循环: if(self.undoManager.isUndoing || self.undoManager.isRedoing) { NSLog (@“问题在这里!跳过”); [self performSelector:@selector(didRedoChange :) withObject:notification afterDelay:0]; } – 2012-07-26 21:33:05