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

如何解决UndoManager非Sendable类型的编译器警告问题

解决全局Actor中传递UndoManager的编译器警告

问题核心

警告Non-sendable type 'UndoManager?' passed in call to global actor 'MyActor'-isolated function cannot cross actor boundary的原因是:UndoManager未遵循Sendable协议,而全局Actor隔离的函数要求参数必须是Sendable类型(确保跨actor传递时的线程安全)。同时要注意,UndoManager本身设计为仅在主线程操作,直接在Actor上下文调用它还会有线程安全风险。

解决方法

方法一:分离业务逻辑与撤销注册(推荐)

将Actor中的业务逻辑和撤销操作注册分离,让撤销操作始终在主线程执行,避免跨actor传递UndoManager:

@globalActor actor MyActor {
    static let shared = MyActor()
}

class MyClass {
    @MyActor func doSomething() -> (() -> Void) {
        // 执行你的业务逻辑(在MyActor上下文)
        print("执行doSomething")
        
        // 返回反向操作的闭包
        return { [weak self] in
            Task { await self?.reverseSomething() }
        }
    }

    @MyActor func reverseSomething() -> (() -> Void) {
        // 执行反向业务逻辑(在MyActor上下文)
        print("执行reverseSomething")
        
        // 返回正向操作的闭包
        return { [weak self] in
            Task { await self?.doSomething() }
        }
    }
}
 
struct MyView: View {
   @Environment(\.undoManager) private var undoManager: UndoManager?
   let myObject: MyClass

   var body: some View {
        Button("Do something") {
            Task {
                // 在Actor上下文执行业务逻辑,获取撤销闭包
                let undoAction = await myObject.doSomething()
                // 切回主线程注册撤销操作
                DispatchQueue.main.async {
                    undoManager?.registerUndo(withTarget: self) { _ in
                        undoAction()
                    }
                }
            }
        }
   }
}

这种方式完全遵循UndoManager的主线程要求,同时避免了跨actor传递非Sendable类型,从根源解决警告和线程安全问题。

方法二:标记UndoManager为@unchecked Sendable

如果需要保留原有的参数传递方式,可以扩展UndoManager让它遵循@unchecked Sendable(手动保证线程安全),同时确保所有对UndoManager的操作都在主线程执行:

// 扩展UndoManager,标记为@unchecked Sendable
extension UndoManager: @unchecked Sendable {}

@globalActor actor MyActor {
    static let shared = MyActor()
}

class MyClass {
    @MyActor func doSomething(undoManager: UndoManager?) {
        // 执行你的业务逻辑(在MyActor上下文)
        print("执行doSomething")
        
        // 切回主线程注册撤销操作
        DispatchQueue.main.async { [weak self, undoManager] in
            undoManager?.registerUndo(withTarget: self) { target in
                Task {
                    await target?.reverseSomething(undoManager: undoManager)
                }
            }
        }
    }

    @MyActor func reverseSomething(undoManager: UndoManager?) {
        // 执行反向业务逻辑(在MyActor上下文)
        print("执行reverseSomething")
        
        // 切回主线程注册撤销操作
        DispatchQueue.main.async { [weak self, undoManager] in
            undoManager?.registerUndo(withTarget: self) { target in
                Task {
                    await target?.doSomething(undoManager: undoManager)
                }
            }
        }
    }
}
 
struct MyView: View {
   @Environment(\.undoManager) private var undoManager: UndoManager?
   let myObject: MyClass

   var body: some View {
        Button("Do something") {
            Task {
                await myObject.doSomething(undoManager: undoManager)
            }
        }
   }
}

注意:使用@unchecked Sendable意味着你需要自己保证UndoManager的线程安全,必须确保所有操作都在主线程执行,否则会引发崩溃或异常。

内容的提问来源于stack exchange,提问作者Philip Pegden

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 06:06:19