Swift中基于调用的撤消管理器 [英] Invocation-based Undo Manager in Swift

查看:112
本文介绍了Swift中基于调用的撤消管理器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在采用更复杂的基于调用的方法来在Swift中撤消注册时遇到了麻烦(基于NSHipster文章此处.Apple的文档在Objective-C中仍然具有所有示例代码,并且调用设置的语义也有很大不同).

I'm having trouble adopting the more complex invocation-based approach to undo registration in Swift (based on NSHipster article here. Apple's docs still have all sample code in Objective-C, and the semantics are very different for the invocation setup).

我的NSDocument子类Document具有对模型对象进行操作的以下方法,我希望使其成为可撤消的:

My NSDocument subclass Document has the following method that operates on the model objects, which I wish to make undoable:

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:)返回以下对象:

What I have found out is that undoController above is nil, becuase the atempted cast to Document fails. If I remove the cast (and comment out the call to undoController.rename(...), prepare(withInvocationTarget:) returns the folowing object:

(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:)返回一个 proxy对象(恰好是撤消管理器本身,但这是实施细节).想法是,向该代理对象发送与撤消操作相同的消息,但不执行它们(因为它不是实际对象),而不是执行它们,而是在内部捕获这些调用并将其保存以备后用.

I think the basic confusion is that prepare(withInvocationTarget:) returns a proxy object (that happens to be the undo manager itself, but that's an implementation detail). The idea is that you send this proxy object the same message(s) you send to undo the action, but instead of executing them (because it's not the actual object), it internally captures those invocations and saves them for later.

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

So your code should really start out something like this:

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

这在Objective-C中效果很好,因为"catchall"类型(id)具有非常宽松的类型检查.但是,Swift中等效的Any类要严格得多,即使有的话也不会采用相同的技术.

This works great in Objective-C because the "catchall" type (id) has very lax type checking. But the equivalent Any class in Swift is much more stringent and does not lend itself to the same technique, if at all.

请参见在Swift 3中使用NSUndoManager和.prepare(withInvocationTarget:)

这篇关于Swift中基于调用的撤消管理器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆