RunLoop是iOS开发中的核心机制,它使线程能够在没有工作时进入休眠状态,并在有新任务时被唤醒。本文将深入探讨RunLoop的工作原理及其在实际开发中的应用。
1. RunLoop基础结构
1.1 RunLoop对象模型
RunLoop是一个对象,用于处理来自不同源的输入事件,并调度这些事件到对应的处理程序。在iOS中,有两套API可以访问RunLoop:
- Foundation框架中的
NSRunLoop类 - Core Foundation框架中的
CFRunLoopRef类型
这两者是紧密关联的,NSRunLoop实际上是对CFRunLoopRef的封装,提供了面向对象的接口。

每个线程都有且只有一个RunLoop对象与之关联,主线程的RunLoop在应用启动时自动创建并运行,而子线程的RunLoop需要显式创建和运行。
// 获取当前线程的RunLoop
NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
// 获取主线程的RunLoop
NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop];
// Core Foundation中的等效调用
CFRunLoopRef currentCFRunLoop = CFRunLoopGetCurrent();
CFRunLoopRef mainCFRunLoop = CFRunLoopGetMain();
1.2 事件源分类与处理
RunLoop处理的事件源分为三种类型:
- Source0:非基于端口的输入源,需要手动触发。主要包括:
- 用户触摸事件
- 界面刷新事件
- 自定义事件
- Source1:基于端口的输入源,可以自动触发。包括:
- 系统事件
- 进程间通信
- Mach端口事件
- Timer:基于时间的触发源,包括:
- NSTimer
- CADisplayLink
- performSelector:withObject:afterDelay:

1.3 运行模式与切换机制
RunLoop可以在不同的模式下运行,每种模式定义了一组事件源和定时器的集合。RunLoop在某一时刻只能运行在一种模式下,如果想要切换模式,只能退出当前RunLoop,再以新模式重新启动。
常见的RunLoop模式包括:
- kCFRunLoopDefaultMode (NSDefaultRunLoopMode):默认模式,大多数情况下使用。
- UITrackingRunLoopMode:用于跟踪UI事件(如滚动)时的模式。
- kCFRunLoopCommonModes (NSRunLoopCommonModes):一个模式集合,包含Default和Tracking模式。
// 在默认模式下添加定时器
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerFired:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
// 在UI跟踪模式下添加
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
// 在Common模式下添加(同时在Default和Tracking模式下有效)
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
当界面滚动时,RunLoop会从NSDefaultRunLoopMode切换到UITrackingRunLoopMode,如果Timer只添加到了NSDefaultRunLoopMode,则在滚动时Timer不会被触发。这就是为什么我们经常使用NSRunLoopCommonModes来确保Timer在滚动时也能正常工作。
1.4 底层数据结构剖析
RunLoop的核心是其内部数据结构的设计。以下是Core Foundation中的RunLoop相关数据结构的简化版本:
struct __CFRunLoop {
CFRuntimeBase _base;
pthread_mutex_t _lock;
__CFPort _wakeUpPort;
Boolean _stopped;
pthread_t _pthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
};
struct __CFRunLoopMode {
CFRuntimeBase _base;
pthread_mutex_t _lock;
CFStringRef _name;
Boolean _stopped;
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
};
struct __CFRunLoopSource {
CFRuntimeBase _base;
uint32_t _bits;
pthread_mutex_t _lock;
CFIndex _order;
CFMutableBagRef _runLoops;
union {
struct {
void (*perform)(void *info);
void *info;
} version0;
struct {
void *(*retain)(void *info);
void (*release)(void *info);
void (*perform)(void *info);
void *info;
} version1;
} _context;
};
struct __CFRunLoopTimer {
CFRuntimeBase _base;
uint16_t _bits;
pthread_mutex_t _lock;
CFRunLoopRef _runLoop;
CFMutableSetRef _rlModes;
CFAbsoluteTime _nextFireDate;
CFTimeInterval _interval;
CFTimeInterval _tolerance;
uint64_t _fireTSR;
CFIndex _order;
void (*_callout)(CFRunLoopTimerRef timer, void *info);
void *_context;
};

2. RunLoop与线程
2.1 主线程RunLoop特性
主线程的RunLoop具有一些特殊的特性:
- 自动创建和启动:主线程的RunLoop在应用启动时自动创建并启动,不需要手动干预。
- 处理UI事件:主线程RunLoop负责处理所有的UI事件,包括触摸、手势等。
- 与应用生命周期绑定:主线程RunLoop会一直运行,直到应用终止。
- 集成系统框架:主线程RunLoop集成了UIKit、CoreAnimation等框架的事件处理。
// 主线程RunLoop已自动启动,不需要手动启动
NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop];
// 可以添加额外的输入源或定时器
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(onTimer:) userInfo:nil repeats:YES];
[mainRunLoop addTimer:timer forMode:NSRunLoopCommonModes];
2.2 RunLoop线程管理机制
RunLoop与线程之间存在着密切的关系:
- 一对一关系:每个线程都有且只有一个RunLoop对象,且不能共享。
- 懒创建:除主线程外,其他线程的RunLoop需要显式获取才会被创建。
- 自动销毁:RunLoop会随着线程的结束而销毁。
- 线程保活:通过启动RunLoop可以保持线程不退出,实现线程常驻。
// 创建一个常驻线程
+ (void)createPermanentThread {
static NSThread *permanentThread = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
permanentThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadEntryPoint) object:nil];
[permanentThread setName:@"PermanentThread"];
[permanentThread start];
});
}
// 线程入口函数
+ (void)threadEntryPoint {
@autoreleasepool {
NSRunLoop

最低0.47元/天 解锁文章
3408

被折叠的 条评论
为什么被折叠?



