从倒计时到多线程(2)
如果你发现自己在坑里,就不要再挖了。
------美国谚语
上一篇我们说到了用nstimer做倒计时遇到的一些问题,要想了解这些问题背后的原因,我们首先要了解一下nstimer。
我们看一下苹果的文档,第一句话就是:
Timers work in conjunction with run loops. To use a timer effectively, you should be aware of how run loops operate—see NSRunLoop and Threading Programming Guide.
timer是和run loop一起使用的,那么这个run loop又是什么东西呢?
跳到Threading Programming Guide 的runloops部分,
如果你英文不好,可以看些国人写的文章:
http://blog.ibireme.com/2015/05/18/runloop/
http://www.cnblogs.com/tangbinblog/archive/2012/12/07/2807290.html
我简单说一下。runloop首先不是“跑着撸”, 是一个事件处理的循环,用来处理输入事件,调度工作。其目的就是为了有事要处理的时候就让你的线程去干,没事处理的时候就让它休息。(其实就是文档的第一句话)简单举个例子吧,为什么你滚动scrollview的时候,你的app能够响应?就是因为你的app的runloop一直在等待,你一滚动视图,runloop就会去处理相应的事情,比如刷新ui,这样,你的app才会真正的“滚动”起来。
runloop还跟线程有关系,一个runloop对应着一个线程。说到这,就知道我们的题目是什么意思了。
所以要了解runloop就又要先了解线程。(好大一个圈)
再看看线程的文档
又是一堆英文,一样的,贴几篇中文文章:
http://blog.csdn.net/lengshengren/article/details/17267555
http://www.cnblogs.com/sunfrog/p/3243230.html
我简单说一下线程的用法。
创建线程的两种方法:(hmmm,茴字有几种写法?)
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument;//这个方法只创建线程,如果要启动该线程还要显示调用- (void)start方法。
- (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument; //这方法是不是很眼熟?
上一篇文章我们就用到了这个方法。这个方法会创建一个线程并启动。
好了,想想上一篇文章,
我们开启了一个新的线程,并在该线程下初始化定时器,也就是把定时器加入该线程。为什么要这么做呢?
好问题。如果我们不这么做,那么我们创建的定时器就会加入当前线程,也就是主线程,当我们主线程有事要忙的时候
定时器事件就会被阻塞,比如上篇文章后面提到的,视图控制器调用了busy方法,于是定时器的开启就被延后了。
明白了吧?而如果我们在一个新的线程开启定时器,就没有这个问题了。
是时候回过头来看runloop了。
上文我们提到runloop对应一个线程。也就是每个线程都有一个自己的runloop。而且我们不能直接创建runloop,只能从当前线程获取。
使用[NSRunLoop currentRunLoop]获取。第一次获取的时候,该线程就帮我们生成runloop。主线程除外,主线程的runloop系统已经生成好了。不管怎么样,我们都只能获取runloop。如果你想获取主线程的runloop,可以用[NSRunLoop mainRunLoop]获取。
runloop需运行在一个mode(模式)下面,一个runloop可以有多个mode,如果你不指定mode,默认就是defaultMode.
子线程的runloop需要手动运行,即调用- (void)run方法。(主线程runloop不需要)。
现在可以来说说上篇文章中说的为什么滚动scrollview的时候,定时器就不动了。一开始,我们的timer是加到了主线程里,并且是放到了runloop的defaultmode下,当你滚动scrollview的时候,runloop进入了另一个mode:TrackingRunLoopMode。于是定时器就停止工作了。
要解决这个问题,只要把timer加入两个mode:defaultmode 和TrackingRunLoopMode就可以,还有一个更好的方法就是加入NSRunLoopCommonModes。commonmode是一个集合,包含了前面的两个mode。
ok,我们再来捋一捋思路。
由于我们没有显示地指定改runloop运行于哪个mode,默认地它运行于defaultmode,你也可以显示地指定,但是由于是在一个新的线程,
这个无所谓了。然后调用run,运行runloop。
事情就这么成了!!
这篇文章说了runloop(跑着撸),下篇我们说说runfuck。敬请期待!
如果你觉得文章不错,可以给我打赏点比特股(bts),以示支持。^_^
BTS6jUaVVkz9gN8t9sWY9NR5UbiubSz7QtVDnEtFGpujYeqQSfQ5E