我有一个Mac OS X应用程序,我想在用户强制关闭我的应用程序时检测或捕获事件。
起初我以为applicationWillTerminate
会完成这项工作,但它没有:
func applicationWillTerminate(_ aNotification: Notification) {
print("applicationWillTerminate called")
}
你知道我该怎么做吗?
主要归功于@caseynolan在这里投入工作以得出答案。不幸的是,事实上,所建议的方法存在一些重大问题。
首先,信号处理程序是按进程进行的。信号还有其他合法用途,安装处理程序会对他们的行为产生负面影响,而无需非常仔细的工作。
其次,信号中断线程,该线程可以做任何事情,包括保持锁定。持有锁的事物示例 - malloc/free,Objective-C 运行时。
这是一类称为"异步安全"的问题的一部分。如果你查看"man sigaction"(注意,sigaction
是一个比signal
大大改进的API),你会看到实际上有非常少的函数可以从信号处理程序安全调用。调用不安全的函数,如NSLog
,有时会起作用。但是,有时也会死锁,具体取决于线程当时正在做什么。
现在,我承认我对信号的经验不包括使用SIGTERM
.但是,由于它可以随时交付,因此它仍然会受到异步安全问题的影响,即使认为它不是崩溃。
简而言之:在信号处理程序中运行代码几乎肯定是不安全的,并且有时会死锁。而且,这些死锁将在其不可预测的执行点发生。
第三,除了SIGKILL之外,还有一些致命事件没有映射到信号。这可能无关紧要,具体取决于您需要多强的保证来检测流程终止。
我会做什么:
我认为您唯一安全的选择是使用哨兵流程。这个想法是你启动一个子进程,并在该子进程中观察父进程。如果/当该过程消失时,您可以执行代码。
这是一种安全、无死锁风险的监视任意进程退出的方法。但是,将操作移出进程可能具有挑战性。不幸的是,我相信这是其中一个必要的方面,如果你想让它是可靠的。
祝你好运!
免责声明:我不是专家,为这篇文章进行研究已经超出了我生锈的C/Objective-C知识的极限。我不知道苹果如何看待以下代码以提交到 App Store,所以 YMMV。
以下内容已被无耻地从维基百科中删除并重新设计:
#import <Foundation/Foundation.h>
/**
This will handle signals for us, specifically SIGTERM.
*/
void handleSignal(int sig) {
if (sig == SIGTERM) {
// Caught a SIGTERM
}
/*
SIGTERM is a clear directive to quit, so we exit
and return the signal number for us to inspect if we desire.
We can actually omit the exit(), and everything
will still build normally.
If you Force Quit the application, it will still eventually
exit, suggesting a follow-up SIGKILL is sent.
*/
exit(sig);
}
/**
This will let us set a handler for a specific signal (SIGTERM in this case)
*/
void setHandler() {
if (signal(SIGTERM, handleSignal) == SIG_ERR) {
NSLog(@"Failed to set a signal handler.");
} else {
NSLog(@"Successfully set a signal handler.");
}
}
在信号处理程序中仅调用异步安全函数。看这里。
您可以将上述内容放在 C/Objective-C 文件中,并通过桥接标头在 Swift 中使用它。在应用程序生命周期开始时的某个位置调用setHandler()
,例如在applicationDidFinishLaunching
中,您现在应该有机会在您的应用程序强制退出之前执行一些工作。我不知道你有多少时间在这里,所以我会尽可能减轻工作量(避免在这里开始关键任务的东西,我猜?
以下是一些背景信息:
在典型的戒烟中
退出程序实际上是 Apple Events 的一部分。
如果应用程序基于
NSDocument
,则行为取决于保存参数,该参数具有以下三个值之一:
NSSaveOptionsNo
:应用程序退出而不向任何文档发送关闭消息。NSSaveOptionsYes
:向每个未修改的文档发送一条关闭消息;向每个修改的文档发送以下消息:saveDocumentWithDelegate:didSaveSelector:contextInfo:
NSSaveOptionsAsk
:(如果事件中未提供保存参数,则这是默认值。如果有修改的文档 打开后,NSDocumentController
会向自身发送以下消息:reviewUnsavedDocumentsWithAlertTitle:cancellable:delegate:didReviewAllSelector:contextInfo:
如果应用程序不是基于
NSDocument
的,则会向应用程序委托发送以下消息(如果已实现):applicationShouldTerminate:
可以通过实现此方法来修改默认行为。
来源:可可应用程序如何处理 Apple 事件
强制退出期间
应用程序将发送一个SIGTERM
,您的应用程序可以捕获并处理该。理想情况下,应用程序将尽可能优雅地清理和退出,但这不是必需的,甚至可以忽略信号。
一个不耐烦的用户(我强烈怀疑Force Quit
最终会这样做)可能会发送一个SIGKILL
,这是不能忽视的,并且完全忽略了您作为开发人员可以停止的任何内容。
额外信息和资源:
强制退出在 OS X 中有什么作用?
OS X 会为"退出"和"强制退出"命令发送哪些信号?
可可应用程序如何处理 Apple 事件
西格特尔 vs. 西格基尔 - major.io
C 信号处理 - 维基百科