Swift中基于调用的撤消管理器

时间:2017-08-21 03:57:17

标签: swift cocoa nsundomanager

我在采用更复杂的基于调用的方法来撤销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) 

我缺少什么?

1 个答案:

答案 0 :(得分:1)

我认为基本的困惑是=返回代理对象(恰好是撤销管理器本身,但这是一个实现细节)。这个想法是你发送这个代理对象你发送的相同消息撤消动作,但不是执行它们(因为它不是实际的对象),它在内部捕获这些调用并保存它们以供以后使用。

所以你的代码应该真正开始这样的事情:

prepare(withInvocationTarget:)

这在Objective-C中效果很好,因为“catchall”类型(id)具有非常宽松的类型检查。但是,Swift中的等价let selfProxy: Any = undoManager?.prepare(withInvocationTarget: self) 类更加严格,并且如果有的话,也不适用于相同的技术。

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