RunLoop学习记录
什么是RunLoop
运行循环,保持程序的持续运行,不退出,处理APP的跟中事件 runloop源码下载地址:opensource.apple.com/tarballs/CF…
基本作用
- 保持程序的持续运行,
- 处理APP中的各种事件(触摸,定时器,GCD异步回到主线程,runloop中block回调的处理等)
- 节省CPU资源,提供性能,改做事的时候做事,改休息的时候休息
RunLoop对象
iOS中提供了2套Api来访问runlopp
- Fundation : NSRunLoop
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
- Core Fundation : CFRunloop
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象
NSRunLoop是基于CFRunloop的一层OC包装
RunLoop与线程
- 每条线程都有唯一的一个与之对应的RunLoop对象
- RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK();
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
if (rl) return rl;
return _CFRunLoopGet0(pthread_self());
}
- 线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建
- RunLoop会在线程结束时销毁
RunLoop相关的类
Core Foundation中关于RunLoop的5个类
- CFRunLoopRef
- CFRunLoopModeRef
- CFRunLoopSourceRef
- CFRunLoopTimerRef
- CFRunLoopObserverRef
CFRunLoop 的结构体
struct __CFRunLoop {
pthread_t _pthread; // runloop和线程是一一对应关系,每个runloop内部会保留一个对应的线程
CFMutableSetRef _commonModes; //标记为common的mode的集合
CFMutableSetRef _commonModeItems; //commonMode的item集合
CFRunLoopModeRef _currentMode; // 当前的模式
CFMutableSetRef _modes; // CFRunLoopModeRef类型的集合,相对NSArray有序,Set为无序集合
};
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会立马退出
目前已知的Mode有5种
- kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
- UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
- UIInitializationRunLoopMode:在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
- GSEventReceiveRunLoopMode:接受系统事件的内部 Mode,通常用不到
- kCFRunLoopCommonModes:这是一个占位用的Mode,不是一种真正的Mode
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
CFMutableDictionaryRef _portToV1SourceMap;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
dispatch_source_t _timerSource;
dispatch_queue_t _queue;
Boolean _timerFired; // set to true by the source when a timer has fired
Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
mach_port_t _timerPort;
Boolean _mkTimerArmed;
#endif
};
runloop 运行逻辑
- CFMutableSetRef _sources0;
- 触摸事件
- perform selectors
- CFMutableSetRef _sources1;
- 基于port的线程间通讯
- CFMutableArrayRef _observers;
- 监听器
- 用于监听runloop状态变化
- CFMutableArrayRef _timers;
- 定时器, NSTimer
- 定时器, NSTimer
核心代码解析(简化版)
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
int32_t retVal = 0;
do {
// 通知Observers: 即将处理timers
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
// 通知Observers: 即将处理Sources
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
// 处理Blocks (runloop有个方法可以直接处理block)
__CFRunLoopDoBlocks(rl, rlm);
// 处理Sources0
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
if (sourceHandledThisLoop) {
__CFRunLoopDoBlocks(rl, rlm);
}
// 如果有source1 就跳转到handle_msg标记处
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
goto handle_msg;
}
// 通知Observers: 即将休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
// 进入休眠 等待其他消息唤醒
__CFRunLoopSetSleeping(rl);
__CFPortSetInsert(dispatchPort, waitSet);
do {
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
} while (1);
// 醒来了
__CFPortSetRemove(dispatchPort, waitSet);
__CFRunLoopUnsetSleeping(rl);
// 通知Observers: 已经唤醒
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
handle_msg:; // 看谁唤醒runloop ,进行响应处理
// 被timer唤醒
if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())
}else if (livePort == dispatchPort) { // 被GCD唤醒
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else { // 被source1唤醒
__CFRunLoopDoSource1(rl, rlm, rls) || sourceHandledThisLoop;
}
// 执行blocks
__CFRunLoopDoBlocks(rl, rlm);
// 根据之前的处理结果来决定返回值
if (sourceHandledThisLoop && stopAfterHandle) {
retVal = kCFRunLoopRunHandledSource;
} 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;
}
voucher_mach_msg_revert(voucherState);
os_release(voucherCopy);
} while (0 == retVal);
if (timeout_timer) {
dispatch_source_cancel(timeout_timer);
dispatch_release(timeout_timer);
} else {
free(timeout_context);
}
return retVal;
}
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {
// 通知Observers: 进入loop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
// 核心逻辑
__CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
// 通知Observers: 退出loop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
底层运行逻辑流程图
RunLoop应用场景
- 控制线程生命周期(线程保活)
- 解决NSTimer在滑动时停止工作的问题
- 监控应用卡顿
- 性能优化
- 闪退处理