NSUndoManager 转换 NSUndoManagerProxy 在 Swift 代码中崩溃

NSUndoManager casting NSUndoManagerProxy crash in Swift code

在我们的应用程序中,我们使用以下代码:

let lInvocationTarget = lUndoManager.prepare(withInvocationTarget: self)
let _ = (lInvocationTarget as! MyObjectType).myMethod(_: self.opacity, undoManager: lUndoManager)

编译时没有警告,在 macOS 10.12 Sierra 下运行良好。然而,它在 10.9 - 10.11(Mavericks 到 El Capitan)的运行时崩溃。 崩溃报告通知:

Could not cast value of type 'NSUndoManagerProxy' (0x7fff76d6d9e8) to 'MyObjectType' (0x108b82218).

然后我将代码重写为:

if let lInvocationTarget = lUndoManager.prepare(withInvocationTarget: self) as? MyObjectType {
    let _ = lInvocationTarget.setOpacity(_: self.opacity, undoManager: lUndoManager)
}

然后它没有崩溃,但是撤消根本不起作用。 最后一种写法直接来自 Apples documentation,因此显然行为在 Swift 3 或 10.12 SDK 中发生了变化。我正在使用 Xcode 8.2 与 Swift 3 和 SDK 10.12

registerUndo(withTarget, selector:, object:) 不适合,因为我有很多其他参数更多的可撤消方法。真的不想将它们包装在字典中。即使现在选择器类型安全,我仍然不喜欢它们。 还有基于块的 API (registerUndo(withTarget: handler:) ),但不幸的是,这仅适用于 10.11+.

有人遇到过同样的问题吗?更重要的是:有人想出出路吗?

听到您的第一个代码 works on macOS 10.12,我感到非常惊讶。方法 prepare(withInvocationTarget:) 一直是 this-hardly-works-in-Swift 的事情。不仅返回的代理对象不是原始 class 的实例,而且该对象也不是 NSObject 的后代(至少在某些以前的 OS X 中)。

无论如何,这是一件值得尝试的事情:

let lInvocationTarget = lUndoManager.prepare(withInvocationTarget: self)
_ = (lInvocationTarget as AnyObject).myMethod(self.opacity, undoManager: lUndoManager)

我发现 registerUndoWithTarget:handler: 现在更好 API。