Mac端从程序坞退出应用偶发触发SIGTERM而非[NSApp terminate:]问题
根因分析
- Dock触发的正常退出流程不会直接发送
SIGTERM,你遇到的SIGTERM是macOS的无响应兜底机制:系统向应用发送终止请求后,若应用在规定时间内未完成退出流程,系统会判定应用无响应,发送SIGTERM强制终止。由于你没有注册SIGTERM信号处理器,信号触发后进程没有执行退出逻辑,就会变成后台驻留的不可见进程。 - 根本问题出在你自定义实现的Cocoa事件循环:你没有使用
[NSApp run]的标准运行逻辑,也没有处理应用终止相关的系统回调。你当前的事件循环仅处理普通UI事件,没有响应系统发出的退出请求,系统触发退出后,你的app.Run()始终返回true,循环不会终止,最终触发系统的SIGTERM兜底。 flushBuffer调用处触发概率高和OpenGL无关,仅因为该调用会等待垂直同步,是你的游戏逻辑中耗时最长的阻塞调用,进程大部分时间停留在该位置,信号触发时自然会在此处中断。
修复方案
1. 处理Cocoa原生退出请求
在你的MacAppDelegate中实现applicationShouldTerminate:回调,通知C++层的Application类触发退出:
// MacAppDelegate.h @class Application; @interface MacAppDelegate : NSObject <NSApplicationDelegate> @property (nonatomic, assign) Application* appInstance; @end // MacAppDelegate.mm @implementation MacAppDelegate - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { // 通知C++层的Application实例需要退出,你需要给Application类新增此方法,内部设置退出标记让Run()返回false if (_appInstance) { _appInstance->RequestQuit(); } // 告诉NSApp稍后再完成终止流程,等游戏循环跑完当前帧 return NSTerminateLater; } @end
在main函数的循环中调整事件超时配置,循环退出后调用NSApp的终止应答方法:
do { // 把untilDate从nil改为固定16ms超时,避免永久阻塞在事件等待逻辑,保证退出请求能及时响应 while((e = [NSApp nextEventMatchingMask: NSEventMaskAny untilDate: [NSDate dateWithTimeIntervalSinceNow:1/60.0] inMode: NSDefaultRunLoopMode dequeue: YES])) { [NSApp sendEvent: e]; [NSApp updateWindows]; } } while (app.Run()); // 循环退出后通知NSApp可以完成终止流程 [NSApp replyToApplicationShouldTerminate:YES];
2. 可选:添加SIGTERM信号处理器
为了避免极端情况下的进程驻留,可以添加SIGTERM信号捕获逻辑,收到信号时同样设置C++层的退出标记:
#include <signal.h> static Application* gAppInstance = nullptr; void sigtermHandler(int signal) { if (gAppInstance) { gAppInstance->RequestQuit(); } } // 在main函数初始化Application后注册信号处理器 gAppInstance = &app; signal(SIGTERM, sigtermHandler);
疑问解答
NSSupportsSuddenTermination关闭是正确的配置,该配置仅控制系统是否可以不通知应用直接杀掉进程,和当前问题无关。你不需要为正常退出流程单独配置信号处理器,只要正确处理Cocoa的终止回调即可解决绝大多数退出异常问题。
内容的提问来源于stack exchange,提问作者theOtherMichael
相关产品推荐
相关产品推荐

