如何在Mac OS X应用程序上检测强制关闭

时间:2018-03-06 13:33:30

标签: swift macos event-handling

我有一个Mac OS X应用程序,我想在用户强制关闭我的应用程序时检测或捕获事件。

起初我以为applicationWillTerminate会做这项工作,但事实并非如此:

func applicationWillTerminate(_ aNotification: Notification) {
    print("applicationWillTerminate called")
}

你知道我该怎么办?

2 个答案:

答案 0 :(得分:3)

主要感谢@caseynolan将工作放在这里以提供答案。不幸的是,实际上,建议的方法存在一些重大问题。

首先,信号处理程序是每个进程。还有其他合法的信号使用方式,安装处理程序将对其行为产生负面影响,而无需进行非常仔细的工作。

第二,信号中断线程,该线程可以做任何事情,包括保持锁。持有锁的示例-malloc / free,Objective-C运行时。

这是一类称为“异步安全性”的问题的一部分。如果您查看“ man sigaction”(请注意,sigaction是对signal的API的改进),您会发现实际上只有很少一部分函数可以安全地从信号处理程序中调用。有时会调用诸如NSLog之类的不安全函数。但是,有时也会死锁,具体取决于线程当时在做什么。

现在,我承认我在信号方面的经验不包括使用SIGTERM。但是,由于可以随时交付,因此即使它不是崩溃,它仍然会遇到异步安全问题。

简而言之:在信号处理程序中运行代码几乎肯定是不安全的,并且有时会死锁。而且,这些死锁将在执行过程的不可预测的时刻发生。

第三,在SIGKILL之上有一些致命事件未映射到信号。这可能无关紧要,具体取决于您需要多大的保证才能检测到进程终止。

我该怎么做:

我认为您唯一安全的选择是使用前哨程序。想法是您启动一个子进程,然后在该子进程中观察父进程。如果该进程消失了,则可以执行代码。

这是一种安全,无死锁的方式,可以监视任意进程退出。但是,将您的操作移到流程外可能会带来挑战。不幸的是,如果您希望它可靠,我相信这是一个必要的方面。

祝你好运!

答案 1 :(得分:0)

  

免责声明:我不是专家,研究这篇文章已经扩展了我生锈的C / Objective-C知识的极限。我不知道Apple如何查看以下代码以提交给App Store,因此YMMV。

following has been shamelessly ripped from Wikipedia并重做:

#import <Foundation/Foundation.h>

/**
 This will handle signals for us, specifically SIGTERM.
 */
void handleSignal(int sig) {
    if (sig == SIGTERM) {
        NSLog(@"Caught a SIGTERM.");
    } else {
        NSLog(@"Caught a different SIG.");
    }

    /*
      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文件中,并通过Bridging Header在Swift中使用它。在应用程序生命周期的开头某处调用setHandler(),例如在applicationDidFinishLaunching,你应该有机会在你的应用程序强制退出之前做一些工作。我不知道你有多少时间来到这里,所以我会尽可能保持工作量(避免在这里启动关键任务的东西,我猜?)。

以下是一些背景信息:

在典型的退出

Quit程序实际上是Apple Events的一部分。

  
      
  • 如果应用程序基于NSDocument,则行为取决于保存参数,该参数具有以下三个值之一:

         
        
    • NSSaveOptionsNo :应用程序退出而不向任何文档发送关闭消息。
    •   
    • NSSaveOptionsYes :每个未修改的文档都会发送一条密切消息;每个修改过的文档都会发送以下消息:   saveDocumentWithDelegate:didSaveSelector:contextInfo:
    •   
    • NSSaveOptionsAsk :(如果事件中未提供保存参数,则这是默认值。)如果有修改过的文档   打开,NSDocumentController发送此消息:   reviewUnsavedDocumentsWithAlertTitle:cancellable:delegate:didReviewAllSelector:contextInfo:
    •   
  •   
  • 如果应用程序不是基于NSDocument,则会向应用程序委托发送此消息(如果已实现):   applicationShouldTerminate:

  •   
     

您可以通过实施此方法来修改默认行为。

     

来源How Cocoa Applications Handle Apple Events

强制戒烟期间

应用程序发送SIGTERM,您的应用程序可以捕获并处理该应用程序。理想情况下,应用程序会尽可能优雅地清理和退出,但这不是必需的,甚至可以忽略信号。

一个不耐烦的用户(我强烈怀疑 Force Quit最终会这样做)可能会发送一个SIGKILL,这是不容忽视的在开发人员可以停止的任何事情上都会讨论。

额外信息和资源:

What does Force Quit do in OS X?

What signals does OS X send for the Quit and Force Quit commands?

How Cocoa Applications Handle Apple Events

SIGTERM vs. SIGKILL - major.io

C Signal Handling - Wikipedia