ios中的RunLoop

  • 什么是RunLoop

RunLoop 是线程专属的事件循环机制

  • 为什么要有RunLoop

普通子线程执行完代码会直接销毁,频繁创建 / 销毁线程损耗大量 CPU;RunLoop 给线程赋予「循环等待」能力

RunLoop 最精妙的设计在于它的「休眠-唤醒」机制。线程在没有事件时通过 mach_msg()系统调用进入内核休眠状态,几乎不消耗 CPU 资源。当有事件到来时(如触摸屏幕、Timer 到期、网络数据到达),内核会唤醒线程,RunLoop 继续处理事件。

  • RunLoop和线程的关系

在 iOS 中,线程和 RunLoop 之间是严格的一一对应关系。这个映射关系被维护在一个全局的 Dictionary 中,key 是线程标识(pthread_t),value 是对应的 CFRunLoopRef。

关键特性:

  1. 主线程自动创建:主线程的 RunLoop 在 App 启动时就已经自动创建并启动,无需手动干预

  2. 子线程懒加载:子线程的 RunLoop 不会自动创建,只有当你第一次调用CFRunLoopGetCurrent()或 RunLoop.current 时才会创建

  3. 生命周期绑定:RunLoop 的生命周期与线程绑定。线程销毁时,其对应的 RunLoop 也会自动销毁

  4. 不能跨线程访问:你只能在当前线程内部获取其 RunLoop(主线程除外)

  • RunLoop内部结构

RunLoop 的内部结构可以概括为:一个 RunLoop 包含多个 Mode,每个 Mode 包含多个 Item(Source、Timer、Observer)

具体结构如下图所示

其中pthread代表当前线程,modes存储当前RunLoop注册过的所有运行模式,currentMode代表当前运行的mode,而commonModes不是真实 Mode,是虚拟别名集合:给 Timer/Source 绑定 CommonModes 时,底层会自动把任务同时添加到集合内所有真实 Mode 中。

source0存放source0类型事件源

source1存放source1类型事件源

timers存放 CFRunLoopTimer 定时器

observer存放 CFRunLoopObserver 生命周期观察者

  • 详细介绍RunLoop四大核心组件

1.mode

CFRunLoopMode

Mode 是 RunLoop 的事件隔离容器,所有 Source、Timer、Observer 都必须绑定指定 Mode 才能生效。

想象一下:你在滑动一个 TableView 时,希望滚动尽可能流畅。此时如果还有 Timer 不停地触发回调,或者网络请求的回调频繁更新 UI,就会导致滚动卡顿。Mode 的设计就是为了解决这个问题------滑动时 RunLoop 切换到「追踪模式」,暂停非必要的事件处理,将 CPU 资源优先用于渲染。

核心规则:同一时刻,RunLoop 只能运行在一种 Mode 下,切换 Mode 时,当前未处理的事件会暂时搁置

mode的运行模式

  1. NSDefaultRunLoopMode:App 常规运行状态,页面静止、普通定时器、常规 UI 事件默认运行在此模式

  2. UITrackingRunLoopMode:ScrollView、TableView 滑动时,主线程自动切换到此模式,DefaultMode 的所有任务会暂停

  3. NSRunLoopCommonModes:重点!不是真实运行模式,它只是一个标记字符串。当你将某个 Mode 标记为 Common,或者将某个 Item 添加到 CommonModes 时:

    • Item 会被自动同步到所有标记为 Common 的 Mode 中

    • 目前 DefaultMode 和 TrackingRunLoopMode 都已被标记为 Common

2.CFRunLoopSource 事件源

source 是 RunLoop 待处理的任务载体,分为两类,底层唤醒逻辑完全不同: Source0:App 自定义事件

  • 无内核 Mach 端口,无法唤醒休眠的 RunLoop

  • 需要手动标记待处理状态,仅 RunLoop 唤醒后才能执行

  • 场景:无延时 performSelector、自定义手势、UI 交互事件

Source1:内核驱动事件

  • 绑定系统 Mach 端口,内核可主动发信号,可直接唤醒休眠的 RunLoop

  • 场景:主线程 GCD 异步、网络回调、硬件触摸事件、延时 performSelector

3.CFRunLoopTimer 定时器

NSTimer 的底层本质就是 CFRunLoopTimer,完全依赖 RunLoop Mode 运行。

致命特性:定时器绑定指定 Mode,RunLoop 切换 Mode 时,定时器会暂停计时。这也是「列表滑动时 NSTimer 失效」的根本原因。

4.CFRunLoopObserver 生命周期观察者

用于监听 RunLoop 单次循环的 6 个关键节点,是线上卡顿监控、性能优化的核心工具:

  • kCFRunLoopEntry:进入循环

  • kCFRunLoopBeforeTimers:即将处理定时器

  • kCFRunLoopBeforeSources:即将处理 Source0 事件

  • kCFRunLoopBeforeWaiting:线程即将休眠(卡顿监控核心节点)

  • kCFRunLoopAfterWaiting:线程被唤醒,结束休眠

  • kCFRunLoopExit:单次循环结束

RunLoop 内部运行流程

runLoop和多线程