美文网首页
iOS 底层 - runloop的源码分析

iOS 底层 - runloop的源码分析

作者: 水中的蓝天 | 来源:发表于2020-04-05 22:21 被阅读0次

本文源自本人的学习记录整理与理解,其中参考阅读了部分优秀的博客和书籍,尽量以通俗简单的语句转述。引用到的地方如有遗漏或未能一一列举原文出处还望见谅与指出,另文章内容如有不妥之处还望指教,万分感谢 !

在开始之前需要对:iOS 底层 - runloop内部的数据结构 有一个简单认识会好一些!

简化后的源码解读 - -CFRunLoop.c


void CFRunLoopRun(void) {   /* DOES CALLOUT */
    int32_t result;
    do {
        //执行入口
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
        CHECK_FOR_FORK();
     //RunLoop没有停止且没有结束就一直执行CFRunLoopRunSpecific函数
    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    
    //通知Observer:即将进入loop
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    
    //核心执行逻辑
     result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    
    //通知Observer:即将退出loop
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

    return result;
    
}

/* rl, rlm are locked on entrance and exit */
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
            __CFRunLoopDoBlocks(rl, rlm);

            //开始处理Sources0
            Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
            if (sourceHandledThisLoop) {//有用到Blocks,就处理
                __CFRunLoopDoBlocks(rl, rlm);
            }

            //判断有没有Sources1,有没有MachPort线程通信
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
                //有就跳转到handle_msg函数处理
                goto handle_msg;
            }

           //通知Observers 即将休眠
          __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        
          //开始休眠
          __CFRunLoopSetSleeping(rl);
        
        CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();

        //等待别的消息来唤醒当前线程,不再往下执行代码了 !一直到有人唤醒
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
          
        //结束休眠
        __CFRunLoopUnsetSleeping(rl);
        
        //通知Observers 结束休眠
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
        
   //handle_msg函数处理
   handle_msg:
        
       if (被Timer唤醒) {
            //处理Timers
            __CFRunLoopDoTimers(rl, rlm, mach_absolute_time())
        }else if (被GCD唤醒) {
            //处理GCD相关的事件
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
            
        } else {//被Sources1唤醒
            //处理Sources1
             __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
        } 
        if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);

        
        //处理Blocks
    __CFRunLoopDoBlocks(rl, rlm);
        
    //设置返回值
    if (sourceHandledThisLoop && stopAfterHandle) {
        //处理源
        retVal = kCFRunLoopRunHandledSource; 4
        
    } else if (timeout_context->termTSR < mach_absolute_time()) {
        //超时
        retVal = kCFRunLoopRunTimedOut; 3
            
    } else if (__CFRunLoopIsStopped(rl)) {
        
            __CFRunLoopUnsetStopped(rl);
        //暂停
        retVal = kCFRunLoopRunStopped; 2
        
    } else if (rlm->_stopped) {
        
        rlm->_stopped = false;
        //暂停
        retVal = kCFRunLoopRunStopped; 2
        
    } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
        //完成
        retVal = kCFRunLoopRunFinished; 1
    }
        
        voucher_mach_msg_revert(voucherState);
        os_release(voucherCopy);

    } while (0 == retVal);

    return retVal;
}

runloop 调用细节

  • 关于:__CFRunLoopDoObservers

static void __CFRunLoopDoObservers(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopActivity activity) { /* DOES CALLOUT */

 //通知Observer处理事件的核心函数调用
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(rlo->_callout, rlo, activity, rlo->_context.info);
    
}

  • 关于 __CFRunLoopDoTimers
// mode and rl are locked on entry and exit
static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopTimerRef rlt) {   /* DOES CALLOUT */

   //真正处理定时器逻辑的函数调用
    __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(rlt->_callout, rlt, context_info);
    
    return timerHandled;
}
  • 关于 __CFRunLoopDoSources0
static Boolean __CFRunLoopDoSources0(CFRunLoopRef rl, CFRunLoopModeRef rlm, Boolean stopAfterHandle) {  /* DOES CALLOUT */
   
  //真正处理Sources0的函数调用
   __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(rls->_context.version0.perform, rls->_context.version0.info);
    
    return sourceHandled;
}
  • 关于 __CFRunLoopDoBlocks
static Boolean __CFRunLoopDoBlocks(CFRunLoopRef rl, CFRunLoopModeRef rlm) { // Call with rl and rlm locked
   
   //真正处理__CFRunLoopDoBlocks的函数调用
    __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
    
    return did;
}
  • 关于 GCD
//真正处理GCD-->dispatch_queue的函数调用
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);


GCD的执行逻辑一般是不依赖于RunLoop,只有少数情况会交给RunLoop处理;比如:异步子线程回到主线程刷新UI 、dispatch_after();这样的操作才会交给RunLoop处理

        // 回到主线程去刷新UI界面
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"嘿嘿嘿😝");
        });

  • 关于 __CFRunLoopServiceMachPort 线程休眠
static Boolean __CFRunLoopServiceMachPort(mach_port_name_t port, mach_msg_header_t **buffer, size_t buffer_size, mach_port_t *livePort, mach_msg_timeout_t timeout, voucher_mach_msg_state_t *voucherState, voucher_t *voucherCopy) {
  //真正处理__CFRunLoopServiceMachPort的函数调用
ret = mach_msg(msg, MACH_RCV_MSG|(voucherState ? MACH_RCV_VOUCHER : 0)|MACH_RCV_LARGE|((TIMEOUT_INFINITY != timeout) ? MACH_RCV_TIMEOUT : 0)|MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AV), 0, msg->msgh_size, port, timeout, MACH_PORT_NULL);

   return false;
}

__CFRunLoopServiceMachPort: 让当前线程进入休眠状态,不做任何事情;CPU也不需要再给当前线程分配任何资源、代码也就停在该函数的位置不在向下执行,一直到被唤醒后才会继续执行下面的代码,这个阻塞线程是真正的阻塞;不是在那里搞一个死循环,这一点是需要区分开 !

如何做到的呢 ?

在源码中可以看到是调用内核层面的API-->mach_msg()函数实现;
操作系统提供的API按层级换分会有很多种:

  • 应用层面API: 这个层面的API是给开发者编写程序使用的,比如:编写UI界面、发送通知、发网络请求等
  • 内核层面API:这种层面的API通常情况下是不会开放给开发者调用的,这很危险;

    图解:
休眠原理@2x.png

相关文章

网友评论

      本文标题:iOS 底层 - runloop的源码分析

      本文链接:https://www.haomeiwen.com/subject/zitpphtx.html