撤销所有在子视图控制器中所做的更改。

9

有两个实体:作者和书籍。作者具有属性authorName和一个to-many关系books。Book具有多个属性和一个关系author。有一个视图控制器(VCAuthor)用于编辑作者对象。子视图控制器(VCBook)用于编辑作者的书籍。只有一个managedobjectcontext。在VCBook类中,我将undomanager分组如下:

-(void)viewDidLoad
{    
    NSUndoManager *anUndoManager = [[NSUndoManager  alloc] init];
    [self.book.managedObjectContext setUndoManager:anUndoManager];
    [anUndoManager release];
    [self.book.managedObjectContext.undoManager beginUndoGrouping];
}

-(void)cancelAction:(id)sender
{
    NSLog(@"%@", self.author.authorName);
    [self.book.managedObjectContext.undoManager endUndoGrouping];
    [self.book.managedObjectContext.undoManager undoNestedGroup];
    self.book.managedObjectContext.undoManager = nil;
    NSLog(@"%@", self.author.authorName);    
    [self dismissModalViewControllerAnimated:YES];  
}

cancelAction是与VCBook中的取消按钮相关联的,用于撤消在VCBook中所做的所有更改。

问题在于:首先,在VCAuthor中,我使用UITextfiled authorNameTextField编辑了authorName,将其从Obama修改为Big Obama,并通过author.authorName = authorNameTextField.text在- (void)viewWillDisappear:(BOOL)animated{}方法中保存到MOC。然后,我进入子视图控制器VCBook来编辑作者的书籍,并单击取消按钮返回到VCAuthor。我发现作者名称仍然为Obama,这意味着预期的作者名称更改已被撤消。作者姓名的更改根本不在撤消组中,为什么会发生这种情况?ps.当我回到VCAuthor时,我肯定会重新加载数据。

我只是在撤消前后NSLog作者名称。在undo之前,作者名称已更改为Big Obama,在undo之后变为了Obama。


但我没有对更改进行分组,这里怎么能撤销呢? - lu yuan
其撤销管理器的默认行为 - Charan
如果我推送一个新的视图控制器来编辑作者名称,并在返回到VCAuthor时保存更改后的名称到实体中,则VCBook中的撤销方法将不会受到任何影响。 - lu yuan
这就是我说的,它没有保存新的名称,请调试它。 - Charan
你的意思是将它保存到持久存储中吗?不,我应该将保存操作留给VCAuthor中的保存按钮。我上一条评论中的保存只是通过author.authorName = authorNameTextField.text实现的。 - lu yuan
显示剩余2条评论
1个回答

9

有几个需要考虑的事情。首先,在这种情况下,我会使用单独的 MOC 而不是撤销管理器。具体来说,我会像这样做(假设使用 ARC - 如果需要,您可以进行映射)...

您必须有一些其他代码通过 setter 向 VC 提供书籍,因为您在 viewDidLoad 中访问它。我会将 viewDidLoad 更改为类似于此的内容...

-(void)viewDidLoad
{
    self.managedObjectContext = [[NSManagedObjectContext alloc] init];
    self.managedObjectContext.parentContext = self.book.managedObjectContext;
    // We are in the main thread, so we can safely access the main MOC
    // Basically, move our object pointer to book into our local MOC.
    NSError * error = nil;
    Book *book = [self.managedObjectContext existingObjectWithID:self.book.objectID error:&error];
    // handle nil return and/or error
    self.book = book;
    // Now, your access to book will be in the local MOC, and any changes
    // you make to the book or book.author will all be confined to the local MOC.
}

现在,你只需要调用即可。
[self.managedObjectContext save:&error];

在你的saveAndDismiss操作中,如果你不调用保存方法,那么所有的更改都不会被保存,它们只会自动消失。
编辑说明:上面的“保存”仅将对象状态移动到父上下文中。因此,“主”MOC现在具有来自“子”更改的更改,但尚未将任何更改保存到磁盘中。

1、如果我在一个单独的MOC中编辑书籍,就不能通过"[self.author addBookObject self.book];"将书籍添加到作者中。因为它们不在同一个MOC中。 2、在VCBook中,"[self.managedObjectContext save:&error];"不是我想要的,因为有其他原因。 3、当VCAuthor中的保存按钮被按下时,所有更改都将保存到持久存储中。 - lu yuan
1
你读了我发布的代码吗?首先,一次只能有一个视图控制器处于活动状态。根据你之前说的,这个特定的视图控制器正在编辑作者。其次,在我发布的代码中,“当前书籍”对象正在使用相同的MOC。最后,你的假设完全错误。保存作者上下文只会将这些更改推入父MOC中。直到该MOC保存,它们才会被保存。 - Jody Hagins
正确,保存一个子上下文只是将这些更改推送到父上下文。 直到“根”上下文保存它们才会保存到数据库中。 另外,我只是编造了saveAndDismiss来显示当完成时需要将上下文保存到父级...这一切都是因为如果您不这样做,在释放该上下文时所有在子上下文中进行的更改都将“消失”。 - Jody Hagins
如果保存失败,如果没有撤销管理器,您如何撤消部分失败的更改? - malhal
我想问一下现在你是怎么做的。你可以有一个撤销管理器,但我发现它是不必要的,并且浪费资源。临时MOC充当临时草稿本。让用户更改临时MOC中的数据,或者放弃所有更改,重新开始。同样,这取决于您试图解决的问题...我只需要在临时MOC上使用一次撤销管理器。当您为这些用例设计应用程序时,这并不是什么大问题。 - Jody Hagins
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接