我们时常遇到这样的情况,tableview上面有一个计时的NSTimer,当tableView(scrollView)滑动的时候,NSTimer就失效了。
NSTimer 为什么会失效,牵扯到了 runloop 这个概念,如果对 runloop 不了解的话,请先阅读ibireme大神的这篇博客 深入理解RunLoop
我们通常这样创建timer
// 这种创建方式默认将 timer 加入到了 NSDefaultRunLoopMode 模式下
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(startTimer) userInfo:nil repeats:YES];
或者这样
self.timer = [NSTimer timerWithTimeInterval:1.0f target:self selector:@selector(startTimer) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode: NSDefaultRunLoopMode];
以上面两种写法,都是将定时器Timer加入到当前的主线程,并且当前主线程runloop的mode为NSDefaultRunLoopMode
,这个模式也是runloop的默认模式。
我们不滑动 scrollview 的时候主线程的runloop 停留在NSDefaultRunLoopMode
模式,这个时候定时器Timer是有效的,但当我们滑动 scrollview 的时候, 主线程的runloop 会切换到 UITrackingRunLoopMode
模式,这个时候因为定时器Timer 是在NSDefaultRunLoopMode
模式下,所以主线程的 runloop 不能处理定时器,定时器也就理所当然失效,当停止滑动,runloop 的模式切换回NSDefaultRunLoopMode
,定时器才会重新生效。
明白了失效的原因,解决问题也就简单了,有两种办法。
第一种:更改线程 runloop 的mode
self.timer = [NSTimer timerWithTimeInterval:1.0f target:self selector:@selector(startTimer) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode: NSRunLoopCommonModes];
NSRunLoopCommonModes
是runloop中的另一种模式,其作用等价于NSDefaultRunLoopMode
与UITrackingRunLoopMode
的结合,滑动scrollview的时候等价于UITrackingRunLoopMode
,停止滑动的时候等价于UITrackingRunLoopMode
。
第二种:将定时器放到到子线程里面创建。
// 创建一个子线程
[NSThread detachNewThreadSelector:@selector(createTimer) toTarget:self withObject:nil];
// 在子线程里创建定时器
- (void)createTimer {
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(startTimer) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] run];
}