如何在 Swift 中实现 NSDocument 方法 -canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo: ? [英] How can you implement the NSDocument method -canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo: in Swift?

查看:44
本文介绍了如何在 Swift 中实现 NSDocument 方法 -canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo: ?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用程序中,一个 NSDocument 子类关键任务硬件 - 用户真的不希望意外关闭文档!所以,我实现了 canCloseDocumentWithDelegate... 来显示一个 NSAlert 并在关闭前询问.

In my application, a NSDocument subclass mission-critical hardware – users really don’t want to close a document by accident! So, I’ve implemented canCloseDocumentWithDelegate… to show an NSAlert and ask before closing.

我现在正尝试在用 Swift 编写的应用程序中实现同样的功能.

I am now trying to implement this same thing in an application written in Swift.

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

Since the answer comes asynchronously, the "should close" result is passed to a callback on a delegate, and not simply returned. In the documentation for -canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo:, it says:

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

The shouldCloseSelector callback method should have the following signature:

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

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

So, as there’s 3 arguments of different types, I cannot use the simple performSelector:withObject: style methods – you have to use NSInvocation. Note that the delegate is of type id, and the signature above does not appear in any formal protocol – you can’t simply call the method normally. (See this mailing list post for example of how this should be done)

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

Now, the issue is, NSInvocation is not allowed in Swift! See Swift blog "What Happened to NSMethodSignature":

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

Bringing the Cocoa frameworks to Swift gave us a unique opportunity to look at our APIs with a fresh perspective. We found classes that we didn't feel fit with the goals of Swift, most often due to the priority we give to safety. For instance, some classes related to dynamic method invocation are not exposed in Swift, namely NSInvocation and NSMethodSignature.

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

That sounds like a good thing, but falls down when a simple NSDocument API requires NSInvocation still! The real solution to this whole problem would be for Apple to introduce a new canCloseDocument… API using a block callback. But until that happens, what’s the best solution?

推荐答案

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

You can solve this with some low level runtime functions:

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)
}

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

If you need to move this behaviour to another method (eg. after a sheet gets confirmation from the user), simply store the delegate and shouldCloseSelector in properties so you can access them later.

这篇关于如何在 Swift 中实现 NSDocument 方法 -canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo: ?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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