如何解决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
相关产品推荐
相关产品推荐

