iOS多線程——你要知道的RunLoop都在這里

你要知道的iOS多線程N(yùn)SThread、GCD、NSOperation、RunLoop都在這里

轉(zhuǎn)載請(qǐng)注明出處 http://m.itdecent.cn/p/cfe5132e975f

本系列文章主要講解iOS中多線程的使用,包括:NSThread、GCD、NSOperation以及RunLoop的使用方法詳解,本系列文章不涉及基礎(chǔ)的線程/進(jìn)程、同步/異步、阻塞/非阻塞、串行/并行,這些基礎(chǔ)概念,有不明白的讀者還請(qǐng)自行查閱。本系列文章將分以下幾篇文章進(jìn)行講解,讀者可按需查閱。

RunLoop 基本概念

前面幾篇文章詳細(xì)講解了創(chuàng)建多線程的方法和多線程編程的相關(guān)知識(shí),當(dāng)我們使用NSThread進(jìn)行多線程編程時(shí),只要任務(wù)結(jié)束,線程也就退出了,每次執(zhí)行一個(gè)任務(wù)都需要?jiǎng)?chuàng)建一個(gè)線程非常浪費(fèi)資源,所以需要一種能夠使線程常駐內(nèi)存不退出d,當(dāng)有任務(wù)來(lái)臨時(shí)能隨時(shí)執(zhí)行的方法,這就是RunLoop的作用。類似于javascriptEvent Loop模型,大致類似于如下代碼:

int retVal = Running;
do {
     // 執(zhí)行各種任務(wù),處理各種事件
     // ......
} while (retVal != Stop && retVal != Timeout);

上述循環(huán)只有在特定條件才才會(huì)退出,否則就會(huì)一直在循環(huán)中處理各種任務(wù)或事件,諸如觸摸屏幕事件、手勢(shì)事件、定時(shí)器事件、用戶提交的任務(wù)、各種方法的執(zhí)行等。

RunLoop與線程關(guān)聯(lián)的,是一種事件處理環(huán),用來(lái)安排和協(xié)調(diào)到來(lái)的事件,目的就是讓其關(guān)聯(lián)的線程在有事件到達(dá)時(shí)時(shí)刻保持運(yùn)行狀態(tài),而當(dāng)沒(méi)有事件需要處理時(shí)進(jìn)入睡眠狀態(tài)從而節(jié)約資源,每一個(gè)線程都可以有一個(gè)RunLoop對(duì)象與之對(duì)應(yīng),并且是在第一次獲取它是系統(tǒng)自動(dòng)創(chuàng)建的,比如主線程關(guān)聯(lián)的RunLoop,我們都知道程序的入口函數(shù)是main函數(shù),下面是創(chuàng)建工程后Xcode自動(dòng)生成的main.m文件的main函數(shù)代碼:

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

該方法執(zhí)行體被autoreleasepool包圍,所以程序可以使用ARC來(lái)管理內(nèi)存,后面會(huì)講解RunLoopautoreleasepool的關(guān)系,main函數(shù)直接返回了UIApplicationMain函數(shù),該函數(shù)內(nèi)部就會(huì)第一次獲取RunLoop對(duì)象,所以系統(tǒng)就會(huì)創(chuàng)建這樣一個(gè)RunLoop對(duì)象,因此在沒(méi)有滿足特定條件的時(shí)候該主線程不會(huì)退出,應(yīng)用就可以持續(xù)運(yùn)行而不會(huì)退出。

在官方文檔中使用下圖描述RunLoop模型:

官方RunLoop模型圖

從上圖可以看出一個(gè)線程會(huì)關(guān)聯(lián)一個(gè)RunLoop對(duì)象,RunLoop對(duì)象會(huì)一直循環(huán),直到超時(shí)或收到退出指令。在無(wú)限循環(huán)的過(guò)程中會(huì)一直處理到來(lái)的事件,右側(cè)將事件分為了兩類,一類是Input sources這部分包括基于端口的source1事件,開(kāi)發(fā)者提交的各種source0事件,調(diào)用performSelector:onThread:方法事件,還有一類Timer sources這個(gè)就是常用的定時(shí)器事件,這些事件在程序運(yùn)行期間會(huì)不斷產(chǎn)生之后會(huì)由RunLoop對(duì)象檢測(cè)并負(fù)責(zé)處理相關(guān)事件。

RunLoop 源碼解析

RunLoop有兩個(gè)對(duì)象,NSRunLoopCFRunLoopRef,區(qū)別在于由Core Foundation框架提供的CFRunLoopRef是純C語(yǔ)言編寫(xiě)的,提供的也是C語(yǔ)言接口,這些接口都是線程安全的,由Foundation框架提供的NSRunLoop是面向?qū)ο蟮模腔?code>CFRunLoopRef的封裝,提供的都是面向?qū)ο蟮慕涌?,但這些接口不是線程安全的,Core Foudation框架是開(kāi)源的,可以在這個(gè)地址下載:Core Foundation開(kāi)源代碼,本文接下來(lái)的內(nèi)容主要是針對(duì)該開(kāi)源代碼進(jìn)行講解。

首先,看一下在代碼中如何獲取RunLoop對(duì)象,在Foundation框架中的NSRunLoop類提供了如下兩個(gè)類屬性:

//獲取當(dāng)前線程關(guān)聯(lián)的RunLoop對(duì)象
@property (class, readonly, strong) NSRunLoop *currentRunLoop;
//獲取主線程關(guān)聯(lián)的RunLoop對(duì)象
@property (class, readonly, strong) NSRunLoop *mainRunLoop

對(duì)應(yīng)的Core Foundation框架中提供了如下兩個(gè)函數(shù)來(lái)獲取RunLoop對(duì)象:

//獲得當(dāng)前線程關(guān)聯(lián)的RunLoop對(duì)象
CFRunLoopGetCurrent(); 
// 獲得主線程關(guān)聯(lián)的RunLoop對(duì)象
CFRunLoopGetMain();

前面一直講每一個(gè)線程都會(huì)關(guān)聯(lián)一個(gè)RunLoop對(duì)象,并且不能通過(guò)手動(dòng)創(chuàng)建該對(duì)象,只能在第一次獲取時(shí)系統(tǒng)自動(dòng)創(chuàng)建,看一下Core Foundation框架是如何實(shí)現(xiàn)的:

//CFRunLoopGetMain函數(shù)用于獲取主線程關(guān)聯(lián)的RunLoop對(duì)象
CFRunLoopRef CFRunLoopGetMain(void) {
    CHECK_FOR_FORK();
    //靜態(tài)變量保存主線程關(guān)聯(lián)的RunLoop對(duì)象
    static CFRunLoopRef __main = NULL; // no retain needed
    //如果主線程關(guān)聯(lián)的RunLoop對(duì)象為NULL就調(diào)用_CFRunLoopGet0函數(shù)獲取一個(gè)
    if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
    return __main
}

//獲取當(dāng)前線程關(guān)聯(lián)的RunLoop對(duì)象
CFRunLoopRef CFRunLoopGetCurrent(void) {
    CHECK_FOR_FORK();
    //這一段沒(méi)找到對(duì)應(yīng)的函數(shù)...猜測(cè)是和上面的函數(shù)用意一樣
    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
    if (rl) return rl;
    //如果上面沒(méi)找到就調(diào)用_CFRunLoopGet0函數(shù)去獲取一個(gè)
    return _CFRunLoopGet0(pthread_self());
}


//全局的可變字典數(shù)據(jù)結(jié)構(gòu),key為thread_t即線程,value為RunLoop對(duì)象
static CFMutableDictionaryRef __CFRunLoops = NULL;
//全局的一個(gè)鎖
static CFLock_t loopsLock = CFLockInit;

//_CFRunLoopGet0接收一個(gè)pthread_t對(duì)象,即線程對(duì)象,返回一個(gè)與之關(guān)聯(lián)的RunLoop對(duì)象
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
    //判斷是否為主線程
    if (pthread_equal(t, kNilPthreadT)) {
    //pthread_main_thread_np()函數(shù)用來(lái)獲取主線程
    t = pthread_main_thread_np();
    }
    //加鎖,防止產(chǎn)生競(jìng)爭(zhēng)創(chuàng)建多個(gè)RunLoop對(duì)象
    __CFLock(&loopsLock);
    //如果全局的保存線程和runloop對(duì)象的字典為空
    if (!__CFRunLoops) {
        __CFUnlock(&loopsLock);
    //創(chuàng)建一個(gè)字典
    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
    /*
    根據(jù)主線程創(chuàng)建RunLoop對(duì)象
    所以,當(dāng)?shù)谝淮潍@取RunLoop對(duì)象時(shí)就會(huì)自動(dòng)創(chuàng)建主線程關(guān)聯(lián)的RunLoop對(duì)象
    */
    CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
    //設(shè)置全局的字典,key為主線程,value為主線程關(guān)聯(lián)的RunLoop對(duì)象
    CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
    if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
        CFRelease(dict);
    }
    CFRelease(mainLoop);
        __CFLock(&loopsLock);
    }
    //通過(guò)線程在字典中獲取RunLoop對(duì)象
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFUnlock(&loopsLock);
    //如果沒(méi)有獲取到
    if (!loop) {
    //沒(méi)有獲取到就根據(jù)線程創(chuàng)建一個(gè)RunLoop對(duì)象
    CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        __CFLock(&loopsLock);
    //再次獲取
    loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    if (!loop) {
        //字典中仍然沒(méi)有線程關(guān)聯(lián)的RunLoop對(duì)象就將剛才新創(chuàng)建加入到字典照中
        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)) {
            //設(shè)置銷毀時(shí)的回調(diào)
            _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
        }
    }
    //返回線程關(guān)聯(lián)的RunLoop對(duì)象
    return loop;
}

/*
真正的用于創(chuàng)建RunLoop對(duì)象的靜態(tài)函數(shù),形參為線程對(duì)象
該函數(shù)主要用于分配存儲(chǔ)空間,并進(jìn)行RunLoop對(duì)象相關(guān)初始化操作
*/
static CFRunLoopRef __CFRunLoopCreate(pthread_t t) {
    CFRunLoopRef loop = NULL;
    CFRunLoopModeRef rlm;
    uint32_t size = sizeof(struct __CFRunLoop) - sizeof(CFRuntimeBase);
    loop = (CFRunLoopRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, CFRunLoopGetTypeID(), size, NULL);
    if (NULL == loop) {
    return NULL;
    }
    (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;
}

通過(guò)上面源碼不難發(fā)現(xiàn),RunLoop對(duì)象保存在一個(gè)全局的字典中,該字典以線程對(duì)象pthread_tkey,以RunLoop對(duì)象為value,并且,在第一次獲取RunLoop對(duì)象時(shí)總會(huì)先把主線程關(guān)聯(lián)的RunLoop對(duì)象創(chuàng)建好,在獲取其他線程關(guān)聯(lián)的RunLoop對(duì)象時(shí)都從這個(gè)全局的字典中獲取,如果沒(méi)有獲取到就創(chuàng)建一個(gè)并且添加進(jìn)字典中,所以每一個(gè)線程有且僅有一個(gè)與之關(guān)聯(lián)的RunLoop對(duì)象,重要的是,如果不獲取線程關(guān)聯(lián)的RunLoop對(duì)象,那么這個(gè)RunLoop對(duì)象就不會(huì)被創(chuàng)建。當(dāng)線程退出時(shí),也會(huì)將RunLoop對(duì)象銷毀。

接下來(lái)查看一下CFRunLoopRef具體的數(shù)據(jù)結(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;
};

typedef struct __CFRunLoop * CFRunLoopRef;

上述數(shù)據(jù)結(jié)構(gòu)中比較重要的就是_commonModes_commonModeItems、_currentMode以及_modes,具體關(guān)系如下圖所示,該圖取自文章深入理解RunLoop https://blog.ibireme.com/2015/05/18/runloop/下面講的內(nèi)容也有參考該博客中的內(nèi)容,建議讀者閱讀原文:

RunLoop數(shù)據(jù)結(jié)構(gòu)

上圖很好的描述了struct __CFRunLoop數(shù)據(jù)結(jié)構(gòu)相關(guān)成員變量的關(guān)系,每一個(gè)__CFRunLoop對(duì)象可以包含數(shù)個(gè)不同的Mode,而每一個(gè)Mode又包含了數(shù)個(gè)SourceObserverTimer,當(dāng)一個(gè)RunLoop運(yùn)行時(shí)只能選擇其中的某一個(gè)Mode來(lái)執(zhí)行,如果要切換Mode則需要退出運(yùn)行后指定一個(gè)新的Mode后重新執(zhí)行運(yùn)行。通過(guò)這樣的方式,可以在不同Mode中設(shè)置不同的Source/Observer/Timer而不同的Mode中間的這三部分互不影響,也就是說(shuō),有些Source/Observer/Timer只能在某一個(gè)Mode中運(yùn)行,當(dāng)RunLoop運(yùn)行在其他Mode中,該事件得不到處理。

Source CFRunLoopSourceRef

SourceCFRunLoopSourceRef類的對(duì)象,指代事件源,即前文官方結(jié)構(gòu)圖中的Input Source,在官方文檔中該事件源Source分為三類:

  • Port-Based Sources 基于端口的,也稱為source1事件,通過(guò)內(nèi)核和其他線程通信,接收到事件后包裝為source0事件后分發(fā)給其他線程處理。

  • Custom Input Sources 用戶自定義

  • Cocoa Perform Selector Sources 調(diào)用諸如perfromSelector:onThread:這樣的方法產(chǎn)生的事件

按照調(diào)用棧來(lái)說(shuō)其實(shí)只分成兩類,Source0不基于端口的和Source1基于端口的,分類方式并不是很重要,了解即可。

Timer CFRunLoopTimerRef

Timer可以理解為定時(shí)器即NSTimer,因?yàn)?code>CFRunLoopTimerRef和NSTimertoll-free bridged,所以可以互相轉(zhuǎn)換,將其理解為NSTimer即可,RunLoop對(duì)象會(huì)在注冊(cè)的定時(shí)器時(shí)間到達(dá)時(shí)喚醒關(guān)聯(lián)的線程對(duì)象來(lái)執(zhí)行定時(shí)器的回調(diào)。

Observer CFRunLoopObserverRef

Observer就是監(jiān)聽(tīng)器,用來(lái)監(jiān)聽(tīng)RunLoop的各種狀態(tài),在源碼中有如下監(jiān)聽(tīng)狀態(tài)的枚舉定義:

/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    //即將進(jìn)入RunLoop的執(zhí)行循環(huán)
    kCFRunLoopEntry = (1UL << 0),
    //即將處理Timer事件
    kCFRunLoopBeforeTimers = (1UL << 1),
    //即將處理Source事件
    kCFRunLoopBeforeSources = (1UL << 2),
    //RunLoop即將進(jìn)入休眠狀態(tài)
    kCFRunLoopBeforeWaiting = (1UL << 5),
    //RunLoop即將被喚醒
    kCFRunLoopAfterWaiting = (1UL << 6),
    //RunLoop即將退出
    kCFRunLoopExit = (1UL << 7),
    //監(jiān)聽(tīng)RunLoop的全部狀態(tài)
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

Observer中定義了一系列的監(jiān)聽(tīng)器,開(kāi)發(fā)者也可以使用監(jiān)聽(tīng)器來(lái)監(jiān)聽(tīng)具體的狀態(tài)改變,具體栗子后文會(huì)介紹。

Mode CFRunLoopModeRef

ModeRunLoop中比較重要的部分,系統(tǒng)默認(rèn)為我們提供了五種Mode:

  • kCFRunLoopDefaultMode 即 NSDefaultRunLoopMode,默認(rèn)運(yùn)行模式

  • UITrackingRunLoopMode 跟蹤UIScrollView滑動(dòng)時(shí)使用的運(yùn)行模式,保證滑動(dòng)時(shí)不受其他事件處理的影響,保證絲滑

  • UIInitializationRunLoopMode 啟動(dòng)應(yīng)用時(shí)的運(yùn)行模式,應(yīng)用啟動(dòng)完成后就不會(huì)再使用

  • GSEventReceiveRunLoopMode 事件接收運(yùn)行模式

  • kCFRunLoopCommonModes 即 NSRunLoopCommonModes 是一種標(biāo)記的模式,還需要上述四種模式的支持

UITrackingRunLoopMode只有當(dāng)用戶滑動(dòng)屏幕時(shí),即滑動(dòng)UIScrollView時(shí)才會(huì)執(zhí)行的模式,此時(shí),不在該模式內(nèi)的Source/Timer/Observer都不會(huì)得到執(zhí)行,它僅僅專注于滑動(dòng)時(shí)產(chǎn)生的各種事件,通過(guò)這樣的方式就可以保證用戶在滑動(dòng)頁(yè)面時(shí)的流暢性,這也是分不同Mode的優(yōu)點(diǎn)。

具體數(shù)據(jù)結(jié)構(gòu)如下:

typedef struct __CFRunLoopMode *CFRunLoopModeRef;

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 */
};

從上述數(shù)據(jù)結(jié)構(gòu)中可以看出,Mode內(nèi)部管理了一個(gè)_source0的事件集合,一個(gè)_source1的事件集合,一個(gè)_observers的數(shù)組以及_timers的數(shù)組,這也印證了前文中關(guān)于Mode的圖例,再結(jié)合之前講的__CFRunLoop中比較重要的幾個(gè)成員變量:

struct __CFRunLoop {
    ...
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
    ...
}

其中_currentMode即代表當(dāng)前RunLoop對(duì)象正在執(zhí)行的ModeCFRunLoopModeRef類的對(duì)象。

_mode是一個(gè)Set集合保存了所有該RunLoop對(duì)象可以執(zhí)行的Mode。

_commonModes保存的是具有Common屬性的Mode的名稱,前文__CFRunLoopMode的結(jié)構(gòu)體定義中可以看到,每個(gè)Mode管理自己的Source/Timer/Observer,而被標(biāo)記為Common屬性的Mode還有一個(gè)特性就是當(dāng)RunLoop對(duì)象在執(zhí)Common屬性的Mode時(shí),會(huì)自動(dòng)將_commonModeItems中保存的Source/Observer/Timer同步添加該Mode中,標(biāo)識(shí)Common屬性只需要將__CFRunLoopModeRef_name成員變量的值添加進(jìn)_commonModes集合中即可。被標(biāo)記為Common屬性的Mode就是前文講的kCFRunLoopCommonModes模式,可以看出這種模式不是一種真正的模式,僅僅是標(biāo)識(shí)其他模式是否需要同步添加_commonModeItems中的Source/Timer/Observer。

_commonModeItems中保存的就是那些需要同步添加到具有Common屬性的Mode中的Source/Timer/Observer集合。

系統(tǒng)默認(rèn)將kCFRunLoopDefaultModeUITrackingRunLoopMode添加到了_commonModes中,即標(biāo)識(shí)為Common屬性,所以當(dāng)RunLoop運(yùn)行在這兩種模式中會(huì)自動(dòng)同步添加_commonModeItems中的Source/Timer/Observer。

舉個(gè)常見(jiàn)的栗子:

- (void) viewWillAppear:(BOOL)animate
{
    [super viewWilAppear:YES];
    //創(chuàng)建一個(gè)NSTimer的對(duì)象,從當(dāng)前時(shí)間開(kāi)始每1s輸出一次Hello,World
    NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate date] interval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
        NSLog(@"Hello, World");
    }];
    //將timer加入到當(dāng)前線程關(guān)聯(lián)的RunLoop對(duì)象的NSDefaultRunLoopMode中
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    
    //類方法,創(chuàng)建一個(gè)timer并添加到當(dāng)前線程關(guān)聯(lián)的RunLoop的NSDefaultRunLoopMode中
    [NSTimer scheduledTimerWithTimeInterval:2 repeats:YES block:^(NSTimer * _Nonnull timer) {
        NSLog(@"Hello, World222");
    }];
}

上面的栗子創(chuàng)建了兩個(gè)NSTimer,這兩個(gè)定時(shí)器執(zhí)行效果相同,但如果頁(yè)面中有一個(gè)UIScrollView或其子類的對(duì)象在滑動(dòng)時(shí),NSTimer就不會(huì)再有任何輸出,當(dāng)停下滑動(dòng)時(shí)又會(huì)有輸出,因?yàn)樯鲜龃a創(chuàng)建的兩個(gè)NSTimer都加入到了RunLoop對(duì)象的NSDefaultRunLoopMode中,在滑動(dòng)時(shí)RunLoop會(huì)切換到UITrackingRunLoopMode模式下執(zhí)行,而UITrackingRunLoopMode中沒(méi)有上述定時(shí)器,所以不會(huì)執(zhí)行,當(dāng)停止滑動(dòng)時(shí)RunLoop對(duì)象又切換到了NSDefaultRunLoopMode模式,所以可以繼續(xù)執(zhí)行定時(shí)器的回調(diào)。

為了解決這個(gè)問(wèn)題,可以將NSTimer即加入到NSDefaultRunLoopMode中,又加入到UITrackingRunLoopMode中,同一個(gè)Source/Timer/Observer可以添加到不同的Mode中,但同一個(gè)Source/Timer/Observer不能添加到同一個(gè)Mode中,這樣不會(huì)有任何效果,但添加到兩個(gè)Mode中并不是最好的解決方案,還有一個(gè)方案就是利用前面的Common屬性,NSDefaultRunLoopModeUITrackingRunLoopMode都被添加進(jìn)了_commonModes集合中被標(biāo)識(shí)了具有Common屬性,所以在運(yùn)行時(shí)就會(huì)自動(dòng)將_commonModeItems中的Source/Timer/Observer同步添加到其中,因此,只需要將創(chuàng)建的NSTimer加入到_commonModeItems中即可,此時(shí)只需要使用NSRunLoopCommonModes即可,代碼如下:

- (void) viewWillAppear:(BOOL)animate
{
    [super viewWilAppear:YES];
    NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate date] interval:2 repeats:YES block:^(NSTimer * _Nonnull timer) {
        NSLog(@"Hello, World");
    }];
    
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

NSTimer加入到NSRunLoopCommonModes中就是把其加入到_commonModeItems集合中,這樣在滑動(dòng)時(shí)就會(huì)自動(dòng)同步添加NSTimerUITrackingRunLoopMode模式下,所以定時(shí)器也可會(huì)得到執(zhí)行。如果需要注意使用類方法scheduledTimerWithTimeInterval:repeats:block時(shí)要注意該方法默認(rèn)是加入到NSDefaultRunLoopMode模式中的。

通過(guò)上述講解,可以發(fā)現(xiàn),NSTimer其實(shí)是不那么精確的,首先,在使用時(shí)需要加入到RunLoop中,如果加在CommonMode在普通情況或滑動(dòng)時(shí)都可以執(zhí)行回調(diào)方法,這個(gè)時(shí)候的誤差就來(lái)自于RunLoop一次循環(huán)的執(zhí)行延遲,最壞情況下,RunLoop一次循環(huán)需要執(zhí)行的任務(wù)較多,NSTimer回調(diào)執(zhí)行的延遲就會(huì)加大。如果加在其他模式下,當(dāng)模式切換時(shí)就不會(huì)再執(zhí)行NSTimer的回調(diào)方法了,所以,在使用時(shí)需要根據(jù)情況選擇不同的定時(shí)器以滿足項(xiàng)目需求。

在查看RunLoop運(yùn)行機(jī)制前,做一個(gè)小實(shí)驗(yàn),創(chuàng)建一個(gè)視圖控制器,并添加一個(gè)按鈕,在按鈕點(diǎn)擊事件的回調(diào)函數(shù)中打一個(gè)斷點(diǎn),然后運(yùn)行程序點(diǎn)擊按鈕,之后查看調(diào)用棧如下圖所示:

按鈕點(diǎn)擊的調(diào)用棧

從上圖中可以看到程序在18處執(zhí)行main函數(shù),17執(zhí)行UIApplicationMain函數(shù),這就是程序啟動(dòng)過(guò)程,16是系統(tǒng)內(nèi)部事件,15調(diào)用CFRunLoopRunSpecific后文會(huì)詳細(xì)講解該函數(shù),14開(kāi)始執(zhí)行RunLoop進(jìn)入循環(huán),13開(kāi)始處理source0這個(gè)source0就是點(diǎn)擊按鈕的事件,11是真正執(zhí)行source0的函數(shù),10-0就是點(diǎn)擊事件的整個(gè)轉(zhuǎn)發(fā)處理過(guò)程,最終交由我們自定義的回調(diào)方法進(jìn)行處理。

RunLoop 執(zhí)行邏輯

在官方文檔中描述的RunLoop循環(huán)中的執(zhí)行邏輯如下:

  1. 通知監(jiān)聽(tīng)器RunLoop進(jìn)入循環(huán)

  2. 通知監(jiān)聽(tīng)器即將處理Timer事件

  3. 通知監(jiān)聽(tīng)器即將處理source0(不是基于端口的)事件

  4. 執(zhí)行source0事件

  5. 如果有source1(基于端口的)事件則立即執(zhí)行跳轉(zhuǎn)到第九步

  6. 通知監(jiān)聽(tīng)器RunLoop即將進(jìn)入休眠狀態(tài)

  7. 將線程休眠,直到以下事件發(fā)生才會(huì)被喚醒:

    • 有source1事件到達(dá)
    • 定時(shí)器觸發(fā)時(shí)間到達(dá)
    • RunLoop對(duì)象的超時(shí)時(shí)間過(guò)期
    • 被外部顯示喚醒
  8. 通知監(jiān)聽(tīng)器RunLoop對(duì)象即將被喚醒

  9. 處理添加進(jìn)來(lái)的事件,包括:

    • 如果用戶定義的定時(shí)器時(shí)間到達(dá),執(zhí)行定時(shí)器時(shí)間并重啟循環(huán),跳轉(zhuǎn)到第二步
    • 如果有source1事件,傳遞這個(gè)事件
    • 如果RunLoop被顯示喚醒并且沒(méi)有超時(shí)則重啟RunLoop,跳轉(zhuǎn)到第二步
  10. 通知監(jiān)聽(tīng)器RunLoop退出循環(huán)

為了驗(yàn)證上述執(zhí)行順序,可以自行編寫(xiě)監(jiān)聽(tīng)器來(lái)監(jiān)聽(tīng)RunLoop對(duì)象狀態(tài)的改變,具體栗子如下:

- (void)viewWillAppear:(BOOL)animated
{
    // 創(chuàng)建觀察者
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        NSLog(@"Status has changed into: %zd", activity);
    });    
    
    /*
    將監(jiān)聽(tīng)器添加到當(dāng)前RunLoop對(duì)象中,在RunLoop循環(huán)中就會(huì)執(zhí)行上述回調(diào)塊
    監(jiān)聽(tīng)的是kCFRunLoopDefaultMode即默認(rèn)狀態(tài)
    也可以使用kCFRunLoopCommonModes,同時(shí)監(jiān)聽(tīng)默認(rèn)狀態(tài)以及滑動(dòng)視圖的狀態(tài)
    */
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
    //CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes);

    //Core Foundation需要手動(dòng)釋放observer
    CFRelease(observer);
    
    //添加一個(gè)textView,它是UIScrollView的子類
    UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0, 20, ScreenWidth, 300)];
    textView.text = @"Hello, World";
    [textView setBackgroundColor:[UIColor redColor]];
    [self.view addSubview:textView];
}

為了減少輸出選擇監(jiān)聽(tīng)kCFRunLoopDefaultMode模式,啟動(dòng)程序后不做任何操作發(fā)現(xiàn)其輸出如下:

/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    //即將進(jìn)入RunLoop的執(zhí)行循環(huán) 1
    kCFRunLoopEntry = (1UL << 0),
    //即將處理Timer事件 2
    kCFRunLoopBeforeTimers = (1UL << 1),
    //即將處理Source事件 4
    kCFRunLoopBeforeSources = (1UL << 2),
    //RunLoop即將進(jìn)入休眠狀態(tài) 32
    kCFRunLoopBeforeWaiting = (1UL << 5),
    //RunLoop即將被喚醒 64
    kCFRunLoopAfterWaiting = (1UL << 6),
    //RunLoop即將退出 128
    kCFRunLoopExit = (1UL << 7),
    //監(jiān)聽(tīng)RunLoop的全部狀態(tài)
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

Status has changed into: 2
Status has changed into: 4
Status has changed into: 2
Status has changed into: 4
Status has changed into: 2
Status has changed into: 4
Status has changed into: 2
Status has changed into: 4
Status has changed into: 2
Status has changed into: 4
Status has changed into: 2
Status has changed into: 4
Status has changed into: 2
Status has changed into: 4
Status has changed into: 32
Status has changed into: 64
Status has changed into: 2
Status has changed into: 4
Status has changed into: 32
Status has changed into: 64

從輸出中不難發(fā)現(xiàn),不做任何操作時(shí)程序處于NSDefaultRunLoopMode模式下,一直在2-9步間循環(huán),當(dāng)沒(méi)有事件要處理時(shí)就轉(zhuǎn)入了休眠狀態(tài),之后又被喚醒繼續(xù)處理,可能有讀者疑惑為什么連續(xù)那么多次都是2 4的輸出,狀態(tài)2表示即將處理Timer,狀態(tài)4表示即將處理Source,接著就會(huì)處理Source,但如果有source1的存在(基于端口的事件)就不會(huì)休眠直接跳轉(zhuǎn)到第九步處理相關(guān)事件,處理完成之后又回到第二步,所以產(chǎn)生上述輸出。

此時(shí)當(dāng)我們將UITextView中添加多個(gè)換行符直到滾動(dòng)條出現(xiàn)后,滑動(dòng)UItextView會(huì)發(fā)現(xiàn)有如下輸出:

Status has changed into: 2
Status has changed into: 4
Status has changed into: 32
Status has changed into: 64
Status has changed into: 2
Status has changed into: 4
Status has changed into: 32
Status has changed into: 64
Status has changed into: 2
Status has changed into: 4
Status has changed into: 128

128代表RunLoop對(duì)象退出循環(huán)了,因?yàn)楫?dāng)我們滑動(dòng)UItextView時(shí),RunLoop對(duì)象切換到了UITrackingRunLoopMode,前文講過(guò),RunLoop對(duì)象每次執(zhí)行時(shí)只能執(zhí)行在一個(gè)模式下,如果要切換模式只能退出后重新進(jìn)入循環(huán),從上述輸出就證明了這一點(diǎn)。

接下來(lái)看一下RunLoop的執(zhí)行源碼。

RunLoop執(zhí)行的入口函數(shù)

RunLoop對(duì)外只提供了兩個(gè)入口函數(shù)

/*
RunLoop對(duì)外提供的入口函數(shù)
用戶可以顯示調(diào)用后使當(dāng)前線程關(guān)聯(lián)的RunLoop對(duì)象以默認(rèn)模式運(yùn)行
*/
void CFRunLoopRun(void) {   /* DOES CALLOUT */
    //返回值
    int32_t result;
    //循環(huán)體,直到RunLoop停止或者結(jié)束時(shí)才會(huì)終止循環(huán)
    do {
        /*
        調(diào)用CFRunLoopRunSpecific啟動(dòng)RunLoop
        執(zhí)行的RunLoop就是當(dāng)前線程關(guān)聯(lián)的RunLoop對(duì)象
        超時(shí)時(shí)間100億秒,317.098年,永不超時(shí)
        */
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
        CHECK_FOR_FORK();
    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}

/*
RunLoop對(duì)外提供的入口函數(shù)
用戶可以顯示調(diào)用后使當(dāng)前線程關(guān)聯(lián)的RunLoop對(duì)象以指定模式、超時(shí)時(shí)間運(yùn)行
*/
SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    CHECK_FOR_FORK();
    return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}

CFRunLoopRunSpecific

/*
按照指定的模式、超時(shí)時(shí)間以及條件運(yùn)行RunLoop
rl: 要運(yùn)行的RunLoop對(duì)象
modeName: RunLoop對(duì)象要執(zhí)行的模式名稱
seconds: RunLoop循環(huán)的超時(shí)時(shí)間
returnAfterSourceHandled: 是否在處理完source后就退出RunLoop循環(huán)
*/
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    CHECK_FOR_FORK();
    if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
    __CFRunLoopLock(rl);
    //通過(guò)Mode的名稱查找CFRunLoopModeRef對(duì)象
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
    /*
    如果沒(méi)有獲取到Mode
    或Mode的內(nèi)容為空,內(nèi)容為空即Mode的Source/Timer/Observer集合都沒(méi)有數(shù)據(jù)
    為空就直接返回,并不真正執(zhí)行RunLoop的循環(huán)
    */
    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;

    /*
    調(diào)用__CFRunLoopDoObservers觸發(fā)監(jiān)聽(tīng)器,RunLoop即將進(jìn)入循環(huán)
    */
    if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    //調(diào)用真正執(zhí)行RunLoop循環(huán)的函數(shù)
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    /*
    調(diào)用__CFRunLoopDoObservers觸發(fā)監(jiān)聽(tīng)器,RunLoop退出循環(huán)
    */
    if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

        __CFRunLoopModeUnlock(currentMode);
        __CFRunLoopPopPerRunData(rl, previousPerRun);
    rl->_currentMode = previousMode;
    __CFRunLoopUnlock(rl);
    return result;
}

上述代碼做了一系列的處理,比如通過(guò)名稱查找Mode,判斷Mode是否為空,即判斷Mode中是否還有Source/Timer/Observer,其中比較重要的函數(shù)有__CFRunLoopFindMode函數(shù),該函數(shù)在查找根據(jù)Mode名稱查找時(shí),如果沒(méi)有找到會(huì)嘗試創(chuàng)建一個(gè)新的Mode,如果創(chuàng)建失敗才會(huì)返回NULL。__CFRunLoopModeIsEmpty函數(shù)用來(lái)判斷Mode中的Source/Timer/Observer是否為空,如果集合中沒(méi)有對(duì)象就返回true。__CFRunLoopDoObservers用來(lái)觸發(fā)監(jiān)聽(tīng)器的回調(diào)函數(shù)或回調(diào)塊,前文舉的栗子在創(chuàng)建監(jiān)聽(tīng)器并加入到RunLoop對(duì)象后,其實(shí)是將這個(gè)監(jiān)聽(tīng)器加入到了Mode_observers數(shù)組中,所以該函數(shù)內(nèi)部會(huì)遍歷對(duì)應(yīng)數(shù)組并調(diào)用回調(diào)函數(shù)或回調(diào)塊來(lái)進(jìn)行通知。

接下來(lái)就要查看__CFRunLoopRun函數(shù)的實(shí)現(xiàn),但該函數(shù)源碼太長(zhǎng)有三百多行,而且包含了不少跨平臺(tái)的預(yù)編譯指令,由于篇幅的問(wèn)題,這里不直接分析了,有興趣的讀者可以參考本系列文章第五篇iOS多線程——RunLoop與GCD、AutoreleasePool,在這篇文章中會(huì)詳細(xì)講解該函數(shù)的源碼,那這里直接使用深入理解RunLoop https://blog.ibireme.com/2015/05/18/runloop/中作者簡(jiǎn)化整理版本,代碼如下:

__CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled) {
    
    Boolean sourceHandledThisLoop = NO;
    int retVal = 0;
    do {
        
        /// 2. 通知 Observers: RunLoop 即將觸發(fā) Timer 回調(diào)。
        __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers);
        /// 3. 通知 Observers: RunLoop 即將觸發(fā) Source0 (非port) 回調(diào)。
        __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources);
        /// 執(zhí)行被加入的block
        __CFRunLoopDoBlocks(runloop, currentMode);
        
        /// 4. RunLoop 觸發(fā) Source0 (非port) 回調(diào)。
        sourceHandledThisLoop = __CFRunLoopDoSources0(runloop, currentMode, stopAfterHandle);
        /// 執(zhí)行被加入的block
        __CFRunLoopDoBlocks(runloop, currentMode);
        
        /// 5. 如果有 Source1 (基于port) 處于 ready 狀態(tài),直接處理這個(gè) Source1 然后跳轉(zhuǎn)去處理消息。
        if (__Source0DidDispatchPortLastTime) {
            Boolean hasMsg = __CFRunLoopServiceMachPort(dispatchPort, &msg)
            if (hasMsg) goto handle_msg;
        }
        
        /// 通知 Observers: RunLoop 的線程即將進(jìn)入休眠(sleep)。
        if (!sourceHandledThisLoop) {
            __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting);
        }
        
        /// 7. 調(diào)用 mach_msg 等待接受 mach_port 的消息。線程將進(jìn)入休眠, 直到被下面某一個(gè)事件喚醒。
        /// ? 一個(gè)基于 port 的Source 的事件。
        /// ? 一個(gè) Timer 到時(shí)間了
        /// ? RunLoop 自身的超時(shí)時(shí)間到了
        /// ? 被其他什么調(diào)用者手動(dòng)喚醒
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) {
            mach_msg(msg, MACH_RCV_MSG, port); // thread wait for receive msg
        }
        
        /// 8. 通知 Observers: RunLoop 的線程剛剛被喚醒了。
        __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting);
        
        /// 收到消息,處理消息。
    handle_msg:
        
        /// 9.1 如果一個(gè) Timer 到時(shí)間了,觸發(fā)這個(gè)Timer的回調(diào)。
        if (msg_is_timer) {
            __CFRunLoopDoTimers(runloop, currentMode, mach_absolute_time())
        }
        
        /// 9.2 如果有dispatch到main_queue的block,執(zhí)行block。
        else if (msg_is_dispatch) {
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
        }
        
        /// 9.3 如果一個(gè) Source1 (基于port) 發(fā)出事件了,處理這個(gè)事件
        else {
            CFRunLoopSourceRef source1 = __CFRunLoopModeFindSourceForMachPort(runloop, currentMode, livePort);
            sourceHandledThisLoop = __CFRunLoopDoSource1(runloop, currentMode, source1, msg);
            if (sourceHandledThisLoop) {
                mach_msg(reply, MACH_SEND_MSG, reply);
            }
        }
        
        /// 執(zhí)行加入到Loop的block
        __CFRunLoopDoBlocks(runloop, currentMode);
        
        
        if (sourceHandledThisLoop && stopAfterHandle) {
            /// 進(jìn)入loop時(shí)參數(shù)說(shuō)處理完事件就返回。
            retVal = kCFRunLoopRunHandledSource;
        } else if (timeout) {
            /// 超出傳入?yún)?shù)標(biāo)記的超時(shí)時(shí)間了
            retVal = kCFRunLoopRunTimedOut;
        } else if (__CFRunLoopIsStopped(runloop)) {
            /// 被外部調(diào)用者強(qiáng)制停止了
            retVal = kCFRunLoopRunStopped;
        } else if (__CFRunLoopModeIsEmpty(runloop, currentMode)) {
            /// source/timer/observer一個(gè)都沒(méi)有了
            retVal = kCFRunLoopRunFinished;
        }
        
        /// 如果沒(méi)超時(shí),mode里沒(méi)空,loop也沒(méi)被停止,那繼續(xù)loop。
    } while (retVal == 0);
}

上面執(zhí)行代碼就和前面官方文檔中講解的順序一致,不再贅述。

在前文給了一個(gè)點(diǎn)擊按鈕的調(diào)用棧運(yùn)行圖,可以發(fā)現(xiàn)執(zhí)行source0事件時(shí)是調(diào)用了一個(gè)非常長(zhǎng)的函數(shù)來(lái)處理,為了方便查看調(diào)用棧執(zhí)行的順序,深入理解RunLoop https://blog.ibireme.com/2015/05/18/runloop/一文中,作者將整個(gè)RunLoop響應(yīng)函數(shù)按執(zhí)行順序列了下來(lái),如下:

{
    /// 1. 通知Observers,即將進(jìn)入RunLoop
    /// 此處有Observer會(huì)創(chuàng)建AutoreleasePool: _objc_autoreleasePoolPush();
    __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopEntry);
    do {
 
        /// 2. 通知 Observers: 即將觸發(fā) Timer 回調(diào)。
        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeTimers);
        /// 3. 通知 Observers: 即將觸發(fā) Source (非基于port的,Source0) 回調(diào)。
        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeSources);
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
 
        /// 4. 觸發(fā) Source0 (非基于port的) 回調(diào)。
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(source0);
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
 
        /// 6. 通知Observers,即將進(jìn)入休眠
        /// 此處有Observer釋放并新建AutoreleasePool: _objc_autoreleasePoolPop(); _objc_autoreleasePoolPush();
        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeWaiting);
 
        /// 7. sleep to wait msg.
        mach_msg() -> mach_msg_trap();
        
 
        /// 8. 通知Observers,線程被喚醒
        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopAfterWaiting);
 
        /// 9. 如果是被Timer喚醒的,回調(diào)Timer
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(timer);
 
        /// 9. 如果是被dispatch喚醒的,執(zhí)行所有調(diào)用 dispatch_async 等方法放入main queue 的 block
        __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(dispatched_block);
 
        /// 9. 如果如果Runloop是被 Source1 (基于port的) 的事件喚醒了,處理這個(gè)事件
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(source1);
 
 
    } while (...);
 
    /// 10. 通知Observers,即將退出RunLoop
    /// 此處有Observer釋放AutoreleasePool: _objc_autoreleasePoolPop();
    __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopExit);
}

參考文章

深入理解RunLoop https://blog.ibireme.com/2015/05/18/runloop/

備注

由于作者水平有限,難免出現(xiàn)紕漏,如有問(wèn)題還請(qǐng)不吝賜教。

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

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

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