Swift 中基于调用的撤消管理器
Invocation-based Undo Manager in Swift
我在 Swift 中采用更复杂的基于调用的方法来撤消注册时遇到了麻烦(基于 NSHipster 文章 here。Apple 的文档仍然包含 [=31= 中的所有示例代码],并且调用设置的语义非常不同)。
我的 NSDocument
子类 Document
具有以下对模型对象进行操作的方法,我希望它可以撤消:
func rename(object: Any, to newName: String) {
// This is basically a protocol that requires implementing:
// var name: String { get set }
//
guard var namedObject = object as? EditorHierarchyDisplayable else {
return
}
// Register undo:
let undoController = undoManager?.prepare(withInvocationTarget: self) as? Document
undoController?.rename(object: namedObject, to: namedObject.name)
undoManager?.setActionName("Rename \(namedObject.localizedClassName)")
// Perform the change:
namedObject.name = newName
}
我发现上面的 undoController
是 nil
,因为尝试转换为 Document
失败了。如果我删除演员表(并注释掉对 undoController.rename(...
的调用),prepare(withInvocationTarget:)
returns 以下对象:
(lldb) print undoController
(Any?) $R0 = some {
payload_data_0 = 0x00006080000398a0
payload_data_1 = 0x0000000000000000
payload_data_2 = 0x0000000000000000
instance_type = 0x000060800024f0d8
}
(lldb) print undoController.debugDescription
(String) $R1 = "Optional(NSUndoManagerProxy)"
(lldb)
我错过了什么?
我认为基本的混淆是 prepare(withInvocationTarget:)
returns 一个 代理对象 (恰好是撤消管理器本身,但这是一个实现细节).这个想法是你向这个代理对象发送你发送的相同消息来撤消操作,而不是执行它们(因为它不是实际的对象),它在内部捕获这些调用并保存它们以备后用。
所以你的代码真的应该像这样开始:
let selfProxy: Any = undoManager?.prepare(withInvocationTarget: self)
这在 Objective-C 中非常有效,因为 "catchall" 类型 (id) 的类型检查非常宽松。但是 Swift 中的等效 Any
class 更加严格,并且不适合相同的技术,如果有的话。
见
我在 Swift 中采用更复杂的基于调用的方法来撤消注册时遇到了麻烦(基于 NSHipster 文章 here。Apple 的文档仍然包含 [=31= 中的所有示例代码],并且调用设置的语义非常不同)。
我的 NSDocument
子类 Document
具有以下对模型对象进行操作的方法,我希望它可以撤消:
func rename(object: Any, to newName: String) {
// This is basically a protocol that requires implementing:
// var name: String { get set }
//
guard var namedObject = object as? EditorHierarchyDisplayable else {
return
}
// Register undo:
let undoController = undoManager?.prepare(withInvocationTarget: self) as? Document
undoController?.rename(object: namedObject, to: namedObject.name)
undoManager?.setActionName("Rename \(namedObject.localizedClassName)")
// Perform the change:
namedObject.name = newName
}
我发现上面的 undoController
是 nil
,因为尝试转换为 Document
失败了。如果我删除演员表(并注释掉对 undoController.rename(...
的调用),prepare(withInvocationTarget:)
returns 以下对象:
(lldb) print undoController
(Any?) $R0 = some {
payload_data_0 = 0x00006080000398a0
payload_data_1 = 0x0000000000000000
payload_data_2 = 0x0000000000000000
instance_type = 0x000060800024f0d8
}
(lldb) print undoController.debugDescription
(String) $R1 = "Optional(NSUndoManagerProxy)"
(lldb)
我错过了什么?
我认为基本的混淆是 prepare(withInvocationTarget:)
returns 一个 代理对象 (恰好是撤消管理器本身,但这是一个实现细节).这个想法是你向这个代理对象发送你发送的相同消息来撤消操作,而不是执行它们(因为它不是实际的对象),它在内部捕获这些调用并保存它们以备后用。
所以你的代码真的应该像这样开始:
let selfProxy: Any = undoManager?.prepare(withInvocationTarget: self)
这在 Objective-C 中非常有效,因为 "catchall" 类型 (id) 的类型检查非常宽松。但是 Swift 中的等效 Any
class 更加严格,并且不适合相同的技术,如果有的话。
见