美文网首页iOS Developer
iOSApp启动原理解析(二)Runloop源码解读

iOSApp启动原理解析(二)Runloop源码解读

作者: 找不到工作的iOS | 来源:发表于2017-08-24 18:48 被阅读123次

    Runloop源码地址

    https://opensource.apple.com/source/CF
    选一个比较新的版本,比如1153,找到CFRunloop.c文件
    
    • 上一篇(一)中讲到ApplicationMain函数在app启动的时候会执行死循环,归根结底还是源自runloop,那么我们来看一看底层的runloop源码是怎么实现的吧.^ ^

    • 首先我们需要知道runloop里有三大类型的资源sourecetimerobservers,以及runloop有自己的mode,比如说kCFRunloopDefaultModetrackMode(滑动模式)这里不多做介绍,来看源码吧

    1.找到启动方法void runlooprun(void)

    我们可以看到核心方法是CFRunloopRunSpecific,并且了解到runloop默认开启的是default模式

    runlooprun2.png

    2.CFRunloopRunSpecific方法做了什么

    SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
        CHECK_FOR_FORK();
        if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
        __CFRunLoopLock(rl);
        CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
        if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
            Boolean did = false;
            if (currentMode) __CFRunLoopModeUnlock(currentMode);
            __CFRunLoopUnlock(rl);
            return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
        }
        volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
        CFRunLoopModeRef previousMode = rl->_currentMode;
        rl->_currentMode = currentMode;
        int32_t result = kCFRunLoopRunFinished;
        
    //1.上面这部分是状态的判断,比如runloop是否要结束了。或者runloop的mode是否需要切换
    
    
    
    //2.告诉外面的监听者obsevers,我要进入runloop了
        if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    
    
    //3.result才是核心代码
        result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    
    
    //4.告诉外面的监听者obsevers,我要退出runloop了
        if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
        
        __CFRunLoopModeUnlock(currentMode);
        __CFRunLoopPopPerRunData(rl, previousPerRun);
        rl->_currentMode = previousMode;
        __CFRunLoopUnlock(rl);
        return result;
    }
    
    • 每一个mode都是一个独立的runloop
      比如我们从kCFRunloopDefaultMode切换到trackMode就会走一遍上面源码的if判断

    • observers可以监听的Activity状态
      以下状态是系统暴露出来的提供监听的,实际上Activity状态远不止这几种,比如还可以监听到自动释放池的行为

    typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
        kCFRunLoopEntry = (1UL << 0),
        kCFRunLoopBeforeTimers = (1UL << 1),
        kCFRunLoopBeforeSources = (1UL << 2),
        kCFRunLoopBeforeWaiting = (1UL << 5),
        kCFRunLoopAfterWaiting = (1UL << 6),
        kCFRunLoopExit = (1UL << 7),
        kCFRunLoopAllActivities = 0x0FFFFFFFU
    };
    

    3.CFRunloopRunSpecific的核心方法__CFRunLoopRun

    static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
        uint64_t startTSR = mach_absolute_time();
        
        if (__CFRunLoopIsStopped(rl)) {
            __CFRunLoopUnsetStopped(rl);
            return kCFRunLoopRunStopped;
        } else if (rlm->_stopped) {
            rlm->_stopped = false;
            return kCFRunLoopRunStopped;
        }
     //以上部分依旧是状态的判断,runloop停止了会直接返回一个停止状态
    //-------------------------------------------------------------------
    
    
     /*
      *
      * source 分为 source0、source1
      * 这里的端口port的事件和消息是通过source1来处理的
      *
      */
        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)));
        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;
        if (rlm->_queue) {
            modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue);
            if (!modeQueuePort) {
                CRASH("Unable to get port for run loop mode queue (%d)", -1);
            }
        }
    #endif
     //以上部分为端口port的获取,port一般用于线程之间的通信
    //--------------------------------------------------------------
    
    
    
    
     /*
      *
      * runloop的循环时间设置
      * 可以看到使用了GCD(触及内核)的定时器
      * 设置循环时间接近无限
      * 定时器NSTimer是CFRunloopTimerRef的封装
      * 所以GCD的定时器会更加精确
      */
        dispatch_source_t timeout_timer = NULL;
        struct __timeout_context *timeout_context = (struct __timeout_context *)malloc(sizeof(*timeout_context));
        if (seconds <= 0.0) { // instant timeout
            seconds = 0.0;
            timeout_context->termTSR = 0ULL;
        } else if (seconds <= TIMER_INTERVAL_LIMIT) {
            dispatch_queue_t queue = pthread_main_np() ? __CFDispatchQueueGetGenericMatchingMain() : __CFDispatchQueueGetGenericBackground();
            timeout_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
            dispatch_retain(timeout_timer);
            timeout_context->ds = timeout_timer;
            timeout_context->rl = (CFRunLoopRef)CFRetain(rl);
            timeout_context->termTSR = startTSR + __CFTimeIntervalToTSR(seconds);
            dispatch_set_context(timeout_timer, timeout_context); // source gets ownership of context
            dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout);
            dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel);
            uint64_t ns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) * 1000000000ULL);
            dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at), DISPATCH_TIME_FOREVER, 1000ULL);
            dispatch_resume(timeout_timer);
        } else { // infinite timeout
            seconds = 9999999999.0;
            timeout_context->termTSR = UINT64_MAX;
        }
        
        Boolean didDispatchPortLastTime = true;
        int32_t retVal = 0;
    //以上部分为timer的设置
    //--------------------------------------------------------------
    
    
    
     /*
      *
      * 开始一个do循环,准备开始处理事件
      * 参杂一些预编译的宏
      * 
      *
      * 
      */
        do {
    #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
            voucher_mach_msg_state_t voucherState = VOUCHER_MACH_MSG_STATE_UNCHANGED;
            voucher_t voucherCopy = NULL;
    #endif
            uint8_t msg_buffer[3 * 1024];
    #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
            mach_msg_header_t *msg = NULL;
            mach_port_t livePort = MACH_PORT_NULL;
    #elif DEPLOYMENT_TARGET_WINDOWS
            HANDLE livePort = NULL;
            Boolean windowsMessageReceived = false;
    #endif
            __CFPortSet waitSet = rlm->_portSet;
            
            __CFRunLoopUnsetIgnoreWakeUps(rl);
    
     /*
      *
      * 核心部分
      * 准备处理timer
      * 准备处理source
      * 
      *
      */   
    //准备处理timer
            if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
    //准备处理source
            if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
         
            __CFRunLoopDoBlocks(rl, rlm); //执行   
            
            Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
            if (sourceHandledThisLoop) {
                __CFRunLoopDoBlocks(rl, rlm);
            }
            
            Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
            
            if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
    #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
    
    //开始处理事件了,可以发现gcd和runloop是协同工作的关系
                msg = (mach_msg_header_t *)msg_buffer;
                if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
          
                   //如果gdc的端口有消息来了处理消息,没有的话往下执行,就去休息了
                    goto handle_msg;
                }
    #elif DEPLOYMENT_TARGET_WINDOWS
                if (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) {
                    goto handle_msg;
                }
    #endif
            }
            
    
    //休息
            didDispatchPortLastTime = false;
            
            if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __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.
            
            __CFPortSetInsert(dispatchPort, waitSet);
            
            __CFRunLoopModeUnlock(rlm);
            __CFRunLoopUnlock(rl);
            
            CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();
            
    #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
    #if USE_DISPATCH_SOURCE_FOR_TIMERS
            do {
                if (kCFUseCollectableAllocator) {
                    // objc_clear_stack(0);
                    // <rdar://problem/16393959>
                    memset(msg_buffer, 0, sizeof(msg_buffer));
                }
                msg = (mach_msg_header_t *)msg_buffer;
              
    
    //休息,进入此方法发现休息是与内核在打交道  ,内核进入等待状态
                __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
     
    
    //醒了,跳出循环
    //下面的预处理基本也是 休息与唤醒的处理           
                if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
                    // Drain the internal queue. If one of the callout blocks sets the timerFired flag, break out and service the timer.
                    while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue));
                    if (rlm->_timerFired) {
                        // Leave livePort as the queue port, and service timers below
                        rlm->_timerFired = false;
                        break;
                    } else {
                        if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
                    }
                } else {
                    // Go ahead and leave the inner loop.
                    break;
                }
            } while (1);
    #else
            if (kCFUseCollectableAllocator) {
                // objc_clear_stack(0);
                // <rdar://problem/16393959>
                memset(msg_buffer, 0, sizeof(msg_buffer));
            }
            msg = (mach_msg_header_t *)msg_buffer;
            __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
    #endif
            
            
    #elif DEPLOYMENT_TARGET_WINDOWS
            // Here, use the app-supplied message queue mask. They will set this if they are interested in having this run loop receive windows messages.
            __CFRunLoopWaitForMultipleObjects(waitSet, NULL, poll ? 0 : TIMEOUT_INFINITY, rlm->_msgQMask, &livePort, &windowsMessageReceived);
    #endif
            
            __CFRunLoopLock(rl);
            __CFRunLoopModeLock(rlm);
            
            rl->_sleepTime += (poll ? 0.0 : (CFAbsoluteTimeGetCurrent() - sleepStart));
            
            // Must remove 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. Also, we don't want them left
            // in there if this function returns.
            
            __CFPortSetRemove(dispatchPort, waitSet);
            
            __CFRunLoopSetIgnoreWakeUps(rl);
            
            // user callouts now OK again
            __CFRunLoopUnsetSleeping(rl);
    
    
    //醒来了,通知观察者observers
            if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
            
        handle_msg:;//跳转到这处理消息
            __CFRunLoopSetIgnoreWakeUps(rl);
    
    
    
    //开始处理消息 
    #if DEPLOYMENT_TARGET_WINDOWS
            if (windowsMessageReceived) {
                // These Win32 APIs cause a callout, so make sure we're unlocked first and relocked after
                __CFRunLoopModeUnlock(rlm);
                __CFRunLoopUnlock(rl);
                
                if (rlm->_msgPump) {
                    rlm->_msgPump();
                } else {
                    MSG msg;
    
    
    //获取消息、转译、处理,类似window
                    if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD)) {  
                        TranslateMessage(&msg);
                        DispatchMessage(&msg);
                    }
                }
                
                __CFRunLoopLock(rl);
                __CFRunLoopModeLock(rlm);
                sourceHandledThisLoop = true;
                
                // To prevent starvation of sources other than the message queue, we check again to see if any other sources need to be serviced
                // Use 0 for the mask so windows messages are ignored this time. Also use 0 for the timeout, because we're just checking to see if the things are signalled right now -- we will wait on them again later.
                // NOTE: Ignore the dispatch source (it's not in the wait set anymore) and also don't run the observers here since we are polling.
                __CFRunLoopSetSleeping(rl);
                __CFRunLoopModeUnlock(rlm);
                __CFRunLoopUnlock(rl);
                
                __CFRunLoopWaitForMultipleObjects(waitSet, NULL, 0, 0, &livePort, NULL);
                
                __CFRunLoopLock(rl);
                __CFRunLoopModeLock(rlm);
                __CFRunLoopUnsetSleeping(rl);
                // If we have a new live port then it will be handled below as normal
            }
            
            
    
    //识别端口的类型,因为分主动唤醒、被动唤醒、定时器唤醒的等状态,也就是识别处理不同类型的消息
    #endif
            if (MACH_PORT_NULL == livePort) {
                CFRUNLOOP_WAKEUP_FOR_NOTHING();
                // handle nothing
            } else if (livePort == rl->_wakeUpPort) {
                CFRUNLOOP_WAKEUP_FOR_WAKEUP();
                // do nothing on Mac OS
    #if DEPLOYMENT_TARGET_WINDOWS
                // Always reset the wake up port, or risk spinning forever
                ResetEvent(rl->_wakeUpPort);
    #endif
            }
    #if USE_DISPATCH_SOURCE_FOR_TIMERS
            else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
                CFRUNLOOP_WAKEUP_FOR_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
            else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
                CFRUNLOOP_WAKEUP_FOR_TIMER();
                // On Windows, we have observed an issue where the timer port is set before the time which we requested it to be set. For example, we set the fire time to be TSR 167646765860, but it is actually observed firing at TSR 167646764145, which is 1715 ticks early. The result is that, when __CFRunLoopDoTimers checks to see if any of the run loop timers should be firing, it appears to be 'too early' for the next timer, and no timers are handled.
                // In this case, the timer port has been automatically reset (since it was returned from MsgWaitForMultipleObjectsEx), and if we do not re-arm it, then no timers will ever be serviced again unless something adjusts the timer list (e.g. adding or removing timers). The fix for the issue is to reset the timer here if CFRunLoopDoTimers did not handle a timer itself. 9308754
                if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                    // Re-arm the next timer
                    __CFArmNextTimerInMode(rlm, rl);
                }
            }
    #endif
            else if (livePort == dispatchPort) {
                CFRUNLOOP_WAKEUP_FOR_DISPATCH();
                __CFRunLoopModeUnlock(rlm);
                __CFRunLoopUnlock(rl);
                _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL);
    #if DEPLOYMENT_TARGET_WINDOWS
                void *msg = 0;
    #endif
                __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
                _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL);
                __CFRunLoopLock(rl);
                __CFRunLoopModeLock(rlm);
                sourceHandledThisLoop = true;
                didDispatchPortLastTime = true;
            } else {
                CFRUNLOOP_WAKEUP_FOR_SOURCE();
                
                // If we received a voucher from this mach_msg, then put a copy of the new voucher into TSD. CFMachPortBoost will look in the TSD for the voucher. By using the value in the TSD we tie the CFMachPortBoost to this received mach_msg explicitly without a chance for anything in between the two pieces of code to set the voucher again.
                voucher_t previousVoucher = _CFSetTSD(__CFTSDKeyMachMessageHasVoucher, (void *)voucherCopy, os_release);
                
                // Despite the name, this works for windows handles as well
                CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
                if (rls) {
    #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
                    mach_msg_header_t *reply = NULL;
    
    
    //事件、port 都是source1来处理
                    sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
                    if (NULL != reply) {
                        (void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
                        CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
                    }
    #elif DEPLOYMENT_TARGET_WINDOWS
                    sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls) || sourceHandledThisLoop;
    #endif
                }
                
                // Restore the previous voucher
                _CFSetTSD(__CFTSDKeyMachMessageHasVoucher, previousVoucher, os_release);
                
            }
    #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
            if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
    #endif
            
            __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;
            }
            
    #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
            voucher_mach_msg_revert(voucherState);
            os_release(voucherCopy);
    #endif
            
        } while (0 == retVal);
        
        if (timeout_timer) {
            dispatch_source_cancel(timeout_timer);
            dispatch_release(timeout_timer);
        } else {
            free(timeout_context);
        }
        
        return retVal;
    }
    

    Runloop与线程的关系

    • 一个线程有对应的runloop(一一对应)
    • 主线成默认创建一个runloop
    • 如果主动开启一个线程,是不会创建runloop
      当你需要runloop的时候才会懒加载创建 [NSRunloop currentRunloop]
    • 各种runloop都放在一个静态字典里,和线程一一对应,
      跟着下面代码的注释
    • 当我们线程销毁的时候runloop就会自动销毁
    void _CFRunLoopSetCurrent(CFRunLoopRef rl) {
        if (pthread_main_np()) return;
    
        //这里,我们再找get这个函数(获取当前runloop的方法)
        CFRunLoopRef currentLoop = CFRunLoopGetCurrent();
    
    
        if (rl != currentLoop) {
            CFRetain(currentLoop); // avoid a deallocation of the currentLoop inside the lock
            __CFLock(&loopsLock);
            if (rl) {
                CFDictionarySetValue(__CFRunLoops, pthreadPointer(pthread_self()), rl);
            } else {
                CFDictionaryRemoveValue(__CFRunLoops, pthreadPointer(pthread_self()));
            }
            __CFUnlock(&loopsLock);
            CFRelease(currentLoop);
            _CFSetTSD(__CFTSDKeyRunLoop, NULL, NULL);
            _CFSetTSD(__CFTSDKeyRunLoopCntr, 0, (void (*)(void *))__CFFinalizeRunLoop);
        }
    }
    
    • _CFRunLoopSetCurrent找到CFRunLoopGetCurrent(获取当前runloop的方法)
    CFRunLoopRef CFRunLoopGetCurrent(void) {
        CHECK_FOR_FORK();
        CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
        if (rl) return rl;
        return _CFRunLoopGet0(pthread_self()); //我们再看这个方法(获取当成线程)
    }
    
    • 跳转到_CFRunLoopGet0(pthread_self()) (获取当前线程的方法)
    CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
    
    //如果是nil线程就赋值到主线程上
        if (pthread_equal(t, kNilPthreadT)) {
            t = pthread_main_thread_np();
        }
        __CFLock(&loopsLock);
        if (!__CFRunLoops) {
            __CFUnlock(&loopsLock);
            CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
            CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
            CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
            if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
                CFRelease(dict);
            }
            CFRelease(mainLoop);
            __CFLock(&loopsLock);
        }
    
    
    //获取线程上的runloop
        CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
        __CFUnlock(&loopsLock);
    
    //如果线程上的runloop不存在
        if (!loop) {
    
          //创建一个runloop
            CFRunLoopRef newLoop = __CFRunLoopCreate(t);
            __CFLock(&loopsLock);
    
          //从全局的一个保存runloop的字典里获取这个runloop
            loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    
          //如果这个runloop没有保存在字典里,就保存这个runloop到这个全局字典
          //因为我们创建一个子现场是不会自动创建runloop,
          //所以在创建了runloop需要在这个静态的字典里创建runloop与该线程的对应与依赖关系
            if (!loop) {
                CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
                loop = newLoop;
            }
            
            __CFUnlock(&loopsLock);
            CFRelease(newLoop);
        }
        if (pthread_equal(t, pthread_self())) {
            _CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
            if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
                _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
            }
        }
        return loop;
    }
    

    Runloop补充

    • 请勿在a线程操作b线程的runloop
    • runloop一次循环是从开始状态到结束,可以通过observers监听
    • 像touch事件,selector等是source0 处理的
    • 一般系统的是由source1处理
    • 如果接收到sel事件,runloop在循环source1,那就等到这次循环结束,到source0的时候处理sel
    while(1){
    
        处理source0 ;
        处理source1;
           wait();
    }
    

    相关文章

      网友评论

        本文标题:iOSApp启动原理解析(二)Runloop源码解读

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