iOS 中的 RunLoop 详解

1. 什么是 RunLoop?

RunLoop(运行循环) 是 iOS/macOS 开发中用于管理线程事件和消息的核心机制。它通过一个循环不断监听和处理输入事件(如触摸、定时器、网络数据),并在没有任务时让线程休眠以节省资源。

  • 本质 :基于 mach port 的事件循环机制。
  • 核心作用:让线程在有任务时执行任务,无任务时休眠,避免资源浪费。

2. RunLoop 与线程的关系

  • 主线程 :默认自动创建并启动 RunLoop(UIApplicationMain 函数中启动)。
  • 子线程:默认不开启 RunLoop,需手动创建和启动。
  • 生命周期:线程与 RunLoop 一一对应,存储在全局字典中(线程销毁时 RunLoop 也释放)。

3. RunLoop 的核心组成

组件 作用
Modes(模式) 定义 RunLoop 在不同场景下监听的事件源(Source)和观察者(Observer)。
Sources(事件源) - Source0 :处理 App 内部事件(如触摸、PerformSelector)。 - Source1:基于 mach port 的系统事件(如硬件事件)。
Timers(定时器) 基于 RunLoop 的定时器(如 NSTimer),受 RunLoop Mode 影响。
Observers(观察者) 监听 RunLoop 的状态变化(如即将处理事件、即将休眠等),用于性能监控。

4. RunLoop 的 Mode(模式)

模式 描述
Default(NSDefaultRunLoopMode 默认模式,处理大多数事件(如普通触摸、网络请求)。
Tracking(UITrackingRunLoopMode 界面滚动时的模式(如 UIScrollView 滑动),优先保证滑动流畅。
Common(NSRunLoopCommonModes 一个虚拟模式,包含多个模式的集合(Default + Tracking),用于通用事件监听。

5. RunLoop 的典型应用场景

(1) 保持线程存活

通过 RunLoop 让子线程持续处理任务而不退出:

swift 复制代码
class BackgroundThread {
    private var thread: Thread?
    private var shouldKeepRunning = true
    
    func start() {
        thread = Thread { [weak self] in
            // 创建并启动 RunLoop
            let runLoop = RunLoop.current
            runLoop.add(NSMachPort(), forMode: .default)
            while self?.shouldKeepRunning == true {
                runLoop.run(mode: .default, before: .distantFuture)
            }
        }
        thread?.start()
    }
    
    func stop() {
        shouldKeepRunning = false
        thread?.cancel()
    }
}

(2) 优化滚动性能

将耗时任务放在 Default 模式,避免滚动时执行(如 UIScrollView 滑动):

swift 复制代码
DispatchQueue.main.async {
    // 在 Default 模式下执行任务
    RunLoop.current.perform(inMode: .default) {
        // 处理耗时任务(如数据解析)
    }
}

(3) 监听 RunLoop 状态

通过 CFRunLoopObserver 监控主线程卡顿:

swift 复制代码
let observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, CFRunLoopActivity.allActivities.rawValue, true, 0) { observer, activity in
    switch activity {
    case .beforeWaiting:
        print("RunLoop 即将休眠")
    case .afterWaiting:
        print("RunLoop 被唤醒")
    default: break
    }
}
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, .commonModes)

(4) 解决定时器失效问题

在滚动模式下(如 UIScrollView 滑动时),将 NSTimer 添加到 CommonModes

swift 复制代码
let timer = Timer(timeInterval: 1.0, repeats: true) { _ in
    print("Timer fired")
}
RunLoop.current.add(timer, forMode: .common)

6. RunLoop 的工作流程

  1. 接收事件:通过 mach port 接收系统事件(Source1)或 App 内部事件(Source0)。
  2. 通知 Observers :即将处理事件(kCFRunLoopBeforeTimerskCFRunLoopBeforeSources)。
  3. 处理事件:执行触发的 Source0、Source1 和 Timer 事件。
  4. 进入休眠 :若没有事件,调用 mach_msg 让线程休眠,等待新事件唤醒。
  5. 唤醒处理:被唤醒后处理新到达的事件,循环往复。

7. 注意事项

  • 避免阻塞主线程:主线程 RunLoop 负责 UI 更新,长时间阻塞会导致卡顿。
  • 子线程 RunLoop 管理:手动创建的 RunLoop 需自行控制生命周期(如添加保活 Source)。
  • 性能优化:合理使用 Mode 隔离不同任务(如滑动时暂停后台任务)。

总结

场景 RunLoop 的作用 关键技术点
线程保活 让子线程持续处理任务 添加保活 Source(如 NSMachPort
滑动性能优化 隔离耗时任务与滚动事件 使用 CommonModes 或模式切换
定时器管理 确保定时器在滚动时仍有效 注册到 CommonModes
卡顿监控 监听主线程 RunLoop 状态变化 CFRunLoopObserver

RunLoop 是 iOS 系统高效管理线程和事件的核心机制,合理利用可优化性能、实现复杂任务调度。在大多数情况下,开发者无需直接操作 RunLoop,但理解其原理对解决卡顿、线程管理等问题至关重要。

相关推荐
未来之窗软件服务3 小时前
一体化系统(九)智慧社区综合报表——东方仙盟练气期
大数据·前端·仙盟创梦ide·东方仙盟·东方仙盟一体化
陈天伟教授6 小时前
人工智能训练师认证教程(2)Python os入门教程
前端·数据库·python
信看7 小时前
NMEA-GNSS-RTK 定位html小工具
前端·javascript·html
Tony Bai7 小时前
【API 设计之道】04 字段掩码模式:让前端决定后端返回什么
前端
苏打水com7 小时前
第十四篇:Day40-42 前端架构设计入门——从“功能实现”到“架构思维”(对标职场“大型项目架构”需求)
前端·架构
king王一帅7 小时前
流式渲染 Incremark、ant-design-x markdown、streammarkdown-vue 全流程方案对比
前端·javascript·人工智能
苏打水com8 小时前
第十八篇:Day52-54 前端跨端开发进阶——从“多端适配”到“跨端统一”(对标职场“全栈化”需求)
前端
Bigger8 小时前
后端拒写接口?前端硬核自救:纯前端实现静态资源下载全链路解析
前端·浏览器·vite
BD_Marathon8 小时前
【JavaWeb】路径问题_前端绝对路径问题
前端