【问题标题】:Invocation-based Undo Manager in Swift [duplicate]Swift中基于调用的撤消管理器[重复]
【发布时间】:2017-08-21 03:57:17
【问题描述】:

我在采用更复杂的基于调用的方法来撤消 Swift 中的注册时遇到了麻烦(基于 NSHipster 文章 here。Apple 的文档仍然包含 Objective-C 中的所有示例代码,并且语义对于调用设置)。

我的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
}

我发现上面的undoControllernil,因为尝试转换为Document 失败。如果我删除演员表(并注释掉对undoController.rename(... 的调用),prepare(withInvocationTarget:) 将返回以下对象:

(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:) 的文档说“返回自我”。 selfundoManager。在 NSHipster 文章的底部写着“本文使用 Swift 1.0 版。”。
  • 是的,文档也是如此。但它将返回的值转换为as ViewController(我假设这变成了Document)。此外,as 在 Swift 2+ 中变为 as?
  • NSUndoManager 转换为Document 是错误的。 Swift 1 不在乎,但 Swift 3 拒绝这样做。
  • 那么,我如何在返回的代理上调用我的Document 方法rename(object:to:) 呢? Objective-C 允许你向任何对象发送任何消息(至少在编译时),但是 Swift 是强类型的......

标签: swift cocoa nsundomanager


【解决方案1】:

我认为基本的混淆是prepare(withInvocationTarget:) 返回一个代理对象(恰好是撤消管理器本身,但这是一个实现细节)。这个想法是,您向此代理对象发送与您发送以撤消操作相同的消息,但不是执行它们(因为它不是实际对象),而是在内部捕获这些调用并保存它们以供以后使用。

所以你的代码真的应该像这样开始:

let selfProxy: Any = undoManager?.prepare(withInvocationTarget: self)

这在 Objective-C 中非常有效,因为“catchall”类型 (id) 的类型检查非常松懈。但是 Swift 中等效的 Any 类要严格得多,并且不适合使用相同的技术。

Using NSUndoManager and .prepare(withInvocationTarget:) in Swift 3

【讨论】:

  • 令我惊讶的是,我链接为“重复源”的答案中使用的方法,即(target as AnyObject).methodOfMyTargetClass() 确实编译时没有错误或警告,即使targetAnyObject不是 MyClass。我虽然 Swift 的严格类型系统不允许它编译。无论如何,我搬到了更新的、基于闭包的registerUndo(withTarget:handler:)
  • “这在 Objective-C 中非常有效,因为“catchall”类型 (id) 的类型检查非常松懈。” - 确实;我过去在 Objective-C 代码中使用过NSUndoManager
  • 由于某种原因,我错过了您链接的问题/答案;谢谢。
猜你喜欢
  • 1970-01-01
  • 2015-02-27
  • 2013-10-04
  • 1970-01-01
  • 2018-09-12
  • 2012-12-21
  • 2021-09-25
  • 1970-01-01
  • 2014-12-21
相关资源
最近更新 更多