在我的应用程序中,ngMessages
子类任务关键型硬件 - 用户真的不想意外关闭文档!所以,我已经实施NSDocument
来展示canCloseDocumentWithDelegate…
并在结束前询问。
我现在正试图在用Swift编写的应用程序中实现同样的东西。
由于答案是异步的,因此“应该关闭”结果会传递给委托上的回调,而不是简单地返回。在NSAlert
的文档中,它说:
shouldCloseSelector回调方法应具有以下签名:
-canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo:
因此,由于有3个不同类型的参数,我不能使用简单的- (void)document:(NSDocument *)doc shouldClose:(BOOL)shouldClose contextInfo:(void *)contextInfo
样式方法 - 您必须使用NSInvocation。请注意,委托的类型为performSelector:withObject:
,上面的签名不会出现在任何正式协议中 - 您不能简单地调用该方法。 (请参阅此mailing list post,了解如何执行此操作)
现在问题是,Swift中不允许使用NSInvocation!请参阅Swift博客“What Happened to NSMethodSignature”:
将Cocoa框架引入Swift给了我们一个独特的机会,以全新的视角审视我们的API。我们发现了一些我们认为不适合Swift目标的课程,最常见的是我们给予安全的优先权。例如,某些与动态方法调用相关的类未在Swift中公开,即
id
和NSInvocation
。
这听起来像是一件好事,但是当一个简单的NSMethodSignature
API需要NSInvocation时,它就会倒下!这整个问题的真正解决方案是Apple使用块回调引入新的NSDocument
API。但在此之前,什么是最好的解决方案?
答案 0 :(得分:4)
您可以使用一些低级运行时函数来解决此问题:
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存储在属性中,以便稍后访问它们。
答案 1 :(得分:2)
因此,我目前的解决方案是继续使用Objective-C来执行NSInvocation。 NSDocument
子类是用Swift编写的,并调用Objective-C类来完成这项工作。
由于Swift中不存在NSInvocation
,我真的看不到任何其他方式。
- (void)respondToCanClose:(BOOL)shouldClose delegate:(id)delegate selector:(SEL)shouldCloseSelector contextInfo:(void *)contextInfo
{
NSDocument *doc = self;
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[delegate methodSignatureForSelector:shouldCloseSelector]];
invocation.target = delegate;
invocation.selector = shouldCloseSelector;
[invocation setArgument:&doc atIndex:2]; // Note index starts from 2 - 0 & 1 are self & selector
[invocation setArgument:&shouldClose atIndex:3];
[invocation setArgument:&contextInfo atIndex:4];
[invocation invoke];
}
您可以看到我的示例项目: https://github.com/DouglasHeriot/canCloseDocumentWithDelegate
另一个选择是使用Objective-C来回绕objc_msgSend
,这在Swift中也是不可用的。 http://www.cocoabuilder.com/archive/cocoa/87293-how-does-canclosedocumentwithdelegate-work.html#87295
答案 2 :(得分:0)
至少从Swift 4.1开始,您可以执行以下操作:
// Application Logic
myDocument.canClose(
withDelegate: self,
shouldClose: #selector(MyClass.document(_:_:_:)),
contextInfo: nil)
...
// Handler
@objc
private func document(_ doc: NSDocument, _ shouldClose: Bool, _ contextInfo: UnsafeMutableRawPointer) {
...
}
答案 3 :(得分:-1)
以下是我从Apple Developer Technical Support收到的关于此问题的Swift解决方案:
override func canCloseDocumentWithDelegate(delegate: AnyObject, shouldCloseSelector: Selector, contextInfo: UnsafeMutablePointer<Void>) {
super.canCloseDocumentWithDelegate(self, shouldCloseSelector: "document:shouldClose:contextInfo:", contextInfo: contextInfo)
}
func document(doc:NSDocument, shouldClose:Bool, contextInfo:UnsafeMutablePointer<Void>) {
if shouldClose {
// <Your clean-up code>
doc.close()
}
}