搜索
您的当前位置:首页正文

NSTimer 与 Runloop 关系分析

来源:二三娱乐
    [NSTimer scheduledTimerWithTimeInterval:4.0
                                     target:self
                                   selector:@selector(clickAction)
                                   userInfo:nil
                                    repeats:YES];

在项目里,这就是使用 NSTimer 最常用的方式 -- 在 vc 里定时或者延迟执行某个方法,可是又有多少个coder会想知道它和 Runloop 的关系?

当使用 NSTimer 的 scheduledTimerWithTimeInterval 方法时,事实上此时 NSTimer 会被加入到当前的 Runloop 中而且模式是默认的 NSDefaultRunLoopMode。而如果当前线程就是主线程(UI线程), 在某些UI事件中,比如 UIScrollView 的拖动,会将 Runloop 切换成 NSEventTrackingRunLoopMode 模式,在这个过程中,默认的NSDefaultRunLoopMode 模式中注册的事件是不会被执行的。也就是说,此时使用 scheduledTimerWithTimeInterval 添加到 Runloop 中的 NSTimer 就不会执行。

实际需求:如何设置一个不被UI干扰的NSTimer?

需要手动创建一个 Timer,然后使用 NSRunLoop 的 addTimer:forMode:方法来把 Timer 按照指定模式加入到 RunLoop 中。这里使用的模式是: NSRunLoopCommonModes,这个模式等效于 NSDefaultRunLoopModeNSEventTrackingRunLoopMode 的结合

示例 e.g

//创建Timer
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(timer_callback) userInfo:nil repeats:YES];
//使用NSRunLoopCommonModes模式,把timer加入到当前Run Loop中。
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

NSTimer添加到Runloop中,但是不运行?

在iOS多线程中,每一个线程都有一个 Runloop,但是只有主线程的 Runloop 默认是打开的,其他子线程也就是我们创建的线程的 Runloop默认是关闭的,需要我们手动运行。我们可以通过 [NSRunLoop currentRunLoop] 来获得当前线程的 Runloop,并且调用
[runloop addTimer:timer forMode:NSDefaultRunLoopMode] 方法将定时器添加到 Runloop 中,最后一定不要忘记调用 Runloop 的 run 方法将当前 Runloop 开启,否则 NSTimer 永远也不会运行。

附录:苹果官方定义的Runloop的类型


QQ20151213-1@2x.png

网上看到比较好的文章记录:

  1. 分析了平时使用NSTimer的注意点、实时性还有和 Runloop 的关系,特别是和 Runloop 的关系分析的挺细致的
Top