我正在尝试为针对MacOS制作的UI应用程序捕获SIGINT。在应用程序委托类中,我看到以下方法:
func applicationWillTerminate(_ aNotification: Notification) {
}
Run Code Online (Sandbox Code Playgroud)
但是,Ctrl+ C,SIGINT,永远不会陷入这里。在互联网上阅读表明,不能保证执行此功能,尤其是在应用程序在后台运行时。
我可以在应用程序委托中做什么以捕获SIGINT?还是我还有其他地方可以捕获中断,以便可以适当地关闭资源?
Charles的回答是正确的,但是他的警告(“确保仅从处理程序中调用可重入函数”)是一个极端的限制。使用kqueue和可以将信号处理重定向到更安全的环境CFFileDescriptor。
TN2050技术说明:在没有轮询的情况下观察过程寿命是另一个主题,但它说明了该技术。苹果在那里以这种方式描述了查尔斯的警告:
监听信号可能很棘手,因为与信号处理程序关联的执行环境古怪。具体来说,如果您安装了信号处理程序(使用signal或sigaction),则必须非常小心在该处理程序中执行的操作。从信号处理程序调用的函数很少。例如,使用
malloc!分配内存是不安全的。sigaction手册页上列出了可从信号处理程序中安全使用的功能(异步信号安全功能)。
在大多数情况下,您必须采取额外的步骤将传入的信号重定向到更合理的环境。
我从那里获取了代码说明,并对其进行了修改以进行处理SIGINT。抱歉,是Objective-C。这是一次性设置代码:
// Ignore SIGINT so it doesn't terminate the process.
signal(SIGINT, SIG_IGN);
// Create the kqueue and set it up to watch for SIGINT. Use the
// EV_RECEIPT flag to ensure that we get what we expect.
int kq = kqueue();
struct kevent changes;
EV_SET(&changes, SIGINT, EVFILT_SIGNAL, EV_ADD | EV_RECEIPT, NOTE_EXIT, 0, NULL);
(void) kevent(kq, &changes, 1, &changes, 1, NULL);
// Wrap the kqueue in a CFFileDescriptor. Then create a run-loop source
// from the CFFileDescriptor and add that to the runloop.
CFFileDescriptorContext context = { 0, self, NULL, NULL, NULL };
CFFileDescriptorRef kqRef = CFFileDescriptorCreate(NULL, kq, true, sigint_handler, &context);
CFRunLoopSourceRef rls = CFFileDescriptorCreateRunLoopSource(NULL, kqRef, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
CFFileDescriptorEnableCallBacks(kqRef, kCFFileDescriptorReadCallBack);
CFRelease(kqRef);
Run Code Online (Sandbox Code Playgroud)
这是实现上述sigint_handler引用的回调的方法:
static void sigint_handler(CFFileDescriptorRef f, CFOptionFlags callBackTypes, void *info)
{
struct kevent event;
(void) kevent(CFFileDescriptorGetNativeDescriptor(f), NULL, 0, &event, 1, NULL);
CFFileDescriptorEnableCallBacks(f, kCFFileDescriptorReadCallBack);
// You've been notified!
}
Run Code Online (Sandbox Code Playgroud)
请注意,此技术要求您在有兴趣处理SIGINT(也许是应用程序的生命周期)并维护/运行其运行循环的线程上运行安装代码。系统出于自身目的创建的线程(例如,服务于Grand Central Dispatch队列的线程)不适合此目的。
该应用程序的主线程将正常工作,您可以使用它。但是,如果主线程锁定或变得无响应,则表示它不在为它的运行循环提供服务,并且SIGINT不会调用该处理程序。由于SIGINT经常被用来精确地中断这种卡住的进程,因此主线程可能不合适。
因此,您可能想产生一个自己的线程只是为了监视此信号。它不应该做任何其他事情,因为其他任何事情也可能导致它卡住。即使在那里,也存在问题。您的处理程序函数将在您的该后台线程上调用,并且主线程可能仍被锁定。在系统库中,有很多东西仅是主线程的,您将无能为力。但是与POSIX风格的信号处理程序相比,您将拥有更大的灵活性。
我应该补充一点,GCD的调度源也可以监视UNIX信号,并且更易于使用,尤其是从Swift中。但是,它们不会预先创建运行处理程序的专用线程。该处理程序将被提交到队列。现在,您可以指定一个高优先级/高QOS队列,但是我不确定如果该进程已经运行了多个失控线程,则该处理程序将运行。也就是说,实际上在高优先级队列上运行的任务将优先于低优先级线程或队列,但是启动新任务可能不会。我不确定。
| 归档时间: |
|
| 查看次数: |
356 次 |
| 最近记录: |