RunLoop 的理解和使用

一、RunLoop 的基本概念

1.1 什么是 RunLoop

  • RunLoop(运行循环)是 iOS 和 macOS 系统中用于管理线程事件循环的基础设施。
  • 它的本质是一个事件处理循环,不断地从事件源(输入源、定时源)中获取事件并分发处理。
  • RunLoop 让线程在有任务时工作、无任务时休眠,从而节省系统资源。

1.2 线程与 RunLoop 的关系

  • 每个线程都可以拥有自己的 RunLoop,主线程的 RunLoop 默认已经启动。
  • 子线程的 RunLoop 默认没有启动,需要手动创建和启动。
  • RunLoop 主要用于需要持续响应事件的线程(如主线程、需要定时器/端口/输入源的子线程)。

1.2.1 主线程 RunLoop 的启动

主线程的 RunLoop 在 UIApplicationMain() 启动时自动创建并启动:

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

1.2.2 子线程 RunLoop 的获取和启动

  • 获取当前线程的 RunLoop:
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
  • 启动子线程的 RunLoop:
[[NSRunLoop currentRunLoop] run];
  • 注意:Cocoa 的 NSRunLoop 不是线程安全的,不能跨线程操作。CoreFoundation 的 CFRunLoopRef 是线程安全的。

二、RunLoop 的常见应用场景

2.1 子线程中使用定时器

  • 主线程的 RunLoop 默认启动,定时器能正常工作。
  • 子线程中使用 NSTimer 时,必须手动启动 RunLoop,否则定时器不会触发。
- (void)test2 {
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread) object:nil];
    [thread start];
}

- (void)newThread {
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] run];
}

2.2 GCD 定时器

  • GCD 的 dispatch_source_t 也依赖底层的 RunLoop 机制。
uint64_t interval = 0.01 * NSEC_PER_SEC;
dispatch_queue_t queue = dispatch_queue_create("my_queue", 0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, 0), interval, 0);
dispatch_source_set_event_handler(timer, ^{
    NSLog(@"GCD定时器触发");
});
dispatch_resume(timer);

2.3 主线程定时器的 Mode 问题

  • 主线程的 NSTimer 默认加到 NSDefaultRunLoopMode,如果遇到 UI 滚动等会被暂停。
  • 可将 Timer 加到 NSRunLoopCommonModes,避免被 UI 事件影响。
self.timer = [NSTimer timerWithTimeInterval:0.01 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

三、RunLoop 的原理与机制

3.1 RunLoop 的事件源

  • 输入源(Input Source):如端口、Source0/Source1、用户输入等。
  • 定时源(Timer Source):如 NSTimer、GCD Timer 等。

3.2 RunLoop 的运行模式(Mode)

  • NSDefaultRunLoopMode:默认模式,处理普通事件。
  • UITrackingRunLoopMode:用于 UI 追踪(如滚动)。
  • NSRunLoopCommonModes:包含一组常用模式,Timer 加到此模式可避免 UI 事件影响。

3.3 RunLoop 的优点

  • 事件驱动,自动休眠和唤醒,节省系统资源。
  • 高度抽象,简化事件处理流程。
  • 支持定时器、输入源、端口等多种事件源。
  • 自动管理 autorelease pool。

四、常见问题与补充

4.1 为什么主线程的定时器能用,子线程的不行?

  • 主线程 RunLoop 默认启动,子线程需手动启动 RunLoop。

4.2 RunLoop 会不会造成性能问题?

  • 不会,RunLoop 在没有事件时会自动休眠,只有事件到来时才会唤醒线程。

4.3 RunLoop 和 while(1) 死循环有何区别?

  • RunLoop 是事件驱动+自动休眠,while(1) 死循环会一直占用 CPU。

4.4 RunLoop 与 autorelease pool

  • RunLoop 每次循环结束时会自动释放一次 autorelease pool,避免内存泄漏。

五、代码示例

5.1 子线程定时器

- (void)test2 {
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread) object:nil];
    [thread start];
}

- (void)newThread {
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] run];
}

5.2 GCD 定时器

uint64_t interval = 0.01 * NSEC_PER_SEC;
dispatch_queue_t queue = dispatch_queue_create("my_queue", 0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, 0), interval, 0);
dispatch_source_set_event_handler(timer, ^{
    NSLog(@"GCD定时器触发");
});
dispatch_resume(timer);

5.3 主线程定时器加到 CommonModes

self.timer = [NSTimer timerWithTimeInterval:0.01 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

5.4 主线程 RunLoop 启动

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

六、总结

  • 每个线程都有一个 RunLoop 实例,主线程的 RunLoop 在 UIApplication 启动时自动启动。
  • RunLoop 让线程能够持续监听事件(如触摸、网络、定时器),实现“随时待命”的效果。
  • RunLoop 只处理两类事件源:输入源(Input Source)定时源(Timer Source)
  • 子线程如需事件循环,需手动启动 RunLoop 并添加事件源。

参考资料


著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容