前言
对iOS开发者而言,runloop是一个老生常谈的话题,但凡是iOS开发者,在工作中必然直接或间接的接触过runloop。而对于面试者而言,runloop又几乎是必考点。在几年前,笔者写过一篇文章NSRunLoop,对runloop原理以及应用场景做了基本介绍。但是当时也是道听途说,简单的翻看了源码的do…while循环,并没有深入源码。所以,本文将从源码的角度剖析runloop的组成,强化自己对runloop的认识,验证我们脑海中一直以来似懂非懂的原理,真心希望这篇文章能够帮助到大家。
注意:为了减少篇幅、避免困惑,本篇文章贴出的源码稍有精简,比如去除了lock和windows的代码。
为什么是runLoop
runloop顾名思义就是”跑圈“,所谓跑圈就给人一种循环的感觉。runloop运行的核心代码就是一个有状态的do…while循环。每循环一次就相当于跑了一圈,线程就会对当前这一圈里面产生的事件进行处理。那么为什么线程要有runloop呢?其实我们的APP可以理解为是靠event驱动的(包括iOS和Android应用)。我们触摸屏幕、网络回调等都是一个个的event,也就是事件。这些事件产生之后会分发给我们的APP,APP接收到事件之后分发给对应的线程。通常情况下,如果线程没有runloop,那么一个线程一次只能执行一个任务,执行完成后线程就会退出。要想APP的线程一直能够处理事件或者等待事件(比如异步事件),就要保活线程,也就是不能让线程早早的退出,此时runloop就派上用场了。我们已经说了,runloop本质上就是一个有状态的do…while循环,所以只要不是超时或者故意退出状态,那么runLoop就会一直执行do…while,所以可以保证线程不退出。其实也不是必须要给线程指定一个runloop,如果需要我们线程能够持续的处理事件,那么就需要给线程绑定一个runloop。也就是说,runloop能够保证线程一直可以一直处理事件。所以runloop的作用可以理解为:
- 使程序一直运行并处理各种事件。这些事件包括但不限于用户操作、定时器任务、内核消息
- 有顺序的处理各种Event。因为runLoop有状态,可以决定线程在什么时候处理什么事件
- 节省CPU资源。通常情况下,事件并不是永无休止的产生,所以也就没必要让线程永无休止的运行。runloop可以在无事件处理时进入休眠状态,避免无休止的do…while跑空圈。
不得不重复那句老生常谈的话:一个线程对应一个RunLoop,程序运行是主线程的RunLoop默认启动,子线程的RunLoop按需启动(调用run方法)。runloop是线程的事件管理者,或者说是线程的事件管家,他会按照顺序管理线程要处理的事件,决定哪些事件在什么时候提交给主线程处理。