RunLoop回顧&&源碼解析

RunLoop是什么

RunLoop是事件接收和分發(fā)機(jī)制的一個實(shí)現(xiàn),是系統(tǒng)中和線程相關(guān)的基礎(chǔ)框架的組成部分;
一個RunLoop就是一個事件循環(huán)(do…while),用來不停的調(diào)度工作及處理輸入事件,提供一個消息機(jī)制處理模式

注:處理的事件大致分為:
source0響應(yīng):CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION
source1響應(yīng):x__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
GCD主隊(duì)列:CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
observe源:CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION
timer:CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION
block:CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK

RunLoop和線程的關(guān)系

通過對CFRunLoopGetMain源碼中

CFMutableDictionaryRef dict =  CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);

這段代碼可以看出,RunLoop和線程的關(guān)系,它們是一一對應(yīng)的,被保存在一個字典中,并且RunLoop的生成是以線程為參數(shù)的,即RunLoop是依賴于線程的.

注:子線程的RunLoop默認(rèn)不開啟

Timer和RunLoop

Timer的運(yùn)行依賴于RunLoop

RunLoop源碼解析

主要研究對象:
CFRunloop
CFRunloopMode
CFRunloopSource
CFRunloopTimer
CFRunloopObserver

CFRunLoopRef lp = CFRunLoopGetCurrent();
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(ref);
NSLog(@“獲取當(dāng)前的RunLoopMode:%@“,mode);

CFArrayRef modeAry = CFRunLoopCopyAllModes(lp);
NSLog(@“獲取當(dāng)前的所有Mode:%@“,modeAry);
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;
};

RunLoop結(jié)構(gòu)體信息如上,其中我們主要關(guān)注的是_pthread、_commonModes、_commonModeItems、_currentMode

struct __CFRunLoopMode {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;    /* must have the run loop locked before locking this */
    CFStringRef _name;
    Boolean _stopped;
    char _padding[3];
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
    CFMutableDictionaryRef _portToV1SourceMap;
    __CFPortSet _portSet;
    CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
    dispatch_source_t _timerSource;
    dispatch_queue_t _queue;
    Boolean _timerFired; // set to true by the source when a timer has fired
    Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
    mach_port_t _timerPort;
    Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
    DWORD _msgQMask;
    void (*_msgPump)(void);
#endif
    uint64_t _timerSoftDeadline; /* TSR */
    uint64_t _timerHardDeadline; /* TSR */
};

RunLoopMode結(jié)構(gòu)體信息如上,其中我們比較關(guān)注的是_source0(CFMutableSetRef)、_source1(CFMutableSetRef)、_observes(CFMutableArrayRef)、_timers(CFMutableArrayRef)

通過這兩個結(jié)構(gòu)體,很好的解釋了下面的RunLoop機(jī)制圖:


RunLoop機(jī)制圖

observer與runLoop

監(jiān)聽RunLoop狀態(tài)

source與RunLoop

struct __CFRunLoopSource {
    CFRuntimeBase _base;
    uint32_t _bits;
    pthread_mutex_t _lock;
    CFIndex _order;            /* immutable */
    CFMutableBagRef _runLoops;
    union {
        CFRunLoopSourceContext version0;    /* immutable, except invalidation */
        CFRunLoopSourceContext1 version1;    /* immutable, except invalidation */
    } _context;
};

結(jié)構(gòu)體信息如上
source0:處理App內(nèi)部事件,如UIEvent
source1:端口通信,用于線程之間通信

然后我們再來看下,Timer如何加到RunLoop的,又是如何被調(diào)用的
static void __CFRunLoopAddItemsToCommonMode(const void *value, void *ctx) {
    CFTypeRef item = (CFTypeRef)value;
    CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]);
    CFStringRef modeName = (CFStringRef)(((CFTypeRef *)ctx)[1]);
    if (CFGetTypeID(item) == CFRunLoopSourceGetTypeID()) {
        CFRunLoopAddSource(rl, (CFRunLoopSourceRef)item, modeName);
    } else if (CFGetTypeID(item) == CFRunLoopObserverGetTypeID()) {
        CFRunLoopAddObserver(rl, (CFRunLoopObserverRef)item, modeName);
    } else if (CFGetTypeID(item) == CFRunLoopTimerGetTypeID()) {
        CFRunLoopAddTimer(rl, (CFRunLoopTimerRef)item, modeName);
    }
}
void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {
    CHECK_FOR_FORK();
    ...
    if (modeName == kCFRunLoopCommonModes) {
        CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
        if (NULL == rl->_commonModeItems) {
            rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
        }
        CFSetAddValue(rl->_commonModeItems, rlt);
        …
    } else {
        …
    }
    __CFRunLoopUnlock(rl);
}

將timer加入_commonModeItems,然后通過下面函數(shù)

CFRunLoopRun-> CFRunLoopRunSpecific->__CFRunLoopRun->  __CFRunLoopDoBlocks

最后找到代碼:

        if (CFStringGetTypeID() == CFGetTypeID(curr->_mode)) {
            // timer 加入的mode 和 我們當(dāng)前mode是否相同
            // 相同事務(wù)就可以執(zhí)行
            doit = CFEqual(curr->_mode, curMode) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
        } else {
            doit = CFSetContainsValue((CFSetRef)curr->_mode, curMode) || (CFSetContainsValue((CFSetRef)curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
        }

再到下面代碼,最終執(zhí)行事務(wù)

        if (doit) {
            if (prev) prev->_next = item;
            if (curr == head) head = item;
            if (curr == tail) tail = prev;
            void (^block)(void) = curr->_block;
            CFRelease(curr->_mode);
            free(curr);
            if (doit) {
                __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
                did = true;
            }
            Block_release(block); // do this before relocking to prevent deadlocks where some yahoo wants to run the run loop reentrantly from their dealloc
        }

生活如此美好,今天就點(diǎn)到為止。。。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • http://www.cocoachina.com/ios/20150601/11970.html RunLoop...
    紫色冰雨閱讀 946評論 0 3
  • RunLoop 是 iOS 和 OS X 開發(fā)中非?;A(chǔ)的一個概念,這篇文章將從 CFRunLoop 的源碼入手,...
    iOS_Alex閱讀 971評論 0 10
  • 轉(zhuǎn)自http://blog.ibireme.com/2015/05/18/runloop 深入理解RunLoop ...
    飄金閱讀 1,065評論 0 4
  • 深入理解RunLoop 由ibireme| 2015-05-18 |iOS,技術(shù) RunLoop 是 iOS 和 ...
    橙娃閱讀 960評論 1 2
  • https://blog.ibireme.com/2015/05/18/runloop/ RunLoop 是 iO...
    SmallDe閱讀 776評論 0 51

友情鏈接更多精彩內(nèi)容