?xml version="1.0" encoding="UTF-8"?
參考文章:深入理解RunLoop
RunLoop的作用?
保持程序運(yùn)行。一般來(lái)說(shuō)一個(gè)線程一次只能執(zhí)行一次任務(wù),執(zhí)行完成后線程會(huì)退出。RunLoop能夠保證線程執(zhí)行完任務(wù)后不退出。
處理App的各種事件(觸摸事件、timer事件、seletor事件等)
節(jié)省CPU資源,提高程序性能。線程休眠時(shí)不占用cpu資源,等待喚醒執(zhí)行任務(wù)
哪些可以喚醒runloop?
Source
當(dāng)調(diào)用dispatch_async(dispatch_get_main_queue(),block)時(shí)
Timer
外表手動(dòng)喚醒
Runloop對(duì)象?
Fundation框架
NSRunLoop(基于CFRunloopRef的封裝,面向?qū)ο蟮腁PI,線程不安全)
CoreFundation
CFRunLoopRef,純C函數(shù)的API,線程安全
CFRunLoopRef 的代碼是開源的,可以在這里?http://opensource.apple.com/tarballs/CF/CF-855.17.tar.gz?下載到整個(gè)CoreFoundation的源碼。
獲取RunLoop對(duì)象
蘋果不允許直接創(chuàng)建RunLoop對(duì)象,但是提供了函數(shù)獲取。
RunLoop對(duì)象通過(guò)懶加載獲取
Fundation
[NSRunloop currentRunLoop]??// 獲取當(dāng)前線程的RunLoop對(duì)象
[NSRunloop mainRunLoop] // 獲取主線程的RunLoop對(duì)象
CoreFundation
CFRunLoopGetCurrent(); // 獲取當(dāng)前線程的RunLoop對(duì)象
CFRunLoopGetMain(); // 獲取主線程的RunLoop對(duì)象
RunLoop和線程的關(guān)系
1.每一條線程都有唯一的一個(gè)與之對(duì)象的RunLoop對(duì)象
2.主線程的RunLoop在啟動(dòng)的時(shí)候程序自動(dòng)創(chuàng)建,子線程的RunLoop不會(huì)自動(dòng)創(chuàng)建。
3.Runloop在第一次獲取時(shí)創(chuàng)建(懶加載),在線程結(jié)束時(shí)銷毀
RunLoop源碼:
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t){
??? if(pthread_equal(t,kNilPthreadT)){
t = pthread_main_thread_np();
??? }
// 創(chuàng)建主線程中的runloop對(duì)象
//__CFRunLoops 全局的Dictionary對(duì)象,key時(shí)p_thread,value是CFRunLoopRef。保存線程和Runloop,維護(hù)線程和Runloop的一一對(duì)應(yīng)關(guān)系
??? __CFSpinLock(&loopsLock);
??? if(!__CFRunLoops){
// 第一次進(jìn)入時(shí)創(chuàng)建__CFRunLoops,并且為主線程創(chuàng)建一個(gè)RunLoop對(duì)象
??????? __CFSpinUnlock(&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);
??????? __CFSpinLock(&loopsLock);
??? }
?? ?// 從全局的Dictio中獲取runloop?
??? CFRunLoopRef loop =(CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops,pthreadPointer(t));
??? __CFSpinUnlock(&loopsLock);
??? if(!loop){
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
??????? __CFSpinLock(&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
??????? __CFSpinUnlock(&loopsLock);
CFRelease(newLoop);
??? }
// 注冊(cè)一個(gè)回調(diào),當(dāng)線程銷毀時(shí),銷毀RunLoop
? 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對(duì)象時(shí)一一對(duì)應(yīng)的關(guān)系,對(duì)應(yīng)關(guān)系保存在一個(gè)Dictionary中。所以我們?cè)谧泳€程中使用[NSRunloop currentRunLoop]獲取RunLoop對(duì)象時(shí),如果Dictionary中沒(méi)有,則會(huì)去創(chuàng)建一個(gè),并保存在Dictionary中。如果不獲取RunLoop對(duì)象則不會(huì)去創(chuàng)建。RunLoop的銷毀是發(fā)生在線程結(jié)束時(shí)。
RunLoop相關(guān)的類
Core Fundation中RunLoop相關(guān)的類有5個(gè)
CFRunLoopRef:RunLoop對(duì)象
CFRunLoopModeRef:RunLoop所在的運(yùn)行模式
CFRunLoopSourceRef:事件源
CFRunLoopTimerRef:定時(shí)器
CFRunLoopObserverRef:觀察者
CFRunLoopModeRef
CFRunLoopModeRef
CFRunLoopModeRef類并沒(méi)有對(duì)外暴露,只是通過(guò)CFRunLoopRef的接口進(jìn)行了封裝。他們的關(guān)系如下

一個(gè)RunLoop包含若干個(gè)Mode,一個(gè)Mode中又包含若干個(gè)Source/Timer/Observer。每次調(diào)用RunLoop的主函數(shù)時(shí),只能指定一個(gè)Mode,這個(gè)Mode就是CurrentMode。如果需要切換Mode,則必須退出RunLoop,再重新指定一個(gè)Mode進(jìn)入。這樣做是為了分隔開不同組中的Source/Timer/Observer,讓其互不影響。
CFRunLoopSourceRef
CFRunLoopSourceRef是事件產(chǎn)生的地方。Source有兩個(gè)版本:Source0和Source1
Source0只包含一個(gè)回調(diào)(函數(shù)指針),它并不會(huì)主動(dòng)觸發(fā)事件。使用時(shí),需要先調(diào)用CFRunLoopSourceSignal(source),將這個(gè)soucre標(biāo)記為待處理,然后手動(dòng)調(diào)用CFRunLoopSourceWakeUp(runloop)來(lái)喚醒RunLoop,讓其處理這個(gè)事件。(點(diǎn)擊按鈕、點(diǎn)擊屏幕)
Source1包含一個(gè)mach_port和一個(gè)回調(diào)(函數(shù)指針),被用于用過(guò)內(nèi)核和其他線程相互發(fā)送消息。這種Source能注定喚醒RunLoop的線程。硬件事件(觸摸/鎖屏/搖晃等)
CFRunLoopTimerRef
CFRunLoopTimerRef是基于時(shí)間的觸發(fā)器,他和NSTimer時(shí)toll-free bridged的,可以混用。包含一個(gè)時(shí)間長(zhǎng)度和一個(gè)回調(diào)(函數(shù)指針)。當(dāng)其加入到RunLoop時(shí),RunLoop會(huì)注冊(cè)對(duì)應(yīng)的時(shí)間點(diǎn),當(dāng)時(shí)間點(diǎn)到時(shí),RunLoop會(huì)被喚醒以執(zhí)行那個(gè)回調(diào)。
CFRunLoopObserverRef
CFRunLoopObserverRef是觀察者,每一個(gè)Observer都包含了一個(gè)回調(diào)(函數(shù)指針),當(dāng)RunLoop的狀態(tài)發(fā)生變化時(shí),觀察者就能通過(guò)回調(diào)接收到這個(gè)變化。
可以觀測(cè)的點(diǎn)有一下幾個(gè):
typedef?CF_OPTIONS(CFOptionFlags,?CFRunLoopActivity)?{
????kCFRunLoopEntry?????????=?(1UL?<<?0),?//?即將進(jìn)入Loop
????kCFRunLoopBeforeTimers??=?(1UL?<<?1),?//?即將處理?Timer
????kCFRunLoopBeforeSources?=?(1UL?<<?2),?//?即將處理?Source
????kCFRunLoopBeforeWaiting?=?(1UL?<<?5),?//?即將進(jìn)入休眠
????kCFRunLoopAfterWaiting??=?(1UL?<<?6),?//?剛從休眠中喚醒
????kCFRunLoopExit??????????=?(1UL?<<?7),?//?即將退出Loop
};
上面的source、timer、observer被統(tǒng)稱為mode item,一個(gè)item可以被加入多個(gè)mode中。單一個(gè)item被重復(fù)加入同一個(gè)mode時(shí)沒(méi)有效果的。如果mode中一個(gè)item都沒(méi)有,則RunLoop會(huì)直接退出,不進(jìn)入循環(huán)。
RunLoop的Mode
CFRunLoop和CFRunLoopMode的結(jié)構(gòu)大致如下
struct?__CFRunLoopMode?{
????CFStringRef?_name;????????????//?Mode?Name,?例如?@"kCFRunLoopDefaultMode"
????CFMutableSetRef?_sources0;????//?Set
????CFMutableSetRef?_sources1;????//?Set
????CFMutableArrayRef?_observers;?//?Array
????CFMutableArrayRef?_timers;????//?Array
????...
};
struct?__CFRunLoop?{
????CFMutableSetRef?_commonModes;?????//?Set
????CFMutableSetRef?_commonModeItems;?//?Set
????CFRunLoopModeRef?_currentMode;????//?Current?Runloop?Mode
????CFMutableSetRef?_modes;???????????//?Set
????...
};
這里有個(gè)概念叫“CommonModes”:一個(gè)Mode可以將自己標(biāo)記為“Common”屬性(通過(guò)將其的ModeName添加到RunLoop的“CommonModes”中)。每當(dāng)RunLoop的內(nèi)容發(fā)生變化的時(shí)候都會(huì)自動(dòng)將_commonModeItems里的Source/Observer/Timer同步到具有“Common”標(biāo)記的所有Mode中。
應(yīng)用場(chǎng)景舉例:主線程的RunLoop里有兩個(gè)預(yù)置的Mode:kCFRunLoopDefaultMode和UITrackingRunLoopMode。這兩個(gè)Mode都已經(jīng)被標(biāo)記為"Common"屬性。DefaultMode是App平時(shí)所處的狀態(tài),TrackingRunLoopMode是追蹤ScrollView滑動(dòng)時(shí)的狀態(tài)。當(dāng)你創(chuàng)建一個(gè)Timer并加到DefaultMode時(shí),Timer會(huì)得到重復(fù)回調(diào),但此時(shí)滑動(dòng)一個(gè)TableView時(shí),RunLoop會(huì)將mode切換為TrackingRunLoopMode,這時(shí)Timer就不會(huì)被回調(diào),并且也不會(huì)影響到滑動(dòng)操作。
有時(shí)你需要一個(gè)Timer,在兩個(gè)Mode中都能得到回調(diào),一種辦法就是將這個(gè)Timer分別加入這兩個(gè)Mode。還有一種方式,就是將Timer加入到頂層的RunLoop的"commonModeItems"中。"commonModeItems"被RunLoop自動(dòng)更新到所有具有"Common"屬性的Mode里去。
CFRunLoop對(duì)外暴露的管理Model的接口只有下面兩個(gè):
CFRunLoopAddCommonMode(CFRunLoopRef?runloop,?CFStringRef?modeName);
CFRunLoopRunInMode(CFStringRef?modeName,?...);
Mode暴露的管理mode item的接口有下面幾個(gè)
CFRunLoopAddSource(CFRunLoopRef?rl,?CFRunLoopSourceRef?source,?CFStringRef?modeName);
CFRunLoopAddObserver(CFRunLoopRef?rl,?CFRunLoopObserverRef?observer,?CFStringRef?modeName);
CFRunLoopAddTimer(CFRunLoopRef?rl,?CFRunLoopTimerRef?timer,?CFStringRef?mode);
CFRunLoopRemoveSource(CFRunLoopRef?rl,?CFRunLoopSourceRef?source,?CFStringRef?modeName);
CFRunLoopRemoveObserver(CFRunLoopRef?rl,?CFRunLoopObserverRef?observer,?CFStringRef?modeName);
CFRunLoopRemoveTimer(CFRunLoopRef?rl,?CFRunLoopTimerRef?timer,?CFStringRef?mode);
RunLoop的內(nèi)部邏輯

///?用DefaultMode啟動(dòng)
void?CFRunLoopRun(void)?{
????CFRunLoopRunSpecific(CFRunLoopGetCurrent(),?kCFRunLoopDefaultMode,?1.0e10,?false);
}
///?用指定的Mode啟動(dòng),允許設(shè)置RunLoop超時(shí)時(shí)間
int?CFRunLoopRunInMode(CFStringRef?modeName,?CFTimeInterval?seconds,?Boolean?stopAfterHandle)?{
????return?CFRunLoopRunSpecific(CFRunLoopGetCurrent(),?modeName,?seconds,?returnAfterSourceHandled);
}
///?RunLoop的實(shí)現(xiàn)
int?CFRunLoopRunSpecific(runloop,?modeName,?seconds,?stopAfterHandle)?{
????///?首先根據(jù)modeName找到對(duì)應(yīng)mode
????CFRunLoopModeRef?currentMode?=?__CFRunLoopFindMode(runloop,?modeName,?false);
????///?如果mode里沒(méi)有source/timer/observer,?直接返回。
????if?(__CFRunLoopModeIsEmpty(currentMode))?return;
????///?1.?通知?Observers:?RunLoop?即將進(jìn)入?loop。
????__CFRunLoopDoObservers(runloop,?currentMode,?kCFRunLoopEntry);
????///?內(nèi)部函數(shù),進(jìn)入loop
????__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);
????}
????///?10.?通知?Observers:?RunLoop?即將退出。
????__CFRunLoopDoObservers(rl,?currentMode,?kCFRunLoopExit);
}

RunLoop的使用
AutoreleasePool
即將進(jìn)入Runloop的時(shí)候創(chuàng)建AutoreleasePool。
RunLoop即將進(jìn)入休眠的時(shí)候釋放并創(chuàng)建新的AutoreleasePool。
即將退出Runloop的時(shí)候釋放AutoreleasePool。
App啟動(dòng)后,蘋果在主線程RunLoop里注冊(cè)了兩個(gè)Observer,其回調(diào)都是_wrapRunLoopWithAutoreleasePoolHandler()。
第一個(gè)Observer監(jiān)視的事件是Entry(即將進(jìn)入Loop),其回調(diào)內(nèi)會(huì)調(diào)用_objc_autoreleasePoolPush()創(chuàng)建自動(dòng)釋放池。其order是-2147483647,優(yōu)先級(jí)最高,保證創(chuàng)建釋放池發(fā)生在其他所有回調(diào)之前。
第二個(gè)Observer監(jiān)視了兩個(gè)事件:BeforeWaiting(準(zhǔn)備進(jìn)入休眠)時(shí)調(diào)用_objc_autoreleasePoolPop()和_objc_autoreleasePoolPush()釋放舊的池并創(chuàng)建新池;Exit(即將退出Loop)時(shí)調(diào)用_objc_autoreleasePoolPop()來(lái)釋放自動(dòng)釋放池。這個(gè)Observer的order是2147483647,優(yōu)先級(jí)最低,保證其釋放池子發(fā)生在其他所有回調(diào)之后。
在主線程執(zhí)行的代碼,通常是寫在諸如事件回調(diào)、Timer回調(diào)內(nèi)的。這些回調(diào)會(huì)被RunLoop創(chuàng)建好的AutoreleasePool環(huán)繞著,所以不會(huì)出現(xiàn)內(nèi)存泄漏,開發(fā)者也不必顯示創(chuàng)建Pool了。
事件響應(yīng)
蘋果注冊(cè)了一個(gè)Source1(基于mach_port的)用來(lái)接收系統(tǒng)事件
手勢(shì)識(shí)別
蘋果注冊(cè)了一個(gè)Observer監(jiān)測(cè)BeforeWaiting(Loop即將進(jìn)入休眠)事件,這個(gè)Observer的回調(diào)函數(shù)是_UIGestureRecognizerUpdateObserver(),其內(nèi)部會(huì)獲取所有剛被標(biāo)記為待處理的GestureRecognizer,并執(zhí)行GestureRecognizer的回調(diào)。
界面更新
當(dāng)在操作UI時(shí),比如改變了Frame、更新了UIView/CALayer的層次時(shí),或者手動(dòng)調(diào)用了UIView/CALayer的setNeedsLayout/setNeedsDisplay方法后,這個(gè)UIView/CALayer就被標(biāo)記為待處理,并被提交到一個(gè)全局的容器去。
蘋果注冊(cè)了一個(gè)Observer監(jiān)聽BeforeWaiting(即將進(jìn)入休眠)和Exit(即將退出Loop)事件,回調(diào)去執(zhí)行一個(gè)函數(shù)。這個(gè)函數(shù)會(huì)遍歷所有待處理的UIView/UILayer以執(zhí)行實(shí)際的繪制和調(diào)整,并更新UI
定時(shí)器
NSTimer其實(shí)就是CFRunLoopTimerRef,他們之間是toll-free bridged的。一個(gè)NSTimer注冊(cè)到RunLoop后,RunLoop會(huì)為其重復(fù)的時(shí)間點(diǎn)注冊(cè)好事件。例如10:00,10:10,10:20這幾個(gè)時(shí)間點(diǎn)。RunLoop為了節(jié)省資源,并不會(huì)在非常準(zhǔn)確的時(shí)間點(diǎn)回調(diào)這個(gè)Timer。Timer有個(gè)屬性叫做Tolerance(寬容度),標(biāo)示了當(dāng)時(shí)間點(diǎn)到后,容許有多少最大誤差。
如果某個(gè)時(shí)間點(diǎn)被錯(cuò)過(guò)了,例如執(zhí)行了一個(gè)很長(zhǎng)的任務(wù),則那個(gè)時(shí)間點(diǎn)的回調(diào)也會(huì)跳過(guò)去,不會(huì)延后執(zhí)行。就比如等公交,如果10:10時(shí)我忙著玩手機(jī)錯(cuò)過(guò)了那個(gè)點(diǎn)的公交,那我只能等10:20這一趟了。
CADisplayLink是一個(gè)和屏幕刷新率一致的定時(shí)器(但實(shí)際實(shí)現(xiàn)原理更復(fù)雜,和NSTimer并不一樣,其內(nèi)部實(shí)際是操作了一個(gè)Source)。如果在兩次屏幕刷新之間執(zhí)行了一個(gè)長(zhǎng)任務(wù),那其中就會(huì)有一幀被跳過(guò)去(和NSTimer相似),造成界面卡頓的感覺(jué)。在快速滑動(dòng)TableView時(shí),即使一幀的卡頓也會(huì)讓用戶有所察覺(jué)。Facebook開源的AsyncDisplayLink就是為了解決界面卡頓的問(wèn)題,其內(nèi)部也用到了RunLoop。
PerformSelector
當(dāng)調(diào)用NSObject的performSelecter:afterDelay:后,實(shí)際上其內(nèi)部會(huì)創(chuàng)建一個(gè)Timer并添加到當(dāng)前線程的RunLoop中。所以如果當(dāng)前線程沒(méi)有RunLoop,則這個(gè)方法會(huì)失效。
當(dāng)調(diào)用performSelector:onThread:時(shí),實(shí)際上其會(huì)創(chuàng)建一個(gè)Timer加到對(duì)應(yīng)的線程去,同樣的,如果對(duì)應(yīng)線程沒(méi)有RunLoop該方法也會(huì)失效。
GCD
當(dāng)調(diào)用dispatch_async(dispatch_get_main_queue(),block)時(shí),libDispatch會(huì)向主線程的RunLoop發(fā)送消息,RunLoop會(huì)被喚醒,并從消息中取得這個(gè)block,并在回調(diào)__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__()里執(zhí)行這個(gè)block。但這個(gè)邏輯僅限于dispatch到主線程,dispatch到其他線程仍然是由libDispatch處理的。