Runloop 是 iOS 和 OSX 中和線程相關(guān)的基礎(chǔ)概念。提供了線程循環(huán)處理事件的能力,當(dāng)有事件處理時(shí)喚醒線程工作,空閑時(shí)使線程進(jìn)入休眠,以合理的利用系統(tǒng)資源。
Runloop 是線程相關(guān)的概念,和線程是一一對(duì)應(yīng)的。一個(gè)線程對(duì)應(yīng)一個(gè)runloop,默認(rèn)情況下,主線程的 runloop 是啟動(dòng)的,后臺(tái)線程的 runloop 需要自己?jiǎn)?dòng)。
Runloop 的工作模型類似 windows 中的消息機(jī)制(Event loop),接受事件(消息)->處理->等待新事件(消息),偽代碼如下:
loop()
{
init();
while(get_message(&msg) != quit){
handle_message(msg);
}
}
Runloop 中需要處理的事件分2類:
- 輸入源(input source):分發(fā)異步事件,通常都是來(lái)自其他線程或者Application。
- performSelector源
- 基于端口的源(mach port)
- 自定義輸入源
- 計(jì)時(shí)器:定時(shí)的或者一定間隔的觸發(fā)同步事件。
Runloop 的運(yùn)行結(jié)構(gòu)圖如下圖:

Runloop 其實(shí)是一個(gè)對(duì)象,提供了一個(gè)入口函數(shù)來(lái)執(zhí)行上面的事件循環(huán),在函數(shù)內(nèi)部實(shí)現(xiàn)了接受消息->處理->等待新消息這樣的一個(gè)循環(huán)流程,直到循環(huán)結(jié)束。
Runloop 運(yùn)行中會(huì)發(fā)出許多通知,比如狀態(tài)的改變,可以監(jiān)聽(tīng)這些通知作對(duì)應(yīng)的處理。
iOS 和 OSX 中提供了2個(gè)這樣的對(duì)象:NSRunloop和CFRunLoopRef。
內(nèi)部邏輯大致如下圖:

運(yùn)行模式
運(yùn)行模式定義了一個(gè)集合,當(dāng) Runloop 運(yùn)行在某個(gè)模式下時(shí),只會(huì)監(jiān)聽(tīng)某些類型的輸入源事件和計(jì)時(shí)器事件,以及分發(fā)某些特定種類的通知,啟動(dòng) Runloop 時(shí)會(huì)指定一種模式。
常見(jiàn)的一些運(yùn)行模式:
- NSDefaultRunLoopMode:默認(rèn)模式,包括了大部分的操作事件,絕大部份情況下都應(yīng)該使用這個(gè)模式配置自己的 runloop
- NSEventTrackingRunLoopMode:典型的當(dāng) scrollView 滾動(dòng)的時(shí)候是運(yùn)行在該模式下,此時(shí)的不接受Timer事件,所以Timer會(huì)失效
- NSRunLoopCommonModes:模式組,一個(gè)模式將自己標(biāo)記為“common”屬性后就會(huì)在該組中,運(yùn)行在該模式下,能處理的事件就是組中各個(gè)模式能處理事件的合集,在默認(rèn)情況下,NSDefaultRunLoopMode 和NSEventTrackingRunLoopMode 被標(biāo)記為 “common” 屬性
輸入源
- 基于端口的源:監(jiān)聽(tīng)Mach port,cocoa 提供了內(nèi)置的類來(lái)實(shí)現(xiàn)這類輸入源 NSPort (IPC相關(guān))
- 自定義輸入源:
- PerformSelector源:主要是那些和時(shí)間能扯上關(guān)系的API,比如:(其實(shí)是通過(guò)timer實(shí)現(xiàn)的)
- performSelectorOnMainThread:withObject:waitUntilDone:
- performSelector:withObject:afterDelay:
- performSelector:onThread:withObject:waitUntilDone:
Runloop 的使用
- 使用端口或者自定義輸入源來(lái)實(shí)現(xiàn)線程間的通信
- 在線程中使用計(jì)時(shí)器
- 使用performSelector方法
- 保持線程執(zhí)行周期性的任務(wù)
Runloop 的應(yīng)用
- Autorelease pool:runloop 會(huì)自動(dòng)維護(hù)autorelease pool的創(chuàng)建和釋放,autorelease pool 會(huì)在下一次runloop循環(huán)之前釋放 pool 中的對(duì)象,所以在像for,while這樣的循環(huán)中,如果需要大量創(chuàng)建臨時(shí)對(duì)象時(shí)需要自己創(chuàng)建autorelease pool,避免臨時(shí)內(nèi)存峰值過(guò)高,比如圖片相關(guān)的操作
- 事件響應(yīng)(觸摸/鎖屏/搖晃等):source1 事件
- 手勢(shì)識(shí)別:監(jiān)聽(tīng)特定的通知,處理手勢(shì)的回調(diào)
- 界面更新:監(jiān)聽(tīng)特定的通知,處理界面的刷新操作
- 定時(shí)器:NSTimer 和 CADisplayLink
- PerformSelector:AfterDelay:
- GCD:dispatch_async(dispatch_get_main_queue(), block)
附 Runloop 的部分源碼圖:

一個(gè)使用 Runloop 的簡(jiǎn)單例子
- (void)threadMain
{
// The application uses garbage collection, so no autorelease pool is needed.
NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];
// Create a run loop observer and attach it to the run loop.
CFRunLoopObserverContext context = {0, self, NULL, NULL, NULL};
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,
kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context);
if (observer)
{
CFRunLoopRef cfLoop = [myRunLoop getCFRunLoop];
CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode);
}
// Create and schedule the timer.
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self
selector:@selector(doFireTimer:) userInfo:nil repeats:YES];
NSInteger loopCount = 10;
do
{
// Run the run loop 10 times to let the timer fire.
[myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
loopCount--;
}
while (loopCount);
}