Runloop(二)、源码

487 阅读2分钟

1.执行流程图

2.源码

Runloop(一)可知,首先调用了__CFRunloopRunSpecific --> __CFRunloopRun

源码中搜索:CFRunloop.c文件,文件中搜索__CFRunloopRunSpecific

源码间接版伪代码如下:

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */

    //通知Observers: 进入Loop
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    
    // 具体要做的事情
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    
    // 通知Observers:退出Loop
	__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

    return result;
}
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    
    //Macos ,嵌入式,mini嵌入式(平板就是嵌入式)
    #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
    #endif
    
    int32_t retVal = 0;
    do {
        
        //通知observers:即将处理timers
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        
        //通知observers:即将处理source
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);

        //处理blocks
        __CFRunLoopDoBlocks(rl, rlm);
        
        //处理source0
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {
            //处理blocks
            __CFRunLoopDoBlocks(rl, rlm);
        }

        Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);

        //端口相关 判断有无source1
        if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
            msg = (mach_msg_header_t *)msg_buffer;
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
                //如果哟source1 跳转到 handle_msg
                goto handle_msg;
            }
        }

        didDispatchPortLastTime = false;
        
        //通知Observers:即将休眠
	    __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        __CFRunLoopSetSleeping(rl);
	// do not do any user callouts after this point (after notifying of sleeping)

        // Must push the local-to-this-activation ports in on every loop
        // iteration, as this mode could be run re-entrantly and we don't
        // want these ports to get serviced.

        //等待别的消息来唤醒当前线程
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);

        
        __CFRunLoopUnsetSleeping(rl);
        通知Observers:结束休眠
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);

    handle_msg:
        if (被timer唤醒) {
            __CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
            // handle nothing
        }

        else if (被GCD唤醒) {
            //处理GCD
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
            
        } else { //被source1唤醒
            //处理source1
            __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || 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;
        }
    
    } while (0 == retVal);

    if (timeout_timer) {
        dispatch_source_cancel(timeout_timer);
        dispatch_release(timeout_timer);
    } else {
        free(timeout_context);
    }

    return retVal;
}
  • 处理block: CFRunLoopPerformBlock(<#CFRunLoopRef rl#>, <#CFTypeRef mode#>, <#^(void)block#>);

3.调用的细节

Runloop很多API,最终会调一些很长的函数。比如__CFRunLoopDoObservers最终会调如下图:

如图:

  • GCD不依赖Runloop,GCD很多操作自己处理,只不过有些情况会交给Runloop处理。如下:
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        // 处理一些子线程的逻辑
        
        // 回到主线程去刷新UI界面
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"11111111111");
        });

Runloop休眠的细节

源码中Runloop休眠的实现__CFRunLoopServiceMachPort,调的是内核API: Mach_msg