Runloop
Runloop
是事件接收和分发机制的一个实现。是线程相关的基础框架的一部分。一个Runloop就是一个事件处理的循环,用来不停的调度工作及处理输入事件。使用runloop的目的就是让你的线程
RunLoop
的主要目的
保证程序执行的线程不会被系统终止,如果没有RunLoop,UIApplicationMain函数执行完毕之后将直接返回,就是说程序一启动然后就结束,在有工作的时候忙于工作,而没有工作的时候处于休眠状态
什么时候使用Runloop
当需要和该线程进行交互的时候才会使用Runloop
Runloop Mode

一个Runloop
可能有几个mode
Runloop Mode
实际上是Source
,Timer
和 Observer
的集合,不同的 Mode 把不同组的 Source
,Timer
和Observer
隔绝开来。Runloop
在某个时刻只能跑在一个 Mode 下,处理这一个 Mode 当中的 Source,Timer 和 Observer。
苹果文档中提到的 Mode 有五个,分别是:
-
NSDefaultRunLoopMode
:默认的mode,正常情况下都是在这个mode -
NSConnectionReplyMode
-
NSModalPanelRunLoopMode
-
NSEventTrackingRunLoopMode
:使用这个Mode去跟踪来自用户交互的事件(比如UITableView上下滑动)
-NSRunLoopCommonModes
iOS 中公开暴露出来的只有NSDefaultRunLoopMode
和 NSRunLoopCommonModes
。 NSRunLoopCommonModes
实际上是一个 Mode 的集合,默认包括 NSDefaultRunLoopMode
和 NSEventTrackingRunLoopMode
。
Source
即可以唤醒Runloop
的一些事件。比如用户点击了屏幕,就会创建一个input source。
-
source0
: 非系统事件
只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。
-
source1
: 系统事件
包含了一个 mach_port和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程
Timer
我们经常用的NSTimer
就属于这一类。
Observer
某个observer可以监听runloop
的状态变化,并作出一定反应。
RunLoop运行流程
经典大图

没有事情的时候,Runloop
处于休眠状态。当外部source将其唤醒后,它会依次处理接收到的timer/source,然后再次进入休眠。
常见的面试题:
Runloop
和线程是什么关系?
每条线程都有唯一的一个与之对应的RunLoop对象,其关系是保存在一个全局的 Dictionary 里;主线程的RunLoop已经自动创建,子线程的RunLoop需要主动创建;RunLoop在第一次获取时创建,在线程结束时销毁
Runloop
的mode作用是什么?
指定事件在运行循环中的优先级的,
线程的运行需要不同的模式,去响应各种不同的事件,去处理不同情境模式。(比如可以优化tableview的时候可以设置UITrackingRunLoopMode下不进行一些操作,比如设置图片等。)
以+scheduledTimerWithTimeInterval:
的方式触发的timer
,在滑动页面上的列表时,timer
会暂停回调, 为什么?
滑动scrollView时,主线程的
RunLoop
会切换到UITrackingRunLoopMode
这个Mode,执行的也是UITrackingRunLoopMode
下的任务(Mode中的item),而timer是添加在NSDefaultRunLoopMode
下的,所以timer任务并不会执行,只有当UITrackingRunLoopMode
的任务执行完毕,runloop切换到NSDefaultRunLoopMode
后,才会继续执行timer。
如何解决在滑动页面上的列表时,timer会暂停回调?
将
Timer
放到NSRunLoopCommonModes
中执行即可
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop] run];
NSTImer
使用时需要注意什么?
-
注意timer添加到runloop时应该设置为什么mode
-
注意timer在不需要时,一定要调用invalidate方法使定时器失效,否则得不到释放
RunLoop
有哪些应用?
常驻内存、AutoreleasePool 自动释放池
AutoreleasePool
和 RunLoop
有什么联系?
iOS应用启动后会注册两个 Observer 管理和维护 AutoreleasePool。应用程序刚刚启动时默认注册了很多个Observer,其中有两个Observer的 callout 都是 _ wrapRunLoopWithAutoreleasePoolHandler,这两个是和自动释放池相关的两个监听。
- 第一个 Observer 会监听 RunLoop 的进入,它会回调objc_autoreleasePoolPush() 向当前的 AutoreleasePoolPage 增加一个哨兵对象标志创建自动释放池。这个 Observer 的 order 是 -2147483647 优先级最高,确保发生在所有回调操作之前。
- 第二个 Observer 会监听 RunLoop 的进入休眠和即将退出 RunLoop 两种状态,在即将进入休眠时会调用 objc_autoreleasePoolPop() 和 objc_autoreleasePoolPush() 根据情况从最新加入的对象一直往前清理直到遇到哨兵对象。而在即将退出 RunLoop 时会调用objc_autoreleasePoolPop() 释放自动自动释放池内对象。这个Observer 的 order 是 2147483647 ,优先级最低,确保发生在所有回调操作之后。
NSRunLoop 和 CFRunLoopRef 区别
CFRunLoopRef 基于C 线程安全,NSRunLoop 基于 CFRunLoopRef 面向对象的API 是不安全的
网友评论