UndoManager 和多个 MOC

Posted

技术标签:

【中文标题】UndoManager 和多个 MOC【英文标题】:UndoManager and multiple MOC 【发布时间】:2014-02-14 14:08:23 【问题描述】:

我有 3 个 MOC。

    MainThread MOC 显示内容(使用 undomanager) 后台保存 MOC 将数据保存到磁盘(连接到存储) Backgorund-update MOC 从服务器下载数据,解析并稍后保存

他们是父子关系。

    后台更新 -> 1. MainThread -> 2. 后台保存(存储)

现在,当我从后台下载数据时,我需要在主线程上禁用 undomanager,以便它们不会被撤消 - 这可能是用户同时编辑某些内容的情况。

现在的问题是这是否正确。我在后台更新线程中有该代码

 //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

方块看起来像这样:

[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];
        ];

我之所以问,是因为我偶尔会遇到一些不一致的崩溃,而我真的找不到原因。

【问题讨论】:

【参考方案1】:

首先,关于发布的代码的一些基本观察。

    你应该只在绝对必要的时候使用performBlockAndWait...这几乎是永远不会的。

    您在子上下文中调用 performBlockAndWait,而在父上下文中调用 performBlockAndWait。你应该永远不要那样做。

    在具有专利上下文的上下文中使用performBlockAndWait 调用save: 几乎可以保证不会执行您认为的操作。它所做的只是保存到父上下文。

    您在同一上下文中的 performBlockAndWait 内调用 performBlockAndWait。没有错,因为该调用是可重入的。但是,另一个线索表明您的 CD 堆栈管理存在问题。

现在,一些建议可能会有所帮助。

在您的情况下,我建议您更改 MOC 层次结构。将私有队列 MOC 作为主队列 MOC 的父队列。这允许您的数据库更改异步完成。那里没有错。请记住,您必须将 save: 调用级联到父级或安排保存,因为保存主 MOC 只会将其数据向上复制堆栈到父上下文,并且不会触及底层数据库。

但是,我会采用该背景 MOC 并将其作为主 MOC 的子级删除。现在,您完全不必担心撤消管理器,您可以不用管它。

说到撤消管理器,我发现最好的撤消管理器是子上下文。我只会创建一个作为主要上下文的子上下文的上下文,并在其中进行所有更改。如果放弃更改,只需删除上下文。一切都被撤消了。您可以在该上下文中安装撤消管理器以进行增量撤消管理。

现在,如何处理正在执行某种类型的异步更新(可能来自某些 Web 服务)的后台上下文。我建议:

    将其父级设置为与主 MOC 相同。您将需要刷新主 MOC 以更改父级。这样做的缺点是,对数据库的任何更新都是通过同一个父 MOC 同步的,为主 MOC 留出了更多机会在获取时等待。

    将其直接连接到持久存储协调器,并使用通知合并更改。

最后,重新审视您的设计,看看您是否可以使用异步调用。你真的应该能够不打电话给performBlock,并且只在极少数情况下打电话给performBlockAndWait

【讨论】:

您好,感谢您提供的所有提示。我正在使用 performBlockAndWait,因为我在对应用程序进行压力测试期间能够产生一些崩溃。在主线程上进行压力测试 EDIT / SAVE 时在后台进行更新。使用 performBlock UndoManager 进入一些不稳定状态并在该过程中崩溃。我会阅读您的评论并根据您的建议重新考虑我的解决方案 - 我可能对此有一些疑问,但需要先分析一下:) 如果performBlockAndWait 正在防止崩溃......这意味着您在其他领域存在同步问题,可能无需阻塞操作即可解决。 接受答案 - 我花了一段时间才找到解决方案,但我最终丢弃了 undomanager 并在单独的上下文中做一些事情,如果用户取消,我会丢弃这些内容。这解决了我的大部分问题,谢谢。

以上是关于UndoManager 和多个 MOC的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 UndoManager 确定文档是不是有未保存的更改

带有异步或长时间运行任务的 UndoManager

如何将 undoManager 与核心数据实体一起使用

UndoManager 的 undo 方法在测试时执行 registerUndo 的次数

撤消托管对象删除

如何使宏“原子”