Day14-RunLoop&多线程&内存管理
一、核心概念
1. RunLoop
- RunLoop 是线程的事件处理循环,是 NSRunLoop和CFRunLoopRef的封装。
- 默认只在主线程自动启动。
- 管理事件源(Timer、Input Source、Observer)。
- 保持线程活跃、延迟执行任务、监听输入等都依赖 RunLoop。
2. 多线程
- iOS 支持多线程方式有:- NSThread
- GCD(主流)
- NSOperationQueue
 
- 每条线程都有自己独立的 RunLoop,但默认只有主线程的 RunLoop 自动运行。
3. 内存管理(以 ARC 为基础)
- 使用 retain/release管理对象生命周期。
- strong/weak/unowned控制引用关系。
- 多线程场景中注意引用循环、野指针、线程同步问题。
二、三者之间的联系
RunLoop 与 多线程
- 子线程若需处理 Timer、事件监听,必须手动开启 RunLoop。
- 子线程默认 RunLoop 不启动,任务执行完就退出。
- 示例:1 
 2
 3
 4
 5
 6
 7
 8- (void)startBackgroundThread { 
 NSThread *thread = [[NSThread alloc] initWithBlock:^{
 NSLog(@"Start thread");
 [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
 [[NSRunLoop currentRunLoop] run];
 }];
 [thread start];
 }
多线程 与 内存管理
- ARC 负责线程安全地管理引用计数。
- GCD 的 Block 默认强引用捕获外部变量,注意内存泄漏。
- 多线程中访问共享资源要小心并发读写带来的内存访问异常。
RunLoop 与 内存管理
- RunLoop 会持有它注册的事件源(Timer、Port 等)。
- 若 Timer target 对象被强引用,容易造成循环引用:1 
 2// 错误示例 
 self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES];
三、典型场景解析
| 场景 | 涉及点 | 注意事项 | 
|---|---|---|
| 子线程保持活跃 | RunLoop + 多线程 | 添加 Port / Timer 后手动启动 RunLoop | 
| 定时器导致内存泄漏 | RunLoop + 内存管理 | 使用 weak self,或中介对象解耦 | 
| 异步任务中回调 UI | 多线程 + RunLoop | 回到主线程,主线程的 RunLoop 驱动 UI 刷新 | 
| GCD 死锁 | 多线程 | dispatch_sync嵌套调用主队列容易死锁 | 
| RunLoop 的生命周期管理 | RunLoop + 内存管理 | 注意强引用阻止线程退出 | 
四、面试问题举例
Q1: 子线程中 Timer 不执行,为什么?
A: 子线程默认 RunLoop 没有启动,Timer 依赖 RunLoop 执行。
Q2: NSTimer 为什么可能引起内存泄漏?如何避免?
A: 因为 NSTimer 强引用 target,target 又强引用 timer。使用
weak或中间对象(如NSProxy)解决循环引用。
Q3: GCD 的异步任务中为何 UI 更新失败?
A: UI 更新必须在主线程中执行,异步任务默认不在主线程,需要使用
dispatch_async(dispatch_get_main_queue(), ^{ })。
Q4: RunLoop 如何保持线程常驻?
A: 使用
[[NSRunLoop currentRunLoop] run],或者添加事件源如 Port 或 Timer 保持 RunLoop 活跃。
五、总结图示
| 1 | 线程(Thread) |