从倒计时到多线程(2)

in #cn9 years ago

如果你发现自己在坑里,就不要再挖了。

------美国谚语

上一篇我们说到了用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部分,

5056-a363201fb7f5d484.jpg

如果你英文不好,可以看些国人写的文章:

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就又要先了解线程。(好大一个圈)

再看看线程的文档

5056-3bd3fb62388bec6f.jpg

又是一堆英文,一样的,贴几篇中文文章:

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; //这方法是不是很眼熟?

上一篇文章我们就用到了这个方法。这个方法会创建一个线程并启动。

好了,想想上一篇文章,

5056-6c7b825fdf83b81f.jpg

我们开启了一个新的线程,并在该线程下初始化定时器,也就是把定时器加入该线程。为什么要这么做呢?

好问题。如果我们不这么做,那么我们创建的定时器就会加入当前线程,也就是主线程,当我们主线程有事要忙的时候

定时器事件就会被阻塞,比如上篇文章后面提到的,视图控制器调用了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,我们再来捋一捋思路。

5056-681c396f1299df2d.jpg

为了防止定时器被主线程阻塞,我们把定时器放到一个新的线程。
5056-3281ddbaf641a479.jpg

由于我们没有显示地指定改runloop运行于哪个mode,默认地它运行于defaultmode,你也可以显示地指定,但是由于是在一个新的线程,

这个无所谓了。然后调用run,运行runloop。

事情就这么成了!!

这篇文章说了runloop(跑着撸),下篇我们说说runfuck。敬请期待!

如果你觉得文章不错,可以给我打赏点比特股(bts),以示支持。^_^

BTS6jUaVVkz9gN8t9sWY9NR5UbiubSz7QtVDnEtFGpujYeqQSfQ5E

Coin Marketplace

STEEM 0.04
TRX 0.32
JST 0.083
BTC 60866.94
ETH 1568.75
USDT 1.00
SBD 0.50