如何在 Swift 中实现 NSDocument 方法 -canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo: ?

Dou*_*iot 5 cocoa objective-c nsdocument appkit swift

在我的应用程序中,一个NSDocument子类关键任务硬件 - 用户真的不想意外关闭文档!所以,我已经实现canCloseDocumentWithDelegate…NSAlert在关闭之前显示和询问。

我现在试图在用 Swift 编写的应用程序中实现同样的事情。

由于答案是异步出现的,“应该关闭”结果将传递给委托的回调,而不是简单地返回。在 的文档中-canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo:,它说:

shouldCloseSelector 回调方法应具有以下签名:

- (void)document:(NSDocument *)doc shouldClose:(BOOL)shouldClose contextInfo:(void *)contextInfo

所以,由于有 3 个不同类型的参数,我不能使用简单的performSelector:withObject:样式方法——你必须使用 NSInvocation。请注意,委托的类型是id,并且上面的签名没有出现在任何正式协议中——您不能简单地正常调用该方法。(有关如何执行此操作的示例,请参阅此邮件列表帖子

现在,问题是,Swift 中不允许使用 NSInvocation!参见 Swift 博客“NSMethodSignature 发生了什么”

将 Cocoa 框架引入 Swift 为我们提供了一个独特的机会,以全新的视角看待我们的 API。我们发现我们觉得不符合 Swift 目标的类,通常是因为我们将安全放在首位。例如,一些与动态方法调用相关的类在 Swift 中没有公开,即NSInvocationNSMethodSignature

这听起来是件好事,但是当一个简单的NSDocumentAPI 仍然需要 NSInvocation时就会失败!对整个问题的真正解决方案是让 ApplecanCloseDocument…使用块回调引入新的API。但在那之前,最好的解决方案是什么?

cjc*_*eld 5

您可以使用一些低级运行时函数来解决这个问题:

override func canCloseDocumentWithDelegate(delegate: AnyObject, shouldCloseSelector: Selector, contextInfo: UnsafeMutablePointer<Void>) {

    let allowed = true // ...or false. Add your logic here.

    let Class: AnyClass = object_getClass(delegate)
    let method = class_getMethodImplementation(Class, shouldCloseSelector)

    typealias signature = @convention(c) (AnyObject, Selector, AnyObject, Bool, UnsafeMutablePointer<Void>) -> Void
    let function = unsafeBitCast(method, signature.self)

    function(delegate, shouldCloseSelector, self, allowed, contextInfo)
}
Run Code Online (Sandbox Code Playgroud)

如果您需要将此行为移至另一种方法(例如,在工作表获得用户确认后),只需将委托和 shouldCloseSelector 存储在属性中,以便您稍后访问它们。