你要知道的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)行講解,讀者可按需查閱。
- iOS多線程——你要知道的NSThread都在這里
- iOS多線程——你要知道的GCD都在這里
- iOS多線程——你要知道的NSOperation都在這里
- iOS多線程——你要知道的RunLoop都在這里
- iOS多線程——RunLoop與GCD、AutoreleasePool
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的作用。類似于javascript的Event 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ì)講解RunLoop與autoreleasepool的關(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模型:

從上圖可以看出一個(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ì)象,NSRunLoop和CFRunLoopRef,區(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_t為key,以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)容,建議讀者閱讀原文:

上圖很好的描述了struct __CFRunLoop數(shù)據(jù)結(jié)構(gòu)相關(guān)成員變量的關(guān)系,每一個(gè)__CFRunLoop對(duì)象可以包含數(shù)個(gè)不同的Mode,而每一個(gè)Mode又包含了數(shù)個(gè)Source、Observer和Timer,當(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
Source即CFRunLoopSourceRef類的對(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和NSTimer是toll-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
Mode是RunLoop中比較重要的部分,系統(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í)行的Mode即CFRunLoopModeRef類的對(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)將kCFRunLoopDefaultMode和UITrackingRunLoopMode添加到了_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屬性,NSDefaultRunLoopMode和UITrackingRunLoopMode都被添加進(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)同步添加NSTimer到UITrackingRunLoopMode模式下,所以定時(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)用棧如下圖所示:

從上圖中可以看到程序在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í)行邏輯如下:
通知監(jiān)聽(tīng)器RunLoop進(jìn)入循環(huán)
通知監(jiān)聽(tīng)器即將處理Timer事件
通知監(jiān)聽(tīng)器即將處理source0(不是基于端口的)事件
執(zhí)行source0事件
如果有source1(基于端口的)事件則立即執(zhí)行跳轉(zhuǎn)到第九步
通知監(jiān)聽(tīng)器RunLoop即將進(jìn)入休眠狀態(tài)
-
將線程休眠,直到以下事件發(fā)生才會(huì)被喚醒:
- 有source1事件到達(dá)
- 定時(shí)器觸發(fā)時(shí)間到達(dá)
- RunLoop對(duì)象的超時(shí)時(shí)間過(guò)期
- 被外部顯示喚醒
通知監(jiān)聽(tīng)器RunLoop對(duì)象即將被喚醒
-
處理添加進(jìn)來(lái)的事件,包括:
- 如果用戶定義的定時(shí)器時(shí)間到達(dá),執(zhí)行定時(shí)器時(shí)間并重啟循環(huán),跳轉(zhuǎn)到第二步
- 如果有source1事件,傳遞這個(gè)事件
- 如果RunLoop被顯示喚醒并且沒(méi)有超時(shí)則重啟RunLoop,跳轉(zhuǎn)到第二步
通知監(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);
}
參考文章
備注
由于作者水平有限,難免出現(xiàn)紕漏,如有問(wèn)題還請(qǐng)不吝賜教。