什么是 RunLoop?
RunLoop
实际上是一个对象,这个对象在循环中用来处理程序运行过程中出现的各种事件(比如说触摸
事件、UI刷新
事件、定时器
事件、Selector
事件),从而保持程序的持续运行。
RunLoop
在没有事件处理的时候,会使线程进入睡眠模式,从而节省 CPU
资源,提高程序性能。
RunLoop作用是什么?
1、保持程序持续运行
程序一启动就会开一个主线程,主线程一开起来就会跑一个主线程对应的RunLoop,RunLoop保证主线程不会被销毁,也就保证了程序的持续运行。
2、处理App中的各种事件
比如:触摸事件,定时器事件,Selector事件等。
3、节省CPU资源,提高程序性能
程序运行起来时,当什么操作都没有做的时候,RunLoop就告诉CUP,现在没有事情做,我要去休息,这时CUP就会将其资源释放出来去做其他的事情,当有事情做的时候RunLoop就会立马起来去做事情。
RunLoop 和线程
RunLoop
和线程是息息相关的,我们知道线程的作用是用来执行特定的一个或多个任务,在默认情况下,线程执行完之后就会退出,就不能再执行任务了。这时我们就需要采用一种方式来让线程能够不断地处理任务,并不退出。所以,我们就有了 RunLoop。
一条线程
对应一个RunLoop对象
,每条线程都有唯一
一个与之对应的 RunLoop 对象
。
RunLoop
并不保证线程安全。我们只能在当前线程内部操作当前线程的 RunLoop
对象,而不能在当前线程内部去操作其他线程的 RunLoop
对象方法。
RunLoop
对象在第一次获取 RunLoop
时创建
,销毁
则是在线程结束
的时候。
主线程的 RunLoop 对象系统自动帮助我们创建好了,而子线程
的 RunLoop对象
需要我们主动
创建和维护。
RunLoop 相关类
下面我们来了解一下Core Foundation框架下关于 RunLoop 的 5 个类,只有弄懂这几个类的含义,我们才能深入了解 RunLoop 的运行机制。
CFRunLoopRef:代表 RunLoop 的对象
CFRunLoopModeRef:代表 RunLoop 的运行模式
CFRunLoopSourceRef:就是 RunLoop 模型图中提到的输入源 / 事件源
CFRunLoopTimerRef:就是 RunLoop 模型图中提到的定时源
CFRunLoopObserverRef:观察者,能够监听 RunLoop 的状态改变
一个RunLoop对象(CFRunLoopRef)中包含若干个运行模式
(CFRunLoopModeRef)。而每一个运行模式下又包含若干个输入源
(CFRunLoopSourceRef)、定时源
(CFRunLoopTimerRef)、观察者
(CFRunLoopObserverRef)。
每次 RunLoop 启动时,只能指定其中一个运行模式(CFRunLoopModeRef
),这个运行模式(CFRunLoopModeRef
)被称作当前运行模式
(CurrentMode
)。
如果需要切换运行模式(CFRunLoopModeRef),只能退出
当前 Loop,再重新
指定一个运行模式(CFRunLoopModeRef)进入
。
这样做主要是为了分隔开
不同组的
输入源
(CFRunLoopSourceRef)
定时源
(CFRunLoopTimerRef)
观察者
(CFRunLoopObserverRef),让其互不影响 。
RunLoop原理
在每次运行开启RunLoop的时候,所在线程的RunLoop会自动处理之前未处理的事件,并且通知相关的观察者。
顺序如下:
1.通知观察者RunLoop已经启动
2.通知观察者即将要开始的定时器
3.通知观察者任何即将启动的非基于端口的源
4.启动任何准备好的非基于端口的源
5.如果基于端口的源准备好并处于等待状态,立即启动;并进入步骤9
6.通知观察者线程进入休眠状态
7.将线程置于休眠知道任一下面的事件发生:
·某一事件到达基于端口的源
·定时器启动
·RunLoop设置的时间已经超时
·RunLoop被显示唤醒
8.通知观察者线程将被唤醒
9.处理未处理的事件
·如果用户定义的定时器启动,处理定时器事件并重启RunLoop。进入步骤2
· 如果输入源启动,传递相应的消息
·如果RunLoop被显示唤醒而且时间还没超时,重启RunLoop。进入步骤2
10.通知观察者RunLoop结束
RunLoop应用
RunLoop常见的几种模式?模式有啥作用?
- kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
- UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
- UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
- GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
- kCFRunLoopCommonModes: 这是一个占位用的Mode,作为标记kCFRunLoopDefaultMode和UITrackingRunLoo
RunLoop中的source0、source1
Source有两个种:Source0
和 Source1
。
Source1
:基于mach_Port
的,来自系统内核
或者其他进程或线程
的事件,可以主动唤醒
休眠中的RunLoop(iOS里进程间通信开发过程中我们一般不主动使用)。mach_port
大家就理解成进程间相互发送消息的一种机制就好。
Source0
:非基于Port的 处理事件,什么叫非基于Port的呢?就是说你这个消息不是其他进程或者内核直接发送给你的。
简单举个例子:一个APP在前台静止着,此时,用户用手指点击了一下APP界面,那么过程就是下面这样的:
我们触摸屏幕,先摸到硬件(屏幕),屏幕表面的事件会先包装成Event
, Event
先告诉source1
(mach_port),source1
唤醒RunLoop, 然后将事件Event分发给source0
,然后由source0
来处理。
如果没有事件,也没有timer,则runloop就会睡眠, 如果有,则runloop就会被唤醒,然后跑一圈。