讀 Run Loops Document 筆記

下面主要是對(duì)Run Loops官方文檔的翻譯及總結(jié)珠移。

定義

run loop 是一個(gè)事件處理的循環(huán)倔丈,負(fù)責(zé)對(duì)工作進(jìn)行調(diào)度灰嫉,同時(shí)協(xié)調(diào)接收即將到來(lái)的任務(wù)郊酒。他的目的是確保線程在有任務(wù)的時(shí)候立即工作遇绞,沒(méi)有任務(wù)的時(shí)候就歇著键袱。
run loop 的管理不是完全自動(dòng)的,你可以通過(guò)代碼去運(yùn)行線程的run loop摹闽,讓他去處理特定的任務(wù)蹄咖。包括主線程在內(nèi)的所有線程,都不需要自己去創(chuàng)建run loop付鹿,他們本身內(nèi)部就有runloop對(duì)象澜汤,只不過(guò)主線程中的runloop是在程序啟動(dòng)的時(shí)候就自動(dòng)運(yùn)行起來(lái)了,而主線程外的其他線程的runloop 需要用戶自己去run舵匾。

剖析

run loop 就像它的名字一樣俊抵,它是一個(gè)在線程內(nèi)部的循環(huán),用來(lái)運(yùn)行那些負(fù)責(zé)處理即將進(jìn)入線程的事件的程序坐梯。
run loop接收來(lái)自于兩種類(lèi)型數(shù)據(jù)源的事件徽诲;一種是Input source,負(fù)責(zé)傳遞異步事件烛缔,主要來(lái)自其他線程或者其他的應(yīng)用程序馏段;另外一種是Timer source 負(fù)責(zé)傳遞同步事件,主要來(lái)計(jì)劃時(shí)間觸發(fā)或者是重復(fù)間隔觸發(fā)事件践瓷。當(dāng)兩種源類(lèi)型的事件到達(dá)后院喜,都用特定的程序來(lái)進(jìn)行處理。

image.png

上圖是run loop及其數(shù)據(jù)源的構(gòu)想結(jié)構(gòu)圖晕翠,input source 將異步事件傳遞個(gè)對(duì)應(yīng)的處理程序喷舀,最終調(diào)用runUntilDate:方法退出run loop,Timer將事件傳遞給對(duì)應(yīng)的處理程序淋肾,但不會(huì)導(dǎo)致run loop 退出硫麻。
除了處理數(shù)據(jù)源的輸入外,run loop還會(huì)為一些特性產(chǎn)生通知樊卓,只要注了run loop的觀察者都可以接受到這些通知拿愧,并且在線程內(nèi)做一些額外的工作。

  • Run Loop Modes

run loop mode 是被監(jiān)控的input source和 timer source的集合碌尔,也是需被run loop通知的觀察者的集合浇辜。每當(dāng)你運(yùn)行你的run loop 的時(shí)候,你都需要間接或直接的指定一個(gè)在內(nèi)部運(yùn)行的mode 唾戚。在run loop 運(yùn)行的過(guò)程中柳洋,只監(jiān)控與當(dāng)前mode相關(guān)聯(lián)的source,也只接受關(guān)聯(lián)source傳遞的事件叹坦。而與其他mode相關(guān)聯(lián)的source及傳遞的事件熊镣,只能等待到對(duì)應(yīng)的mode才能被處理,因此你可以通過(guò)mode來(lái)過(guò)濾出不需要的數(shù)據(jù)源傳遞的事件。
系統(tǒng)默認(rèn)提供了幾種mode绪囱,你也可以自定義mode测蹲,當(dāng)你自定義mode時(shí),一定要在mode內(nèi)添加一個(gè)或多個(gè)的 input source或者timer source 或者觀察者以確保該mode有用毕箍。

  • 系統(tǒng)預(yù)定義mode

    • 1:NSDefaultRunLoopMode 默認(rèn)mode弛房,多數(shù)情況下run loop在這種mode下運(yùn)行。

    • 2:NSConnectionReplyMode

    • 3:NSModalPanelRunLoopMode

    • 4:NSEventTrackingRunLoopMode 頁(yè)面的滑動(dòng)等運(yùn)行在這用mode下

    • 5: NSRunLoopCommonModes 這是一個(gè)可配置的通用mode組而柑,將input source 和這個(gè)mode相關(guān)聯(lián)文捶,同時(shí)也會(huì)與組內(nèi)的其他mode相關(guān)聯(lián),在Cocoa applications中媒咳,這個(gè)組中默認(rèn)包括default, modal, event tracking modes粹排,你也可以通過(guò)CFRunLoopAddCommonMode(::)方法將自定義的mode加入到這個(gè)組中。

  • input source

    input source 以異步方式傳遞事件給線程涩澡。而基于input source 的事件源主要分為兩類(lèi): Port-based input sources:監(jiān)控你應(yīng)用程序的Match端口顽耳;Custom input sources:監(jiān)控自定義的事件源。他們兩個(gè)對(duì)于系統(tǒng)來(lái)說(shuō)沒(méi)有任何差別妙同,唯一的差別是是如何發(fā)出信號(hào)射富,Port-based是由系統(tǒng)的內(nèi)核自動(dòng)發(fā)出信號(hào),Custom input 需要從其他線程手動(dòng)的發(fā)送信號(hào)粥帚。

    當(dāng)你創(chuàng)建一個(gè)input source 的時(shí)候胰耗,你可將他分配給你的run loop 的一個(gè)或多個(gè)mode,mode會(huì)隨時(shí)影響到哪個(gè)input source被監(jiān)控芒涡,如果你的input source 沒(méi)有在當(dāng)前被監(jiān)控的mode中柴灯,那么事件需要等到runloop 執(zhí)行對(duì)應(yīng)的mode時(shí)才會(huì)被執(zhí)行(這就是如果你將輪播圖的定時(shí)器加入在default mode中后,如果滑動(dòng)頁(yè)面時(shí)费尽,輪播圖不會(huì)動(dòng)的原因赠群,滑動(dòng)頁(yè)面時(shí)當(dāng)前runloop運(yùn)行的是track mode,解決辦法是將定時(shí)器加入到common mode中旱幼,或者將定時(shí)器加入到新的線程查描,并且run該線程的runloop)。

  • Port-Based Sources柏卤、Custom Input Sources
    詳見(jiàn)
    Configuring a Port-Based Input Source.
    Defining a Custom Input Source
    除上面外叹誉,系統(tǒng)定義了一個(gè)custom source 允許用戶調(diào)用,Cocoa Perform Selector Sources闷旧,就是performSelectorOnMainThread:withObject:waitUntilDone:
    等方法

  • Timer source

Timer source 是預(yù)先設(shè)置好的,在未來(lái)的某個(gè)時(shí)間向線程傳遞同步事件钧唐,是一種線程提醒自己做某事的方式忙灼。
Timer source 的產(chǎn)生雖然是基于時(shí)間的通知,但是他并不是實(shí)時(shí)的,和input source一樣该园,它也需要和runloop的具體mode相關(guān)聯(lián)酸舍。如果與之關(guān)聯(lián)的mode沒(méi)有被監(jiān)控,那么timer將擁有不被觸發(fā)里初。
詳見(jiàn)Configuring Timer Sources

  • Run Loop Observers

和需要適當(dāng)?shù)耐交虍惒绞录?lái)觸發(fā)的事件源不同啃勉,Run Loop Observers是由runloop自己在運(yùn)行到對(duì)應(yīng)的的位置的時(shí)去候觸發(fā)的。用戶可以由此根據(jù)不同的時(shí)機(jī)去做一些相應(yīng)的處理双妨。主要的可以關(guān)聯(lián)的屬性如下:

  • 進(jìn)入runloop
  • runloop即將處理timer
  • runloop即將處理input source
  • runloop即將進(jìn)入睡眠
  • runloop被喚醒淮阐,在處理喚醒事件前
  • 從runloop中退出
    詳細(xì)創(chuàng)建Configuring the Run Loop.
  • The Run Loop Sequence of Events

每次運(yùn)行runloop它處理待處理事件的整個(gè)過(guò)程如下:

  • 1.通知已經(jīng)進(jìn)入runloop
  • 2.通知即將處理準(zhǔn)備好的timer
  • 3.通知即將處理所有非port based 的input source事件
  • 4.處理那些待處理的非port based 的input source事件
  • 5.如果有在等待的基于port based 的input source事件則立即去處理,然后到第9步驟
  • 6.通知runloop即將進(jìn)入睡眠
  • 7.保持睡眠狀態(tài)刁品,直到發(fā)生任何一個(gè)如下事件:
    • 有機(jī)遇port based 的input source事件到達(dá)
    • fire一個(gè)timer
    • 為runloop設(shè)置的超時(shí)值到期
    • runloop被明確喚醒
  • 8.通知線程被喚醒
    1. 處理待處理的事件
    • 如果用戶定義的timer被觸發(fā)泣特,處理timer事件,重啟runloop挑随,跳到第2步驟
    • 如果input source 被觸發(fā)状您,則傳遞事件
    • 如果runloop是被明確喚醒的且還沒(méi)有超時(shí),則重新運(yùn)行runloop兜挨,跳轉(zhuǎn)到第2步驟

When Would You Use a Run Loop?

主線程的runloop是在整個(gè)應(yīng)用開(kāi)始運(yùn)行的時(shí)候膏孟,由系統(tǒng)自動(dòng)啟動(dòng)的,所以需要用戶手動(dòng)啟動(dòng)的只有其他的輔助線程拌汇,而在輔助線程中也要根據(jù)情況去考慮柒桑,是否有啟動(dòng)runloop的必要,runloop的預(yù)期是你需要和線程有更多的交互担猛。
如果在下面的任意情況之一幕垦,那么就需要去啟動(dòng)runloop:

  • 通過(guò)port或者custom input source 和其他線程進(jìn)行交流
  • 在線程內(nèi)應(yīng)用timer
  • 應(yīng)用任何的 performSelector..方法
  • 保持線程以執(zhí)行定期任務(wù)

Using Run Loop Objects

  • 獲得runloop對(duì)象

  //獲得當(dāng)前線程的runloop對(duì)象
  NSRunLoop *runloop = [NSRunLoop currentRunLoop];
  • 配置runloop

當(dāng)你運(yùn)行runloop前一定要先像其中加入至少一個(gè)input source 或者 timer,否則的話只要一運(yùn)行傅联,runloop就會(huì)停止先改。除了source 外,你也可以向其注冊(cè)觀察者蒸走,用 CFRunLoopObserverRef和`CFRunLoopAddObserver方法去創(chuàng)建與添加仇奶。
下面的代碼展示了添加觀察者的過(guò)程

  
void myfunc() {
    NSLog(@"回調(diào)");
};

- (void)viewDidLoad {
    [super viewDidLoad];
    NSRunLoop *runloop = [NSRunLoop currentRunLoop];
    
    CFRunLoopObserverContext  context = {0, (__bridge void *)(self), NULL, NULL, NULL};
    CFRunLoopObserverRef    observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &myfunc, &context);
 
    if (observer)
    {
        CFRunLoopRef    cfLoop = [runloop getCFRunLoop];
        CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode);
    }

    [NSTimer scheduledTimerWithTimeInterval: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.
        [runloop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
        loopCount--;
    }
    while (loopCount);
}

- (void)doFireTimer {
    NSLog(@"timer fun");
}

為長(zhǎng)期存在的線程配置runloop時(shí),最好至少添加一個(gè)input source來(lái)接收消息比驻。 雖然您可以附加一個(gè)定時(shí)器進(jìn)入運(yùn)行循環(huán)该溯,但一旦定時(shí)器觸發(fā),它通常會(huì)失效别惦,這會(huì)導(dǎo)致運(yùn)行循環(huán)退出狈茉。 附加重復(fù)計(jì)時(shí)器可以使運(yùn)行循環(huán)運(yùn)行更長(zhǎng)的時(shí)間,但是會(huì)涉及定期觸發(fā)計(jì)時(shí)器以喚醒您的線程掸掸,這實(shí)際上是另一種形式的輪詢氯庆。 相比之下蹭秋,input source會(huì)等待事件到來(lái)的過(guò)程中,讓線程保持睡眠狀態(tài)堤撵。

Configuring Run Loop Sources

大牛的文章從源碼實(shí)現(xiàn)的角度介紹runloop,也可以去觀摩相互印證实昨,加深理解洞豁。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市荒给,隨后出現(xiàn)的幾起案子丈挟,更是在濱河造成了極大的恐慌,老刑警劉巖锐墙,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件礁哄,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡溪北,警方通過(guò)查閱死者的電腦和手機(jī)桐绒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)之拨,“玉大人茉继,你說(shuō)我怎么就攤上這事∈辞牵” “怎么了烁竭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)吉挣。 經(jīng)常有香客問(wèn)我派撕,道長(zhǎng),這世上最難降的妖魔是什么睬魂? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任终吼,我火速辦了婚禮,結(jié)果婚禮上氯哮,老公的妹妹穿的比我還像新娘际跪。我一直安慰自己,他們只是感情好喉钢,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布姆打。 她就那樣靜靜地躺著,像睡著了一般肠虽。 火紅的嫁衣襯著肌膚如雪幔戏。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天税课,我揣著相機(jī)與錄音评抚,去河邊找鬼豹缀。 笑死,一個(gè)胖子當(dāng)著我的面吹牛慨代,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播啸如,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼侍匙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了叮雳?” 一聲冷哼從身側(cè)響起想暗,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎帘不,沒(méi)想到半個(gè)月后说莫,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡寞焙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年储狭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捣郊。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡辽狈,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出呛牲,到底是詐尸還是另有隱情刮萌,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布娘扩,位于F島的核電站着茸,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏琐旁。R本人自食惡果不足惜涮阔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望旋膳。 院中可真熱鬧澎语,春花似錦、人聲如沸验懊。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)义图。三九已至减俏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間碱工,已是汗流浹背娃承。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工奏夫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人历筝。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓酗昼,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親梳猪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子麻削,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 來(lái)或者去,是為了什么春弥,可有想明白呛哟? 忙或者閑,都無(wú)所謂匿沛,只是值不值得扫责? 匆匆的人生,有什么短長(zhǎng)可言逃呼? 精彩鳖孤、傳奇、...
    曉露輕塵閱讀 300評(píng)論 0 2
  • 連載目錄 | 琵琶行傳奇 上一章:琵琶行傳奇之網(wǎng)紅生涯 | 連載二 文 / 十三明月 “誒蜘渣,你的ID名可真夠繞口的...
    十三明月閱讀 538評(píng)論 0 3
  • 有時(shí) 云很淡 心歡心 也有時(shí) 云痕長(zhǎng) 心意涼 有時(shí) 雨微醺 知長(zhǎng)久 有時(shí) 雨傾盆 一瞬間 些許清涼
    亦之游閱讀 64評(píng)論 1 2