iOS底層-32:RunLoop原理

RunLoop

RunLoopiOS泉孩、OSX開發(fā)中非惩纾基礎(chǔ)的一個(gè)概念谴忧,這篇文章將會(huì)從源碼的角度分析RunLoop的概念已經(jīng)底層實(shí)現(xiàn)原理朴下。在iOS中努咐,蘋果如何利用RunLoop實(shí)現(xiàn)了自動(dòng)釋放池苦蒿、延遲回調(diào)殴胧、觸摸事件屏幕刷新等功能,我們也將一一探索。

RunLoop的作用
  • 保持程序的運(yùn)行
  • 處理APP的各種事件(觸摸团滥,定時(shí)器竿屹,performSelector)
  • 節(jié)省CPU資源、提高程序的性能灸姊;該做事就做事拱燃,該休息就休息

RunLoop的應(yīng)用非常廣泛,iOS中多種任務(wù)的執(zhí)行都依賴RunLoop力惯,下面是一些RunLoopcall out函數(shù)

blockCFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK
NSTimer__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
SOURCE0__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
SOURCE1__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
GCD主隊(duì)列__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
observer源__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__

官方文檔

RunLoop的官方文檔在Threading Programming Guide中碗誉。

OSX/iOS系統(tǒng)中,提供了兩個(gè)RunLoop對象NSRunLoopCFRunLoopRef

CFRunLoopRef是在CoreFoundation框架內(nèi)的父晶,它提供了純C函數(shù)的API哮缺,所有這些API線程安全
NSRunLoop是基于CFRunLoopRef的封裝,提供了面向?qū)ο蟮?code>API甲喝,但是這些API不是線程安全

CFRunLoopRef的代碼是開源的尝苇,可以在這里下載

源碼分析

  • 搜索CFRunLoopRun
void CFRunLoopRun(void) {    /* DOES CALLOUT */
    int32_t result;
    do {
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
        CHECK_FOR_FORK();
    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}

這就是一個(gè)do-while循環(huán),這里最重要的就是CFRunLoopRunSpecific方法埠胖。當(dāng)RunLoopRunStopped或者RunLoopRunFinished的時(shí)候才會(huì)跳出循環(huán)

  • 查看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;

    if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);//通知Observer糠溜,runloop即將進(jìn)來
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);//這是才是`runloop`執(zhí)行任務(wù)的地方
    if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);//通知Observer,runloop即將退出

        __CFRunLoopModeUnlock(currentMode);
        __CFRunLoopPopPerRunData(rl, previousPerRun);
    rl->_currentMode = previousMode;
    __CFRunLoopUnlock(rl);
    return result;
}
RunLoop和線程的關(guān)系

蘋果不允許直接創(chuàng)建RunLoop直撤,它提供了兩個(gè)獲取的函數(shù)CFRunLoopGetMain()CFRunLoopGetCurrent ()非竿,下面我們看一下這兩個(gè)函數(shù)的實(shí)現(xiàn)。

  • 搜索CFRunLoopGetMain
CFRunLoopRef CFRunLoopGetMain(void) {
    CHECK_FOR_FORK();
    static CFRunLoopRef __main = NULL; // no retain needed
    if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
    return __main;
}
  • 搜索CFRunLoopGetCurrent
CFRunLoopRef CFRunLoopGetCurrent(void) {
    CHECK_FOR_FORK();
    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
    if (rl) return rl;
    return _CFRunLoopGet0(pthread_self());
}

上面兩個(gè)函數(shù)谊惭,都調(diào)用了_CFRunLoopGet0

  • 查看_CFRunLoopGet0
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
    if (pthread_equal(t, kNilPthreadT)) {
        t = pthread_main_thread_np();//線程為空汽馋,設(shè)置為主線程
    }
    __CFLock(&loopsLock);
    if (!__CFRunLoops) {//綁定主線程
        __CFUnlock(&loopsLock);
        
    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
        
        
    CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
        
    // 進(jìn)行綁定 dict[@"pthread_main_thread_np"] = mainLoop
    CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
        
        
    if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
        CFRelease(dict);
    }
    CFRelease(mainLoop);
        __CFLock(&loopsLock);
    }
    //綁定其他線程
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFUnlock(&loopsLock);
    if (!loop) {
    CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        __CFLock(&loopsLock);
    loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    if (!loop) {
        CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
        loop = newLoop;
    }
        // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
        __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;
}

這個(gè)方法主要是創(chuàng)建runloop,并把runloop和對應(yīng)的線程通過key-value的方式保存下來圈盔。

  • 查看__CFRunLoopCreate
static CFRunLoopRef __CFRunLoopCreate(pthread_t t) {
    CFRunLoopRef loop = NULL;
    CFRunLoopModeRef rlm;
    uint32_t size = sizeof(struct __CFRunLoop) - sizeof(CFRuntimeBase);
    //創(chuàng)建一個(gè)CFRunLoopRef
    loop = (CFRunLoopRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, CFRunLoopGetTypeID(), size, NULL);
    
    if (NULL == loop) {
        return NULL;
    }
    //下面是對`RunLoop`的賦值操作
    (void)__CFRunLoopPushPerRunData(loop);
    __CFRunLoopLockInit(&loop->_lock);
    loop->_wakeUpPort = __CFPortAllocate();
    
    if (CFPORT_NULL == loop->_wakeUpPort) HALT;
    
    __CFRunLoopSetIgnoreWakeUps(loop);
    loop->_commonModes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
    CFSetAddValue(loop->_commonModes, kCFRunLoopDefaultMode);
    loop->_commonModeItems = NULL;
    loop->_currentMode = NULL;
    loop->_modes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
    loop->_blocks_head = NULL;
    loop->_blocks_tail = NULL;
    loop->_counterpart = NULL;
    loop->_pthread = t;
#if DEPLOYMENT_TARGET_WINDOWS
    loop->_winthread = GetCurrentThreadId();
#else
    loop->_winthread = 0;
#endif
    rlm = __CFRunLoopFindMode(loop, kCFRunLoopDefaultMode, true);
    if (NULL != rlm) __CFRunLoopModeUnlock(rlm);
    return loop;
}
  • 搜索__CFRunLoop {查看__CFRunLoop結(jié)構(gòu)體
struct __CFRunLoop {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;            /* locked for accessing mode list */
    __CFPort _wakeUpPort;            // used for CFRunLoopWakeUp
    Boolean _unused;
    volatile _per_run_data *_perRunData;              // reset for runs of the run loop
    pthread_t _pthread;
    uint32_t _winthread;
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFAbsoluteTime _runTime;
    CFAbsoluteTime _sleepTime;
    CFTypeRef _counterpart;
};

可以看到里面的Modes豹芯,說明runloop有多個(gè)mode切換,還有一個(gè)_currentMode表示當(dāng)前mode

Mode


在斷點(diǎn)處進(jìn)行LLDB調(diào)試

(lldb) po CFRunLoopCopyCurrentMode(mainRunLoop)
kCFRunLoopDefaultMode

(lldb) po CFRunLoopCopyAllModes(mainRunLoop)
<__NSArrayM 0x6000007a4930>(
UITrackingRunLoopMode,//追蹤ScrollView的滑動(dòng)狀態(tài)
GSEventReceiveRunLoopMode,//處理事件
kCFRunLoopDefaultMode,
kCFRunLoopCommonModes
)
(lldb) 

可以看出mainRunLoop里面有四種Mode驱敲,這也對應(yīng)了上面铁蹈,一個(gè)runLoop可以有多種mode,可以在各種mode之間切換

CFRunLoopModeCFRunLoop大致源碼如下:

struct __CFRunLoopMode {
    ...
    CFStringRef _name;//mode name , 例如@"kCFRunLoopDefaultMode"
    CFMutableSetRef _sources0;//sources0 set
    CFMutableSetRef _sources1;//sources1 set
    CFMutableArrayRef _observers;//_observers  array
    CFMutableArrayRef _timers;//_timers  array
    CFMutableDictionaryRef _portToV1SourceMap;
    ...
};
struct __CFRunLoop {//RunLoop里面存在modes
    ...
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
    ...
};

這里有個(gè)概念叫commonModes众眨,一個(gè)mode可以將自己標(biāo)記為“Common”屬性(通過將其modeName加入到RunLoopcommonModes中)握牧。每當(dāng)RunLoop的內(nèi)容發(fā)生變化時(shí),RunLoop都會(huì)自動(dòng)將_commonModeItems中的Source/Observer/Timer同步到具有“Common”標(biāo)記的所有mode中娩梨。

主線程的RunLoop中有兩個(gè)預(yù)置的mode: kCFRunLoopDefaultModeUITrackingRunLoopMode沿腰,這兩個(gè)都已經(jīng)被標(biāo)記“Common”屬性。DefaultModeAPP平時(shí)所處的狀態(tài)狈定,TrackingRunLoopMode是追蹤ScrollView滑動(dòng)時(shí)的狀態(tài)颂龙。當(dāng)你創(chuàng)建一個(gè)timer加入到DefaultMode习蓬,正常情況下timer可以得到回調(diào),但當(dāng)你滑動(dòng)ScrollView的時(shí)候措嵌,RunLoop會(huì)切換到TrackingRunLoopMode躲叼,這時(shí)的timer就不會(huì)回調(diào),也不會(huì)影響滑動(dòng)操作企巢。

有時(shí)候你需要一個(gè)timer在兩個(gè)mode中都能得到回調(diào)枫慷,一種方法是將timer分別加入這兩個(gè)mode,還有一種方法就是將timer加入到頂層的commonModeItems中浪规,被
RunLoop自動(dòng)更新到具有“Common”mode中去

面試題

NSTimer


結(jié)果是不會(huì)打印或听,子線的runloop默認(rèn)是不啟動(dòng)的,NSTimer不會(huì)執(zhí)行block任務(wù)笋婿。

解決方法:


加上這一句代碼神帅,開啟runLoop即可

不知道大家有沒有這樣的疑問,NSTimer為什么要依賴于runLoop才可以執(zhí)行呢萌抵?


在斷點(diǎn)處打印堆棧信息找御,可以看到我們熟悉的__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__

  • 在源碼中搜索__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(CFRunLoopTimerCallBack func, CFRunLoopTimerRef timer, void *info) {
    if (func) {
        func(timer, info);
    }
    asm __volatile__(""); // thwart tail-call optimization
}

在這里執(zhí)行了Timer的任務(wù)回調(diào)函數(shù),其實(shí)執(zhí)行任務(wù)要從下面的-[NSRunLoop(NSRunLoop) run]開始

  • 搜索__CFRunLoopRun找到一個(gè)很長的方法


    往下看

    這里是處理blockobserver

    這里doTimers绍填,處理timer

  • 查看__CFRunLoopDoTimers

static Boolean __CFRunLoopDoTimers(CFRunLoopRef rl, CFRunLoopModeRef rlm, uint64_t limitTSR) {    /* DOES CALLOUT */
    Boolean timerHandled = false;
    CFMutableArrayRef timers = NULL;
    for (CFIndex idx = 0, cnt = rlm->_timers ? CFArrayGetCount(rlm->_timers) : 0; idx < cnt; idx++) {
        CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(rlm->_timers, idx);
        
        if (__CFIsValid(rlt) && !__CFRunLoopTimerIsFiring(rlt)) {
            if (rlt->_fireTSR <= limitTSR) {
                if (!timers) timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
                CFArrayAppendValue(timers, rlt);
            }
        }
    }
    
    for (CFIndex idx = 0, cnt = timers ? CFArrayGetCount(timers) : 0; idx < cnt; idx++) {
        CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(timers, idx);
        Boolean did = __CFRunLoopDoTimer(rl, rlm, rlt);
        timerHandled = timerHandled || did;
    }
    if (timers) CFRelease(timers);
    return timerHandled;
}

循環(huán)_timers里面的timer霎桅,執(zhí)行__CFRunLoopDoTimer方法

  • 查看__CFRunLoopDoTimer

    往下可以找到我們熟悉的__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__函數(shù)

    最后我們查看一下timer在下層的結(jié)構(gòu)
struct __CFRunLoopTimer {
    CFRuntimeBase _base;
    uint16_t _bits;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop;
    CFMutableSetRef _rlModes;
    CFAbsoluteTime _nextFireDate;
    CFTimeInterval _interval;        /* immutable */
    CFTimeInterval _tolerance;          /* mutable */
    uint64_t _fireTSR;            /* TSR units */
    CFIndex _order;            /* immutable */
    CFRunLoopTimerCallBack _callout;    /* immutable */
    CFRunLoopTimerContext _context;    /* immutable, except invalidation */
};

NSTimer里面有_runLoop_callout,拿到了timer對象就能準(zhǔn)確執(zhí)行

這些方法執(zhí)行了Timer的任務(wù)讨永。那又是哪個(gè)方法將Timer加到了NSRunLoop呢?
我們記得NSRunLoop有一個(gè)addTimer:forMode:的方法滔驶,在源碼中搜索addTimer

  • 搜索addTimer


    從這里可以看出橱夭,Source铣缠、ObserverTimer都要加入RunLoop

  • 查看CFRunLoopAddTimer方法

void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {
    CHECK_FOR_FORK();
    if (__CFRunLoopIsDeallocating(rl)) return;
    if (!__CFIsValid(rlt) || (NULL != rlt->_runLoop && rlt->_runLoop != rl)) return;
    //屏蔽非空

    __CFRunLoopLock(rl);//加鎖
    if (modeName == kCFRunLoopCommonModes) {//當(dāng)是CommonMode
        //_commonModes為空 賦一個(gè)默認(rèn)值
        CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
        //_commonModeItems為空 賦一個(gè)默認(rèn)值
        if (NULL == rl->_commonModeItems) {
            rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
        }
   
     CFSetAddValue(rl->_commonModeItems, rlt);//把timer加入 _commonModeItems
      if (NULL != set) {//set 不為空 加到`CommonModes`中去
        CFTypeRef context[2] = {rl, rlt};
        /* add new item to all common-modes */
        CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
        CFRelease(set);
      }
    } 
    else {
      //根據(jù)name 和runloop匹配 CFRunLoopModeRef
      CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
      if (NULL != rlm) {
            if (NULL == rlm->_timers) {
                CFArrayCallBacks cb = kCFTypeArrayCallBacks;
                cb.equal = NULL;
                rlm->_timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &cb);
            }
    }
    if (NULL != rlm && !CFSetContainsValue(rlt->_rlModes, rlm->_name)) {
            __CFRunLoopTimerLock(rlt);
            if (NULL == rlt->_runLoop) {
            rlt->_runLoop = rl;
          } else if (rl != rlt->_runLoop) {
              __CFRunLoopTimerUnlock(rlt);
              __CFRunLoopModeUnlock(rlm);
              __CFRunLoopUnlock(rl);
              return;
          }
          
            CFSetAddValue(rlt->_rlModes, rlm->_name);
            __CFRunLoopTimerUnlock(rlt);
            __CFRunLoopTimerFireTSRLock();
            __CFRepositionTimerInMode(rlm, rlt, false);//將timer添加到mode中
            __CFRunLoopTimerFireTSRUnlock();
            if (!_CFExecutableLinkedOnOrAfter(CFSystemVersionLion)) {
                // Normally we don't do this on behalf of clients, but for
                // backwards compatibility due to the change in timer handling...
                if (rl != CFRunLoopGetCurrent()) CFRunLoopWakeUp(rl);
            }
    }
        if (NULL != rlm) {
        __CFRunLoopModeUnlock(rlm);
    }
    }
    __CFRunLoopUnlock(rl);
}

根據(jù)modeNametimer加到__CFRunLoopMode_timers中去

  • 搜索__CFRepositionTimerInMode
static void __CFRepositionTimerInMode(CFRunLoopModeRef rlm, CFRunLoopTimerRef rlt, Boolean isInArray) {
    if (!rlt) return;
    
    CFMutableArrayRef timerArray = rlm->_timers;
    if (!timerArray) return;
    Boolean found = false;
    
    // If we know in advance that the timer is not in the array (just being added now) then we can skip this search
    if (isInArray) {
        CFIndex idx = CFArrayGetFirstIndexOfValue(timerArray, CFRangeMake(0, CFArrayGetCount(timerArray)), rlt);
        if (kCFNotFound != idx) {
            CFRetain(rlt);
            CFArrayRemoveValueAtIndex(timerArray, idx);
            found = true;
        }
    }
    if (!found && isInArray) return;
    CFIndex newIdx = __CFRunLoopInsertionIndexInTimerArray(timerArray, rlt);
    CFArrayInsertValueAtIndex(timerArray, newIdx, rlt);//將timer 加到_timers數(shù)組
    __CFArmNextTimerInMode(rlm, rlt->_runLoop);
    if (isInArray) CFRelease(rlt);
}

這個(gè)函數(shù)主要是添加timer

Observer
  • Observer結(jié)構(gòu)體
struct __CFRunLoopObserver {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop;
    CFIndex _rlCount;
    CFOptionFlags _activities;        /* immutable */
    CFIndex _order;            /* immutable */
    CFRunLoopObserverCallBack _callout;    /* immutable */
    CFRunLoopObserverContext _context;    /* immutable, except invalidation */
};
  • CFRunLoopAddObserver
  • __CFRunLoopDoObservers

Timer的原理基本一致屎即。

__CFRunLoopRun詳解
  • CFRunLoopRun
void CFRunLoopRun(void) {    /* DOES CALLOUT */
    int32_t result;
    do {
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);// 科學(xué)計(jì)數(shù)1.0e10
        CHECK_FOR_FORK();
    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}

1.0e10 = 1.0 * 10^{10}
每次runloop循環(huán)锻霎,傳入了一個(gè)循環(huán)時(shí)間

  • __CFRunLoopRun
    在這里面也有一個(gè)計(jì)時(shí)器用來記錄時(shí)間著角,由于NSTimer是依賴于RunLoop的,在這里顯然不適用旋恼。這里使用的是dispatch_source_t
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 { //超時(shí)
        seconds = 9999999999.0;
        timeout_context->termTSR = UINT64_MAX;
    }
  • 將代碼折疊起來查看整體結(jié)構(gòu)

    do-while循環(huán)才是我們研究的重點(diǎn)吏口,
    下面是runloop方法的流程圖和主要代碼。
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    CHECK_FOR_FORK();
    if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
    __CFRunLoopLock(rl);
    
    /// 首先根據(jù)modeName找到對應(yīng)mode
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
    
    /// 通知 Observers: RunLoop 即將進(jìn)入 loop冰更。
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    
    /// 內(nèi)部函數(shù)产徊,進(jìn)入loop
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    
    /// 通知 Observers: RunLoop 即將退出。
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
    
    return result;
}

/// 核心函數(shù)
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    
    int32_t retVal = 0;
    
    do {  // itmes do
        
        /// 通知 Observers: 即將處理timer事件
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        
        /// 通知 Observers: 即將處理Source事件
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources)
        
        /// 處理Blocks
        __CFRunLoopDoBlocks(rl, rlm);
        
        /// 處理sources0
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        
        /// 處理sources0返回為YES
        if (sourceHandledThisLoop) {
            /// 處理Blocks
            __CFRunLoopDoBlocks(rl, rlm);
        }
        
        
        /// 判斷有無端口消息(Source1)
        if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
            /// 處理消息
            goto handle_msg;
        }
        
        /// 通知 Observers: 即將進(jìn)入休眠
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        __CFRunLoopSetSleeping(rl);
        
        /// 等待被喚醒
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
        
        
        // user callouts now OK again
        __CFRunLoopUnsetSleeping(rl);
        
        /// 通知 Observers: 被喚醒蜀细,結(jié)束休眠
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
        
    handle_msg:
        if (被Timer喚醒) {
            /// 處理Timers
            __CFRunLoopDoTimers(rl, rlm, mach_absolute_time())舟铜;
        } else if (被GCD喚醒) {
            /// 處理gcd
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
        } else if (被Source1喚醒) {
            /// 被Source1喚醒,處理Source1
            __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
        }
        
        /// 處理block
        __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);
    
    return retVal;
}

// main  dispatch queue
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__

// __CFRunLoopDoObservers
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__

// __CFRunLoopDoBlocks
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__

// __CFRunLoopDoSources0
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__

// __CFRunLoopDoSource1
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__

// __CFRunLoopDoTimers
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末奠衔,一起剝皮案震驚了整個(gè)濱河市谆刨,隨后出現(xiàn)的幾起案子奕谭,更是在濱河造成了極大的恐慌,老刑警劉巖痴荐,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異官册,居然都是意外死亡生兆,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門膝宁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸦难,“玉大人,你說我怎么就攤上這事员淫『媳危” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵介返,是天一觀的道長拴事。 經(jīng)常有香客問我,道長圣蝎,這世上最難降的妖魔是什么刃宵? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮徘公,結(jié)果婚禮上牲证,老公的妹妹穿的比我還像新娘。我一直安慰自己关面,他們只是感情好坦袍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著等太,像睡著了一般捂齐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上缩抡,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天辛燥,我揣著相機(jī)與錄音,去河邊找鬼缝其。 笑死挎塌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的内边。 我是一名探鬼主播榴都,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼漠其!你這毒婦竟也來了嘴高?” 一聲冷哼從身側(cè)響起竿音,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拴驮,沒想到半個(gè)月后春瞬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡套啤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年宽气,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片潜沦。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡萄涯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出唆鸡,到底是詐尸還是另有隱情涝影,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布争占,位于F島的核電站燃逻,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏臂痕。R本人自食惡果不足惜唆樊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望刻蟹。 院中可真熱鬧逗旁,春花似錦、人聲如沸舆瘪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽英古。三九已至淀衣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間召调,已是汗流浹背膨桥。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留唠叛,地道東北人只嚣。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像艺沼,于是被迫代替她去往敵國和親册舞。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容