Day14-RunLoop&多线程&内存管理

YVTU

一、核心概念

1. RunLoop

  • RunLoop 是线程的事件处理循环,是 NSRunLoopCFRunLoopRef 的封装。
  • 默认只在主线程自动启动。
  • 管理事件源(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
2
3
4
5
6
7
8
9
线程(Thread)
└── RunLoop(每个线程一个)
├── Timer(引用 target,需避免循环引用)
├── Input Source(事件、Port)
└── Observer(监听状态)

多线程 ↔ RunLoop(任务调度、常驻线程)
多线程 ↔ 内存管理(同步访问、Block 捕获)
RunLoop ↔ 内存管理(Timer target 循环引用)