runloop的使用

?xml version="1.0" encoding="UTF-8"?

參考文章:深入理解RunLoop

iOS刨根問(wèn)底-深入理解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處理的。

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 1 Runloop機(jī)制原理 深入理解RunLoop http://www.cocoachina.com/ios/2...
    Kevin_Junbaozi閱讀 4,239評(píng)論 4 30
  • 轉(zhuǎn)載:http://www.cocoachina.com/ios/20150601/11970.html RunL...
    Gatling閱讀 1,556評(píng)論 0 13
  • RunLoop 的概念 一般來(lái)講,一個(gè)線程一次只能執(zhí)行一個(gè)任務(wù),執(zhí)行完成后線程就會(huì)退出。如果我們需要一個(gè)機(jī)制,讓線...
    Mirsiter_魏閱讀 670評(píng)論 0 2
  • 原文地址:http://blog.ibireme.com/2015/05/18/runloop/ RunLoop ...
    大餅炒雞蛋閱讀 1,264評(píng)論 0 6
  • 為什么門框那里總是有噪音。我聽見了 嬰兒在哭泣,我家沒(méi)有嬰兒,隔壁是老兩口子。它為什么傷心?是不是我差它錢,上輩子...
    李一十八閱讀 144評(píng)論 0 0

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