基本概念
在iOS和macOS中,RunLoop是一个循环机制,用于管理线程中的事件处理。当一个线程启动后,系统会为其创建一个RunLoop,它会不断地运行、检查和处理事件。如果没有事件需要处理,RunLoop会让线程进入休眠状态,从而节省系统资源。
获取当前线程的RunLoop
1
| NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
|
这段代码获取了当前线程的RunLoop实例。对于主线程,这是系统自动创建的;对于后台线程,通常需要手动启动RunLoop。
RunLoop 的主要任务是:
- 处理事件和消息: 处理来自各种输入源的事件,例如触摸事件、网络事件等。
- 调度定时任务: 处理定时器触发的任务。
- 保持线程活跃: 确保线程在有任务时不会空闲而关闭。
运行模式
RunLoop可以在多种模式下运行,最常用的是kCFRunLoopDefaultMode(默认模式)和UITrackingRunLoopMode(UI跟踪模式)。RunLoop在不同模式下处理不同类型的事件。
在不同模式下添加Timer
1 2 3 4 5
| NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerFired:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
|
上面代码展示了如何在RunLoop的不同模式下添加Timer。这意味着Timer在这两种模式下都会被执行。
事件源
RunLoop通过事件源(Source)来获取并处理事件。事件源主要分为输入源(Input Source)和定时源(Timer Source)。输入源主要用于处理异步事件,如用户输入、网络请求等;定时源则用于处理定时事件,如NSTimer。
自定义RunLoop输入源
1 2 3
| CFRunLoopSourceContext context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &myRunLoopSourcePerformRoutine}; CFRunLoopSourceRef runLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
|
在这个例子中,我们创建了一个自定义的RunLoop输入源,并将其添加到默认模式的RunLoop中。自定义的事件源允许我们处理自定义的事件。
事件处理
在RunLoop的每一次循环中,都会处理事件队列中的事件。RunLoop通过检查事件源来决定是否有需要处理的事件。如果有,它会调用对应的处理函数来处理该事件;如果没有,则进入休眠状态。
处理RunLoop事件的示例代码
1 2 3 4 5 6 7 8 9 10 11
| void myRunLoopSourcePerformRoutine(void *info) { NSLog(@"RunLoop事件已处理"); }
CFRunLoopSourceContext context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &myRunLoopSourcePerformRoutine}; CFRunLoopSourceRef runLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
|
在这段代码中,myRunLoopSourcePerformRoutine函数用于处理RunLoop事件源中的事件。当有事件需要处理时,RunLoop会调用这个函数。
线程管理中的应用
在后台线程中,RunLoop通常不会自动启动。如果我们希望一个后台线程持续运行,并且能够响应事件,就需要手动启动RunLoop。
在后台线程中启动RunLoop
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| - (void)startBackgroundTask { NSThread *backgroundThread = [[NSThread alloc] initWithTarget:self selector:@selector(runBackgroundRunLoop) object:nil]; [backgroundThread start]; }
- (void)runBackgroundRunLoop { NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; [runLoop run]; } `` 上面代码创建了一个后台线程,并在其中启动了RunLoop。通过添加一个NSMachPort对象,RunLoop能够保持活跃,并处理来自其他线程的事件。
## 常见使用场景
RunLoop在iOS开发中有广泛的应用场景,如处理长时间运行的任务、管理定时器和网络请求、实现复杂的UI响应逻辑等。例如,在后台下载任务中,RunLoop可以帮助保持线程的活跃,以确保下载过程不会中断。
创建一个 RunLoop 并添加一个定时器来保持线程的活动状态: ```objc #import <Foundation/Foundation.h>
void timerCallback(CFRunLoopTimerRef timer, void *info) { NSLog(@"Timer fired!"); }
int main(int argc, const char * argv[]) { @autoreleasepool { CFRunLoopRef runLoop = CFRunLoopGetCurrent(); CFRunLoopTimerContext context = {0}; CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + 1.0, 1.0, 0, 0, timerCallback, &context); CFRunLoopAddTimer(runLoop, timer, kCFRunLoopCommonModes); NSLog(@"RunLoop started..."); CFRunLoopRun(); CFRunLoopRemoveTimer(runLoop, timer, kCFRunLoopCommonModes); CFRelease(timer); } return 0; }
|
RunLoop 源码结构
RunLoop 的主要组件有输入源(Input Sources)、运行模式(RunLoop Modes)和任务队列(Tasks Queue)。
输入源包括用于处理来自 Mach 消息的事件的端口。用于处理定时器事件的定时器。RunLoop 支持不同的运行模式,每个模式可以处理不同的事件类型。例如,NSDefaultRunLoopMode 和 UITrackingRunLoopMode。任务队列存储待处理的任务,如定时器、事件等。
RunLoop 源码可以在 iOS 的底层系统库中找到。RunLoop 的实现涉及多个组件和文件。以下是一些关键部分的解析:
在 Core Foundation 框架中,CFRunLoop 是 RunLoop 的核心实现。以下是 CFRunLoop 的定义和主要功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| typedef struct __CFRunLoop * CFRunLoopRef;
struct __CFRunLoop { CFRuntimeBase _base; CFMutableSetRef _modes; CFRunLoopModeRef _currentMode; CFMutableSetRef _sources0; CFMutableSetRef _sources1; CFMutableSetRef _observers; CFMutableSetRef _timers; };
|
- _modes: 存储 RunLoop 的运行模式。
- _currentMode: 当前的运行模式。
- _sources0 和 _sources1: 存储不同类型的输入源。
- _observers: 存储观察者,处理特定的事件。
- _timers: 存储定时器,处理定时任务。
主要函数:
- CFRunLoopRun: 启动 RunLoop,并进入主循环。
1 2 3 4 5 6 7 8 9
| void CFRunLoopRun(void) { while (!loopShouldExit) { CFRunLoopMode mode = CFRunLoopCopyCurrentMode(); CFRunLoopPerformTasksForMode(mode); } }
|
- CFRunLoopPerformTasksForMode: 执行当前模式下的任务。
1 2 3 4 5 6 7 8
| void CFRunLoopPerformTasksForMode(CFRunLoopMode mode) { CFRunLoopSourcePerformTasks(mode->_sources); CFRunLoopTimerPerformTasks(mode->_timers); CFRunLoopObserverPerformTasks(mode->_observers); }
|