基于本文回答
0
评论

RunLoop 和线程 (Thread) 的关系?

知识点图片

RunLoop 和线程(Thread)是 iOS/macOS 开发中密不可分的两个概念。用一句话总结它们的关系:

RunLoop 是为了管理线程而存在的,它们之间是「一一对应」的关系。

以下是详细的深度解析:

1. 核心关系:一一对应

  • 映射关系: 每个线程(Thread)都有且仅有一个 RunLoop 对象与之对应。
  • 存储方式: 在底层(Core Foundation 源码中),RunLoop 是存储在一个全局的 CFDictionary 中的。
    • Key: 线程对象(pthread_t)。
    • Value: 对应的 RunLoop 对象(CFRunLoopRef)。

2. 创建与获取机制(Lazy Loading)

RunLoop 并不是线程创建时自动生成的(主线程除外),而是懒加载(Lazy Loading)的。

  • 主线程(Main Thread): 系统在 App 启动时,会自动为主线程创建并启动一个 RunLoop。这是因为主线程需要一直处理 UI 事件、手势、屏幕刷新等,不能退出。
  • 子线程(Background Thread): 默认情况下,子线程没有 RunLoop。
    • 如果你不主动获取,它就没有。
    • 当你第一次调用 [NSRunLoop currentRunLoop]CFRunLoopGetCurrent() 时,系统会检测该线程是否有 RunLoop,如果没有,就会创建一个并保存到全局字典中。

3. 生命周期绑定

RunLoop 的生命周期与线程是绑定的:

  • 出生: 在线程内部第一次获取 RunLoop 时创建。
  • 消亡: 当线程结束(Exit)时,其对应的 RunLoop 也会被销毁。
  • 注意: 你无法自己创建 RunLoop 对象(不能 alloc init),只能通过系统提供的 API 获取当前线程的 RunLoop。

4. 为什么线程需要 RunLoop?

默认情况下,线程执行完入口任务(block 或 selector)后就会立即退出(销毁)。

  • 没有 RunLoop 的线程:
    plaintext
    // 任务执行完,线程立马死掉
    [NSThread detachNewThreadSelector:@selector(doSomething) toTarget:self withObject:nil];
  • 有 RunLoop 的线程:
    RunLoop 的本质是一个 do...while 循环。它让线程在有工作时忙碌,没工作时休眠(释放 CPU 资源),而不是直接退出。
    • 应用场景: 如果你需要一个子线程“常驻”在后台,随时等待处理新的任务(例如:后台网络监听、定时器 NSTimer),你就需要在这个子线程中开启 RunLoop。

5. 代码层面的体现

在 Core Foundation 源码中,获取 RunLoop 的逻辑大致如下(伪代码):

c
// 获取当前线程的 RunLoop
CFRunLoopRef _CFRunLoopGet0(pthread_t thread) {
    // 1. 加锁访问全局字典
    // 2. 尝试从字典中根据 thread 获取 RunLoop
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, thread);
    
    if (!loop) {
        // 3. 如果没有,创建一个新的 RunLoop
        loop = __CFRunLoopCreate(thread);
        // 4. 存入字典
        CFDictionarySetValue(__CFRunLoops, thread, loop);
    }
    
    // 5. 返回 RunLoop
    return loop;
}

6. 总结对比

特性 主线程 (Main Thread) 子线程 (Child Thread)
RunLoop 状态 默认已创建并启动 默认无 RunLoop
创建时机 App 启动时系统自动创建 第一次调用 currentRunLoop 时创建
启动方式 系统自动 [runloop run] 开发者需手动调用 [runloop run]
主要作用 处理 UI、点击、屏幕刷新 处理耗时操作、后台常驻任务
线程存活 App 运行期间一直存活 任务结束即销毁(除非开启 RunLoop 保活)

形象的比喻

  • 线程 是一个 工人
  • RunLoop 是一个 流水线/任务管理系统
  • 主线程工厂里的核心工人,他一上班,老板(系统)就给他配了一条流水线(RunLoop),让他一直盯着,有活干活,没活休息,不能回家。
  • 子线程临时工。默认情况下,临时工干完手里的活就直接下班回家了(线程销毁)。如果你想让临时工一直待在工厂里听候差遣,你就必须专门给他配一条流水线(手动创建并开启 RunLoop),告诉他:“没活的时候在旁边睡觉,别回家,有活我会叫醒你”。
右滑查看面试常问