一. 什么是 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;
}
});
}