RunLoop 的内部逻辑

根据苹果在文档里的说明,RunLoop 内部的逻辑大致如下:

RunLoop 的内部逻辑_第1张图片

其内部代码整理如下 (太长了不想看可以直接跳过去,后面会有说明):

/// 用DefaultMode启动

void CFRunLoopRun(void) {

CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10,false);

}

/// 用指定的Mode启动,允许设置RunLoop超时时间

int CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean stopAfterHandle) {

returnCFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);

}

/// RunLoop的实现

int CFRunLoopRunSpecific(runloop, modeName, seconds, stopAfterHandle) {

/// 首先根据modeName找到对应mode

CFRunLoopModeRef currentMode = __CFRunLoopFindMode(runloop, modeName,false);

/// 如果mode里没有source/timer/observer, 直接返回。

if(__CFRunLoopModeIsEmpty(currentMode))return;

/// 1. 通知 Observers: RunLoop 即将进入 loop。

__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopEntry);

/// 内部函数,进入loop

__CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled) {

Boolean sourceHandledThisLoop = NO;

int retVal = 0;

do{

/// 2. 通知 Observers: RunLoop 即将触发 Timer 回调。

__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers);

/// 3. 通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。

__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources);

/// 执行被加入的block

__CFRunLoopDoBlocks(runloop, currentMode);

/// 4. RunLoop 触发 Source0 (非port) 回调。

sourceHandledThisLoop = __CFRunLoopDoSources0(runloop, currentMode, stopAfterHandle);

/// 执行被加入的block

__CFRunLoopDoBlocks(runloop, currentMode);

/// 5. 如果有 Source1 (基于port) 处于 ready 状态,直接处理这个 Source1 然后跳转去处理消息。

if(__Source0DidDispatchPortLastTime) {

Boolean hasMsg = __CFRunLoopServiceMachPort(dispatchPort, &msg)

if(hasMsg) goto handle_msg;

}

/// 通知 Observers: RunLoop 的线程即将进入休眠(sleep)。

if(!sourceHandledThisLoop) {

__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting);

}

/// 7. 调用 mach_msg 等待接受 mach_port 的消息。线程将进入休眠, 直到被下面某一个事件唤醒。

/// ? 一个基于 port 的Source 的事件。

/// ? 一个 Timer 到时间了

/// ? RunLoop 自身的超时时间到了

/// ? 被其他什么调用者手动唤醒

__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) {

mach_msg(msg, MACH_RCV_MSG, port);// thread wait for receive msg

}

/// 8. 通知 Observers: RunLoop 的线程刚刚被唤醒了。

__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting);

/// 收到消息,处理消息。

handle_msg:

/// 9.1 如果一个 Timer 到时间了,触发这个Timer的回调。

if(msg_is_timer) {

__CFRunLoopDoTimers(runloop, currentMode, mach_absolute_time())

}

/// 9.2 如果有dispatch到main_queue的block,执行block。

elseif(msg_is_dispatch) {

__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);

}

/// 9.3 如果一个 Source1 (基于port) 发出事件了,处理这个事件

else{

CFRunLoopSourceRef source1 = __CFRunLoopModeFindSourceForMachPort(runloop, currentMode, livePort);

sourceHandledThisLoop = __CFRunLoopDoSource1(runloop, currentMode, source1, msg);

if(sourceHandledThisLoop) {

mach_msg(reply, MACH_SEND_MSG, reply);

}

}

/// 执行加入到Loop的block

__CFRunLoopDoBlocks(runloop, currentMode);

if(sourceHandledThisLoop && stopAfterHandle) {

/// 进入loop时参数说处理完事件就返回。

retVal = kCFRunLoopRunHandledSource;

}elseif(timeout) {

/// 超出传入参数标记的超时时间了

retVal = kCFRunLoopRunTimedOut;

}elseif(__CFRunLoopIsStopped(runloop)) {

/// 被外部调用者强制停止了

retVal = kCFRunLoopRunStopped;

}elseif(__CFRunLoopModeIsEmpty(runloop, currentMode)) {

/// source/timer/observer一个都没有了

retVal = kCFRunLoopRunFinished;

}

/// 如果没超时,mode里没空,loop也没被停止,那继续loop。

}while(retVal == 0);

}

/// 10. 通知 Observers: RunLoop 即将退出。

__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

}

可以看到,实际上 RunLoop 就是这样一个函数,其内部是一个 do-while 循环。当你调用 CFRunLoopRun() 时,线程就会一直停留在这个循环里;直到超时或被手动停止,该函数才会返回。

你可能感兴趣的:(RunLoop 的内部逻辑)