RunLoop(三)运行
2020-8-8
| 2024-5-17
0  |  阅读时长 0 分钟
type
status
date
slug
summary
tags
category
icon
password
在之前的篇章中,对RunLoop有了基本认识,以及对其底层对象也有了一定了解。这篇描述的是"RunLoop是如何运行的?''
为了解决这个问题,我们还是直接上源码,并给出导读的路径,如下:
在 CFRounLoop.c 类中:
__CFRunLoopRun >>> CFRunLoopRunSpecific >>> CFRunLoopRun
 
另,Core Foundation 下载地址,源码注读 下载地址,以上对应的链接,见文末。

一、运行逻辑

notion image
上图左侧运行逻辑图出自于官方文档。在RunLoop中,接收输入事件来自两种不同的来源:输入源(input source)和定时源(timer source)。

1. 来源按同步异步分类

上图描述了按两种来源:异步方式接收的输入源,以及同步接收的定时源

1.1 Input sources

输入源传递异步事件,通常消息来自于其他线程或程序,按照是否来源于内核也分为下面几种:
  • Port-Based Sources,基于 Port的 事件,系统底层的,一般由内核自动发出信号。例如 CFSocketRef ,在应用层基本用不到。
  • Custom Input Sources,非基于Port事件,用户手动创建的 Source,则必须从其他线程手动发送信号
  • Cocoa Perform Selector Sources, Cocoa 提供的 performSelector 系列方法,也是一种事件源。和基于端口的源一样,执行selector请求会在目标线程上序列化,减缓许多在线程上允许多个方法容易引起的同步问题。不像基于端口的源,一个selector执行完后会自动从RunLoop里面移除。

1.2 Timer sources

定时源则传递同步事件,发生在特定时间或者重复的时间间隔。
定时器可以产生基于时间的通知,但它并不是实时机制。和输入源一样,定时器也和你的RunLoop的特定模式相关。如果定时器所在的模式当前未被RunLoop监视,那么定时器将不会开始直到RunLoop运行在相应的模式下。
其主要包含了两部分:
  • NSTimer
  • performSelector:withObject:afterDelay:
 

2. 来源按对象分类

在上一篇中,我们就是按对象将RunLoop接收事件按对象分类的。

2.1 Source1

对应于Port-Based Sources,即基于Port的,通过内核和其他线程通信。
常用于接收、分发系统事件,大部分屏幕交互事件都是由Source1接收,包装成Event,然后分发下去,最后由Source0去处理。
所以,其包括:
  • 基于Port的线程间通信;
  • 系统事件捕捉;

2.2 Source0

是非Port事件。在应用中,触摸事件的最终处理,以及perforSelector:onThread都是包装成该类型对象,最后由开发者指定回调函数,手动处理该事件。
需要注意的是perforSelector:onThread是否有delay,即是否延迟函数或者定时函数等类型。
  • perforSelector:onThread 不是delay函数时, 是Source0事件。
  • performSelector:withObject:afterDelaydelay时,则属于Timers事件。
所以,其包括:
  • 触摸事件处理
  • performSelector:onThread
 

2.3 Timers

同上 Timer sources 说明。

二、源码详解

RunLoop主要运行逻辑源码都集中在CFRunLoop.c,笔者进行了注读,而且抽取出RunLoopCycle.c,方便查看。代码见文末参考-示例源码。
也可以跳过该节,直接查看【三、流程图】,直观清晰的分析RunLoop的运行逻辑及详细流程。

1. 入口函数

2. RunLoop执行函数

3. RunLoop消息处理函数

4. 消息处理底层函数

RunLoop 进行回调时,一般都是通过一个很长的函数调用出去 (call out),当你在你的代码中下断点调试时,打印堆栈(bt),就能在调用栈上看到这些函数。
下面是这几个函数的整理版本,如果你在调用栈中看到这些长函数名,在这里查找一下就能定位到具体的调用地点了:

5. 休眠的实现

RunLoop如何实现真正意义上的休眠,而不是像下面这种方式:
其内部是通过内核态的调用。
notion image

三、流程图

将上面的代码逻辑抽取下面得到如下:
  • 黄色:表示通知Observer各个阶段;
  • 蓝色:处理消息的逻辑;
  • 绿色:分支判断逻辑;

1. 执行逻辑

notion image

2. 流程图

notion image

参考

链接
  1. objc源码
  1. CF源码
  1. 源码注读
  1. Threading Programming Guide--RunLoop 中文翻译
  1. 深入理解RunLoop
示例源码
  1. 源码注读
  • Objective-C
  • runloop
  • RunLoop(四)应用runtime(三)方法缓存
    Loading...
    目录