1、什么是Runloop
Runloop是通过内部维护的事件循环来对事件/消息进行管理的一个对象。事件循环不是while死循环,而是状态转换,即用户态-内核态的转换。
image.png2、RunLoop事件循环
下图是仿照Runloop内部逻辑实现的伪代码:
image.png根据以上代码我们可以看出,RunLoop主要处理以下6类事件:
1.Observer事件:runloop中状态变化时进行通知。(微信卡顿监控就是利用这个事件通知来记录下最近一次main runloop活动时间,在另一个check线程中用定时器检测当前时间距离最后一次活动时间过久来判断在主线程中的处理逻辑耗时和卡主线程)。这里还需要特别注意,CAAnimation是由RunloopObserver触发回调来重绘。
2.Block事件:非延迟的performSelector,非延迟的dispatch_after,block回调。
3.Main_Dispatch_Queue事件:GCD中dispatch到main queue的block会被dispatch到main loop执行。
4.Timer事件:延迟的performSelector,延迟的dispatch_after,timer事件。
5.Source0事件:非基于Port的处理事件,不能主动唤醒休眠中的RunLoop,需要手动触发。我们触摸屏幕时,先摸到硬件(屏幕),屏幕表面的事件会先包装成Event,Event先告诉source1(mach_port),source1唤醒RunLoop, 然后将事件Event分发给source0,然后由source0来处理。
6.Source1事件:基于mach_Port的,来自系统内核或者其他进程或线程的事件,可以主动唤醒休眠中的RunLoop(推测CADisplayLink也是这里触发)。
简化的流程图如下:
image.png在每次运行开启RunLoop的时候,所在线程的RunLoop会自动处理之前未处理的事件,并且通知相关的观察者。具体的顺序如下:
-
通知观察者RunLoop已经启动
-
通知观察者即将要开始的定时器
-
通知观察者任何即将启动的非基于端口的源
-
启动任何准备好的非基于端口的源
-
如果基于端口的源准备好并处于等待状态,立即启动,并进入步骤9
-
通知观察者线程进入休眠状态
-
将线程置于休眠知道任一下面的事件发生:
-
某一事件到达基于端口的源
-
定时器启动
-
RunLoop设置的时间已经超时
-
RunLoop被显示唤醒
-
-
通知观察者线程将被唤醒
-
处理未处理的事件
-
如果用户定义的定时器启动,处理定时器事件并重启RunLoop,进入步骤2
-
如果输入源启动,传递相应的消息
-
如果RunLoop被显示唤醒而且时间还没超时,重启RunLoop。进入步骤2
-
-
通知观察者RunLoop结束。
3、RunLoop和线程的关系
RunLoop 和线程是息息相关的,我们知道线程的作用是用来执行特定的一个或多个任务,在默认情况下,线程执行完之后就会退出,就不能再执行任务了。这时我们就需要采用一种方式来让线程能够不断地处理任务,并不退出。所以,我们就有了 RunLoop。
1、一条线程对应一个RunLoop对象,每条线程都有唯一一个与之对应的 RunLoop 对象。
2、RunLoop 并不保证线程安全。我们只能在当前线程内部操作当前线程的 RunLoop 对象,而不能在当前线程内部去操作其他线程的 RunLoop 对象方法。
3、RunLoop 对象在第一次获取 RunLoop 时创建,销毁则是在线程结束的时候。
4、主线程的 RunLoop 对象系统自动帮助我们创建好了,而子线程的 RunLoop对象需要我们主动创建和维护。
4、RunLoop与Core Animation渲染的关系
iOS 图形服务接收到 VSync 信号后,会通过 IPC 通知到 App 内。App 的 Runloop 在启动后会注册对应的 CFRunLoopSource 通过 mach_port 接收传过来的时钟信号通知,随后 Source 的回调会驱动整个 App 的动画与显示。Core Animation 在 RunLoop 中注册了一个 Observer,监听了 BeforeWaiting 和 Exit 事件。这个 Observer 的优先级是 2000000,低于常见的其他 Observer。
当一个触摸事件到来时,RunLoop 被唤醒,App 中的代码会执行一些操作,比如创建和调整视图层级、设置 UIView 的 frame、修改 CALayer 的透明度、为视图添加一个动画;这些操作最终都会被 CALayer 捕获,并通过 CATransaction 提交到一个中间状态去(CATransaction 的文档略有提到这些内容,但并不完整)。
当上面所有操作结束后,RunLoop 即将进入休眠(或者退出)时,关注该事件的 Observer 都会得到通知。这时 CA 注册的那个 Observer 就会在回调中,把所有的中间状态合并提交到 GPU 去显示;如果此处有动画,CA 会通过 DisplayLink 等机制多次触发相关流程。
网友评论