上两篇文章说了Runloop初探和Runloop获取,这篇来说说Runloop执行流程。
- 我们直接从
Runloop
的run
开始:
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);//调用RunLoop执行函数
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;// 如果RunLoop正在释放,返回完成
__CFRunLoopLock(rl);
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);//查找对应的 Mode
...
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);//通知 Observers: 进入RunLoop
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);//运行循环核心
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);//通知 Observers: 退出RunLoop
...
return result;
}
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
...
mach_port_name_t dispatchPort = MACH_PORT_NULL;//主线程的端口,便于和主线程通信
Boolean libdispatchQSafe = pthread_main_np() && ((HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && NULL == previousMode) || (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ)));//判断是否为主线程
//如果在主线程 && runloop是主线程的runloop && 该mode是commonMode
if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) dispatchPort = _dispatch_get_main_queue_port_4CF();//获取主线程端口
#if USE_DISPATCH_SOURCE_FOR_TIMERS
mach_port_name_t modeQueuePort = MACH_PORT_NULL;//GCD 当中根队列的 Port,管理着所有的子队列
if (rlm->_queue) {
modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue);//获取端口
...
}
#endif
...
Boolean didDispatchPortLastTime = true;//标志位默认为true
int32_t retVal = 0;//记录最后runloop状态,用于return
do {
...
__CFRunLoopUnsetIgnoreWakeUps(rl);//设置RunLoop为可以被唤醒状态
if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);//2、通知observer 即将处理 Timer 事件
if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);//3、通知observer 即将处理 sources 事件
__CFRunLoopDoBlocks(rl, rlm);//处理加入当前runloop的block
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);//4、处理 source0 事件
if (sourceHandledThisLoop) {
__CFRunLoopDoBlocks(rl, rlm);//处理加入当前runloop的block
}
Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);//如果有Sources0事件处理 或者 超时,poll都为true
if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
msg = (mach_msg_header_t *)msg_buffer;
//读取消息, 不睡眠当前线程(timeout:0)
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
goto handle_msg;//从dispatchPort读取消息
}
#elif DEPLOYMENT_TARGET_WINDOWS
...
#endif
}
didDispatchPortLastTime = false;
if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);//8、通知 Observers: RunLoop 的线程即将进入休眠(sleep)
__CFRunLoopSetSleeping(rl);//没有事件处理就休眠
...
__CFRunLoopUnsetSleeping(rl);//9.休眠结束
if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
//10、被谁唤醒就处理什么事件
handle_msg:;
__CFRunLoopSetIgnoreWakeUps(rl);
...
if (MACH_PORT_NULL == livePort) { ... } else if (livePort == rl->_wakeUpPort) { ... }
#if USE_DISPATCH_SOURCE_FOR_TIMERS //如果是定时器事件
else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
CFRUNLOOP_WAKEUP_FOR_TIMER();
//处理 Timer 事件
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
// Re-arm the next timer, because we apparently fired early
__CFArmNextTimerInMode(rlm, rl);
}
}
#endif
#if USE_MK_TIMER_TOO //如果是定时器事件
...
#endif
else if (livePort == dispatchPort) {//如果是dispatch到main queue的block
...
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);//处理主线程上的消息;GCD主线程回调的消息由Runloop处理,GCD子线程的消息由GCD处理
...
} else {
...
CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
if (rls) {
...
//处理 Source1
sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
...
}
...
}
...
if (sourceHandledThisLoop && stopAfterHandle) {
retVal = kCFRunLoopRunHandledSource;//进入run loop时传入的参数,处理完事件就返回
} else if (timeout_context->termTSR < mach_absolute_time()) {
retVal = kCFRunLoopRunTimedOut;//run loop超时
} else if (__CFRunLoopIsStopped(rl)) {
__CFRunLoopUnsetStopped(rl);//run loop被手动终止
retVal = kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
rlm->_stopped = false;//mode被终止
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
retVal = kCFRunLoopRunFinished;//mode中没有要处理的事件
}
...
} while (0 == retVal);
...
return retVal;
}
代码流程
- 补充
1.RunLoop
包含多个mode
,每个mode
包含多个sources0
事件,多个sources1
事件,多个timer
事件,多个observer
事件和多port
,RunLoop
总在某个mode
下运行的:
2.Source0
和Source1
区别:
Source0
不能主动触发事件。Source0
只有一个回调(函数指针),使用时先调用 CFRunLoopSourceSignal (source)
将Source
标记为待处理,然后调用 CFRunLoopWakeUp (runloop)
唤醒 RunLoop
,让RunLoop
处理事件。
Source1
能主动唤醒 RunLoop
的线程。Source1
有一个 mach_port
和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。
网友评论