macOS守护进程创建多个CFRunLoopTimer时callback2不触发问题
macOS守护进程多定时器失效问题排查
问题描述
我正尝试为macOS守护进程创建多个周期性定时器,编写了MyTimer类:
struct MyTimer { MyTimer() { } ~MyTimer() { stopTimer(); } bool setTimer(size_t nmsPeriod, //Period in milliseconds void (*pfn)(void) //Function to call when timer fires ) { stopTimer(); CFTimeInterval fInterval = ((CFTimeInterval)nmsPeriod) / 1000.0; CFRunLoopTimerContext ctx = {}; ctx.info = this; _pfnCallback = pfn; _refTmr = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + fInterval, fInterval, 0, 0, _callbackTmr, &ctx); if(!_refTmr) return false; CFRunLoopAddTimer(CFRunLoopGetCurrent(), _refTmr, kCFRunLoopCommonModes); return true; } void stopTimer() { if(_refTmr) { CFRunLoopTimerInvalidate(_refTmr); CFRelease(_refTmr); _refTmr = NULL; } } protected: static void _callbackTmr(CFRunLoopTimerRef timer, void *info) { MyTimer* pThis = (MyTimer*)info; pThis->_pfnCallback(); } private: CFRunLoopTimerRef _refTmr = NULL; void (*_pfnCallback)(void) = NULL; };
在守护进程中定义两个全局实例:
MyTimer gTmr1; MyTimer gTmr2;
启动时初始化定时器:
//Set 1st timer 2 minutes from now gTmr1.setTimer(2 * 60 * 1000, callback1); //Set 2nd timer 300 seconds from now gTmr2.setTimer(300 * 1000, callback2); //And then reset the 2nd timer 2 seconds from now gTmr2.setTimer(2 * 1000, callback2); // ... //Enter the run-loop... CFRunLoopRun();
回调函数:
static void callback1(void) { log("Callback 1 fired"); } static void callback2(void) { log("Callback 2 fired"); }
实际运行时,只有callback1触发,callback2始终不执行。
问题分析与修复
核心问题:栈上CFRunLoopTimerContext的生命周期失效
在setTimer方法中,你创建了栈局部变量CFRunLoopTimerContext ctx,当setTimer执行完毕返回后,这个栈对象会被系统销毁并释放内存。但CFRunLoopTimer会持有该context的指针,后续定时器触发时访问已失效的内存,引发未定义行为——具体表现为callback2无法正常执行(第一次设置gTmr2的timer时context已失效,重置时又创建了新的栈context,同样会在方法结束后失效)。
修复方案
将CFRunLoopTimerContext改为MyTimer的成员变量,确保其生命周期与定时器实例一致:
- 在
MyTimer的private成员中添加context变量:
private: CFRunLoopTimerRef _refTmr = NULL; void (*_pfnCallback)(void) = NULL; CFRunLoopTimerContext _ctx = {}; // 添加这一行
- 修改
setTimer方法中的context初始化逻辑:
bool setTimer(size_t nmsPeriod, void (*pfn)(void)) { stopTimer(); CFTimeInterval fInterval = ((CFTimeInterval)nmsPeriod) / 1000.0; // 重置context,避免旧数据干扰 memset(&_ctx, 0, sizeof(_ctx)); _ctx.info = this; _pfnCallback = pfn; _refTmr = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + fInterval, fInterval, 0, 0, _callbackTmr, &_ctx); if(!_refTmr) return false; CFRunLoopAddTimer(CFRunLoopGetCurrent(), _refTmr, kCFRunLoopCommonModes); return true; }
额外说明
- 全局
MyTimer实例的生命周期覆盖整个守护进程运行期,因此成员变量_ctx不会提前失效,定时器触发时能正确访问到this指针和回调函数。 - 当前的
stopTimer方法逻辑正确,能在重置定时器时销毁旧定时器并释放资源,避免内存泄漏。
内容的提问来源于stack exchange,提问作者c00000fd
相关产品推荐
相关产品推荐

