今是昨非

今是昨非

日出江花红胜火,春来江水绿如蓝

iOS NSTimer不走的问题

iOS NSTimer 不走的问题#

背景#

这个版本上线后,突然发现埋点数据直线下降,调试后发现是定时器上传的方法没有走,但是定时器的方法本期并没有修改过。代码如下


- (BOOL)initTimer() {
        self.uploadTimer = [NSTimer scheduledTimerWithTimeInterval:timerInterval target:self selector:@selector(handleUpload) userInfo:nil repeats:YES];
		[[NSRunLoop currentRunLoop] addTimer:self.uploadTimer forMode:NSRunLoopCommonModes];
}

排查#

这个 handleUpload 方法,怎么都不会走,但是在之前的版本中就是好的,排查了之后发现,是外层调用的地方加了一层异步。即调用的地方变成了


dispatch_async(dispatch_get_global_queue(0, 0), ^{
    initTimer()
});

然后就导致了定时器没有启动。

原因#

iOS 是通过 runloop 作为消息循环机制,主线程默认启动了 runloop,可是自线程没有默认的 runloop,因此,我们在子线程启动定时器是不生效的。

解决方法:在子线程启动一下 runloop 即可


- (BOOL)initTimer() {
        self.uploadTimer = [NSTimer scheduledTimerWithTimeInterval:timerInterval target:self selector:@selector(handleUpload) userInfo:nil repeats:YES];
		[[NSRunLoop currentRunLoop] addTimer:self.uploadTimer forMode:NSRunLoopCommonModes];
        [[NSRunLoop currentRunLoop] run];
}

思考#

通过这个问题,有两点收获,

  1. timer 在 iOS 开发中经常使用,在很多博客中都看到关于 timer 要注意的地方也很多,通常是内存管理,timer 启动相关,但是在开发中,如果没有真正遇到问题,没有 “疼” 在自己身上,就自己注意的就不够,经过这次之后,相信以后再要在异步使用 timer,肯定会留心很多。
  2. 这个地方 initTimer 其实是一个 SDK 初始化的类,其实 SDK 内部没有修改,但是外层使用 SDK 的 App 调用修改了,就导致了 SDK 不能正常工作。所以封装 SDK 时要注意,如果使用 timer,要么做线程检查,要么就直接把代码安全保证好;因为不能保证第三方调用者使用时的情况,所以要保证自己代码的正确性。

参考#

IOS 定时器操作和 NSTimer 的各种坑

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。