Day8-RunLoop 构成与线程保活

YVTU

一、RunLoop 的构成

RunLoop 是基于 CFRunLoopRef(Core Foundation 层)构建的,NSRunLoop 是其 Foundation 层的封装。核心组成如下:

1. RunLoop 对象

  • 每条线程都有唯一的一个 RunLoop 对象(主线程默认创建,子线程需手动创建)。

2. RunLoopMode

  • RunLoop 在任一时刻只能运行在一种模式下。
  • 常见模式:
    • NSDefaultRunLoopMode
    • UITrackingRunLoopMode
    • NSRunLoopCommonModes

3. Source(事件源)

  • Source0:非基于 Port 的,如 performSelector:onThread:
  • Source1:基于 Port 的,如 GCD 主线程通信

4. Timer

  • NSTimer 和 CADisplayLink 都依赖 RunLoop

5. Observer(观察者)

  • 监听 RunLoop 状态变化,如将要进入、即将处理事件、即将休眠等

6. Port

  • 线程通信载体,配合 Source1 使用

二、RunLoop 与线程保活

1. 子线程默认行为

任务执行完即退出,因为没有 RunLoop。

2. 线程保活方式

1
2
3
4
5
6
7
- (void)startThread {
NSThread *thread = [[NSThread alloc] initWithBlock:^{
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}];
[thread start];
}

3. 注意事项

  • run 会阻塞线程。
  • RunLoop 需要至少一个 Input Source 否则会立刻退出。

三、RunLoop 的实际应用(含示例)

1. 保持线程不退出

后台日志收集/解码任务中使用 RunLoop 保活线程。

2. 事件响应机制

如 Touch、Button 点击,通过 Source1 分发给主线程 RunLoop。

3. 定时器

NSTimer/CADisplayLink 依赖 RunLoop 触发。

4. GCD 主队列回调延迟

主线程卡顿时延迟执行,原因是 GCD 回调依赖 Source1。

5. UI 滚动与事件隔离

滚动 UIScrollView 时切换 Mode,暂停定时器触发。

6. AutoreleasePool 管理

RunLoop 每次循环自动创建/释放 pool,防止泄露。

7. 线程间通信

基于 Port 的消息传递,结合 Source1 实现。


四、RunLoop 实现原理

RunLoop 本质是一个基于事件循环的结构:

1
2
3
4
5
6
7
8
while (!stop) {
__CFRunLoopDoObservers(kCFRunLoopEntry);
__CFRunLoopDoTimers();
__CFRunLoopDoSources0();
mach_msg(); // 等待消息唤醒
__CFRunLoopDoSources1();
__CFRunLoopDoObservers(kCFRunLoopExit);
}

核心机制:

模块 说明
mach_port 底层通信机制
Source0 自定义事件
Source1 系统事件(如 GCD)
Timer 定时任务
Observer 状态监听
Mode 事件隔离与切换模式

Mode 与 Input Source 关系结构:

1
2
3
4
5
6
7
8
9
RunLoop
├── ModeA
│ ├── Source0
│ ├── Source1
│ ├── Timer
│ └── Observer
└── ModeB
├── Source0
└── ...

五、总结

分类 应用
UI交互 事件响应、触摸分发、主队列 GCD
多线程 线程保活、线程通信
性能优化 滚动时暂停动画
内存管理 自动释放池管理
动画处理 CADisplayLink 刷新帧率