一. 什么是 Runloop
顾明思议, Runloop就是运行循环.按照正常逻辑一个程序在return 0 返回时候, 这个程序就会退出. 对于 app 来说, 我们希望 app 可以一直运行下去, 等待用户交互, 并做出相应, 那么它就需要能够不停的运行下去, 相当于在它的内部不停地做 do while 循环.
二. Runloop 的基本作用
- 保持程序的持续运行
- 处理APP中的各种事件(触摸、定时器、performSelector)
- 节省cpu资源、提供程序的性能:该做事就做事,该休息就休 息
关于 Runloop 有两套 API 去使用它, 分别是 CoreFoundation
框架 的 CFRunloop
和 Foundation
框架的 的 NSRunLoop
// CoreFoundation
CFRunLoopRef runloop = CFRunLoopGetCurrent();
CFRunLoopRun();
// Foundation
[[NSRunLoop currentRunLoop] run];
我们在开发中大多使用NSRunLoop的 API, 由于 Foundation 并不是开源的, 因此如果我们想要学习 Runloop 相关知识, 弄清楚其内部实现原理, 就需要翻看CFRunLoop的源码来了解其背后的实现原理.
三. Runloop 的数据结构
CoreFoundation关于 RunLoop的 5 个类
CFRunLoopRef #Runloop对象
CFRunLoopModeRef #代表RunLoop的运行模式 __CFRunLoopMode *类型结构体指针
CFRunLoopSourceRef # __CFRunLoopSource * 结构体指针
CFRunLoopTimerRef # __CFRunLoopTimer * 结构体指针
CFRunLoopObserverRef # __CFRunLoopObserver * 结构体指针
1. __CFRunLoop数据结构如下
CFRunLoopRef是一个 Runloop 对象
struct __CFRunLoop {
pthread_t _pthread; # runloop 所在线程
CFMutableSetRef _commonModes; # runloop 的模式, runloop 同时只能运行到一种模式下.
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode; # runloop 当前运行模式
CFMutableSetRef _modes; #runloop 所有模式
};
2. __CFRunLoopMode 数据结构如下
- CFRunLoopModeRef代表RunLoop的运行模式
- 一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0/Source1/Timer/Observer
- RunLoop启动时只能选择其中一个Mode,作为currentMode
- 如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入,不同组的Source0/Source1/Timer/Observer能分隔开来,互不影响
- 如果Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
CFStringRef _name; # 模式的名词 比如 kCFRunLoopDefaultMode, kCFRunLoopCommonModes,
CFMutableSetRef _sources0; # source0
CFMutableSetRef _sources1; # soures1
CFMutableArrayRef _observers; # observers
CFMutableArrayRef _timers; #timers
};
3. __CFRunLoopSource 数据结构如下
struct __CFRunLoopSource {
CFIndex _order; /* immutable */
CFMutableBagRef _runLoops;
union {
CFRunLoopSourceContext version0; /* immutable, except invalidation */
CFRunLoopSourceContext1 version1; /* immutable, except invalidation */
} _context;
};
4. __CFRunLoopTimer 数据结构如下
struct __CFRunLoopTimer {
CFRunLoopRef _runLoop;
CFMutableSetRef _rlModes;
CFAbsoluteTime _nextFireDate;
};
5. __CFRunLoopObserver数据结构如下
struct __CFRunLoopObserver {
CFRunLoopRef _runLoop;
CFRunLoopObserverCallBack _callout; /* immutable */
CFRunLoopObserverContext _context; /* immutable, except invalidation */
};
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), //即将进入 runloop
kCFRunLoopBeforeTimers = (1UL << 1), //即将处理 timer
kCFRunLoopBeforeSources = (1UL << 2), //即将处理 source
kCFRunLoopBeforeWaiting = (1UL << 5), //即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), //即将从休眠中唤醒
kCFRunLoopExit = (1UL << 7), //即将退出 runloop
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
四.Runloop 执行流程
在控制台输入lldb指令 bt
可以看到 runloop 执行的堆栈信息
frame #7: 0x00007fff2513fccd UIKitCore`__eventFetcherSourceCallback + 232
frame #8: 0x00007fff20373833 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
frame #9: 0x00007fff2037372b CoreFoundation`__CFRunLoopDoSource0 + 180
frame #10: 0x00007fff20372bf8 CoreFoundation`__CFRunLoopDoSources0 + 242
frame #11: 0x00007fff2036d2f4 CoreFoundation`__CFRunLoopRun + 871
frame #12: 0x00007fff2036ca90 CoreFoundation`CFRunLoopRunSpecific + 562
-
CFRunLoopRun函数
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
//可以看出来 runloop 是一个 do while 循环 只要条件成立 会一直执行 while 循环里边的代码, 一直到返回结果是 kCFRunLoopRunStopped 或者 kCFRunLoopRunFinished 才会退出while循环
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
- CFRunLoopRunSpecific函数
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
if (currentMode->_observerMask & kCFRunLoopEntry ) { // 通知observes: 即将进入 runloop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
}
// 通过返回值 反推这里 就是 runloop 要做的事情 最终返回一个 result 结果
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
if (currentMode->_observerMask & kCFRunLoopExit ) { // 通知 observers: 即将退出 runloop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
}
// 根据 SInt32 返回值 result 向上反推导 result 赋值地方就是核心的代码
return result;
}
- __CFRunLoopRun函数
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
int32_t retVal = 0;
do {
// 通知 observer 即将处理 timer
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
// 通知 observer 即将处理 sources
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
// 通知 observer 即将处理 block
__CFRunLoopDoBlocks(rl, rlm);
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
if (sourceHandledThisLoop) { // 处理 source0 完毕后 如果返回 YES 将会处理 blocks
__CFRunLoopDoBlocks(rl, rlm);
}
Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
// 判断有无 source1
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
// 如果有 source1 跳转到 handle_msg
goto handle_msg;
}
didDispatchPortLastTime = false;
// 通知 observer 即将休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
// 开始休眠
__CFRunLoopSetSleeping(rl);
CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();
do {
// 等待别的消息来唤醒当前线程 如果被唤醒 继续往下走
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
} while (1);
// 结束休眠
__CFRunLoopUnsetSleeping(rl);
// 通知 observer 结束休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
handle_msg:;
// 被 timer 唤醒
if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
// 处理 timer
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
// Re-arm the next timer
__CFArmNextTimerInMode(rlm, rl);
}
}
else if (livePort == dispatchPort) { // GCD 相关
// 处理 gcd 相关的事情
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else { // 被source1唤醒
// 处理 source1
sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
}
// 继续处理 block
__CFRunLoopDoBlocks(rl, rlm);
// 设置函数返回值
if (sourceHandledThisLoop && stopAfterHandle) {
retVal = kCFRunLoopRunHandledSource; // 处理 source
} else if (timeout_context->termTSR < mach_absolute_time()) {
retVal = kCFRunLoopRunTimedOut; // 超时
} else if (__CFRunLoopIsStopped(rl)) {
__CFRunLoopUnsetStopped(rl);
retVal = kCFRunLoopRunStopped; //停止
} else if (rlm->_stopped) {
rlm->_stopped = false;
retVal = kCFRunLoopRunStopped; //停止
} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
retVal = kCFRunLoopRunFinished; //结束
}
} while (0 == retVal);
return retVal;
}
五. Runloop 应用
1. 线程保活
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.thread = [[MJThread alloc] initWithBlock:^{
NSLog(@"%@",[NSThread currentThread]);
// 创建上下文
CFRunLoopSourceContext context = {0};
// 创建source
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
// 向Runloop中添加source
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
// 销毁source
CFRelease(source);
// 第三个参数returnAfterSourceHandled: 设置为true时,代表执行完任务之后就会退出当前loop
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, false);
NSLog(@"---- runloop 停止成功");
}];
[self.thread start];
}
// 退出 runloop
- (void) stopRunloop {
CFRunLoopStop(CFRunLoopGetCurrent());
}
- (void)dealloc {
[self performSelector:@selector(stopRunloop) onThread:self.thread withObject:nil waitUntilDone:NO];
NSLog(@"MJSecondViewController dealloc");
}
2. timer滑动失效的问题
3. 监听主线程卡顿
static void CallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
MJBlockMonitor *monitor = (__bridge MJBlockMonitor *)info;
monitor->activity = activity;
// 发送信号
dispatch_semaphore_t semaphore = monitor->_semaphore;
dispatch_semaphore_signal(semaphore);
}
- (void)registerObserver{
CFRunLoopObserverContext context = {0,(__bridge void*)self,NULL,NULL};
//NSIntegerMax : 优先级最小
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
kCFRunLoopAllActivities,
YES,
NSIntegerMax,
&CallBack,
&context);
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
}
- (void)startMonitor{
// 创建信号
_semaphore = dispatch_semaphore_create(0);
// 在子线程监控时长
dispatch_async(dispatch_get_global_queue(0, 0), ^{
while (YES)
{
// 超时时间是 1 秒,没有等到信号量,st 就不等于 0, RunLoop 所有的任务
long st = dispatch_semaphore_wait(self->_semaphore, dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC));
if (st != 0) {
if (self->activity == kCFRunLoopBeforeSources || self->activity == kCFRunLoopAfterWaiting) {
if (++self->_timeoutCount < 2){
NSLog(@"timeoutCount==%lu",(unsigned long)self->_timeoutCount);
continue;
}
// 一秒左右的衡量尺度 很大可能性连续来 避免大规模打印!
NSLog(@"检测到超过两次连续卡顿");
}
}
self->_timeoutCount = 0;
}
});
}