You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决UndoManager/NSUndoManager适配异步或长时间运行任务的问题

基于UndoManager/NSUndoManager处理异步长耗时任务的问题

我在使用UndoManager / NSUndoManager处理异步或长时间运行任务时遇到问题,目前已有可行方案但复杂度太高,远超出这类常见问题的合理实现成本,因此我将发布现有方案同时希望能获得更优解法。

问题1

可撤销任务无法在当前runloop内完成,这类任务可以是带有异步回调的短操作,也可以是需要展示进度指示器、甚至支持取消选项的长时间运行操作。

问题2

可撤销任务可能失败或被取消,更糟糕的是重做(redo)任务也可能失败。示例:我移动了一个文件,执行撤销操作时发现该文件已不在新路径下,此时就不应该将重做任务重新放回撤销栈。

已尝试方案

思路1

我尝试在任务完成时再注册undo/redo,毕竟未完成、已取消或已失败的操作本身就不支持撤销。但这种配置下无法让操作和它的撤销操作正确配对:重做功能失效。示例:用户触发文件复制,复制操作结束后我向UndoManager注册该操作;用户选择撤销,我再次等待撤销操作完成后再向UndoManager注册,此时UndoManager无法识别刚完成的文件删除操作其实是之前复制操作的逆向操作,它不会向用户提供重做复制的选项,反而会提供撤销删除的选项。

思路2

关闭自动撤销分组。但我不知道如何针对长时间运行操作实现该方案,毕竟其他绝大多数任务我都需要使用自动分组功能。
我尝试给带异步回调的简单操作使用该方案时抛出了报错:endUndoGrouping called with no matching begin,相关代码如下:

let assets = PHAsset.fetchAssets(in: album, options: nil)
let parent = PHCollectionList.fetchCollectionListsContaining(album, options: nil).firstObject

if let undoManager = undoManager {
    undoManager.groupsByEvent = false
    undoManager.beginUndoGrouping()

    let isUndoManagerOperation = undoManager.isUndoing || undoManager.isRedoing
    let targetSelf = Controller.self as AnyObject

    undoManager.registerUndo(withTarget: targetSelf) { [weak undoManager] targetSelf in
        Controller.createAlbum(for: assets, title: album.localizedTitle, parent: parent, with: undoManager, completionHandler: nil)
    }

    if !isUndoManagerOperation {
        undoManager.setActionName(NSLocalizedString("Delete Album", comment: "Undoable action: Delete Album"))
    }
}

PHPhotoLibrary.shared().performChanges {
    PHAssetCollectionChangeRequest.deleteAssetCollections(NSArray.init(object: album))
} completionHandler: { (success, error) in
    DispatchQueue.main.async {
        undoManager?.endUndoGrouping()
        undoManager?.groupsByEvent = true
    }
}

内容的提问来源于stack exchange,提问作者Pierre Bernard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 09:36:03