RunLoop的事件隊(duì)列

RunLoop的事件隊(duì)列

  1. 每次運(yùn)行RunLoop, 線程中的RunLoop會(huì)自動(dòng)處理線程中的任務(wù), 并且通知觀察者, 匯報(bào)當(dāng)前的狀態(tài), 順序如下

    1. 通知觀察者RunLoop已經(jīng)啟動(dòng)
    2. 通知觀察者任何即將要開(kāi)啟的定時(shí)器
    3. 通知觀察者任何即將要啟動(dòng)的非基于端口的事件源
    4. 啟動(dòng)任何準(zhǔn)備好的非基于端口的事件源
    5. 如果基于端口的事件源準(zhǔn)備好并處于等待狀態(tài), 就立即啟動(dòng), 并且進(jìn)入步驟9
    6. 通知觀察者線程即將進(jìn)入休眠
    7. 將線程置于休眠狀態(tài), 直至以下事件的發(fā)生
      • 某一事件到達(dá)基于端口的源事件
      • 定時(shí)器啟動(dòng)
      • RunLoop設(shè)置的事件已經(jīng)超時(shí)
      • RunLoop被顯式喚醒
    8. 通知觀察者線程即將被喚醒
    9. 處理事件
      • 如果用戶定義的定時(shí)器啟動(dòng), 處理定時(shí)器事件并且重啟RunLoop, 然后進(jìn)入步驟2
      • 如果輸入源啟動(dòng), 傳遞響應(yīng)的信息
      • 如果RunLoop被現(xiàn)實(shí)喚醒, 并且事件還沒(méi)超時(shí), 重啟RunLoop, 進(jìn)入步驟2
    10. 通知觀察者RunLoop結(jié)束
  2. 代碼解釋

     // 用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);
     }
    
  3. 圖解


    322CEF6D-7766-4EE3-9169-40D5F564CDD1.png
  4. RunLoop的一般應(yīng)用

    • NSTimer和GCD定時(shí)器
    • PerformSelector: afterDelay
      • 當(dāng)調(diào)用這個(gè)方法的時(shí)候, 實(shí)際內(nèi)部會(huì)創(chuàng)建一個(gè)Timer并且添加到當(dāng)前的RunLoop中, 如果當(dāng)前線程沒(méi)有RunLoop, 這個(gè)方法也就會(huì)失效
    • 在子線程中開(kāi)啟一個(gè)RunLoop, 做為常駐線程
    • 自動(dòng)釋放池
    • 手勢(shì)識(shí)別等等
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末液走,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子贾陷,更是在濱河造成了極大的恐慌缘眶,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件髓废,死亡現(xiàn)場(chǎng)離奇詭異磅崭,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)瓦哎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門砸喻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蒋譬,你說(shuō)我怎么就攤上這事割岛。” “怎么了犯助?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵癣漆,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我剂买,道長(zhǎng)惠爽,這世上最難降的妖魔是什么癌蓖? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮婚肆,結(jié)果婚禮上租副,老公的妹妹穿的比我還像新娘。我一直安慰自己较性,他們只是感情好用僧,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著赞咙,像睡著了一般责循。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上攀操,一...
    開(kāi)封第一講書(shū)人閱讀 51,258評(píng)論 1 300
  • 那天院仿,我揣著相機(jī)與錄音,去河邊找鬼速和。 笑死歹垫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的健芭。 我是一名探鬼主播县钥,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼慈迈!你這毒婦竟也來(lái)了若贮?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤痒留,失蹤者是張志新(化名)和其女友劉穎谴麦,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體伸头,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡匾效,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了恤磷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片面哼。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖扫步,靈堂內(nèi)的尸體忽然破棺而出魔策,到底是詐尸還是另有隱情,我是刑警寧澤河胎,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布闯袒,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏政敢。R本人自食惡果不足惜其徙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望喷户。 院中可真熱鬧唾那,春花似錦、人聲如沸摩骨。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)恼五。三九已至,卻和暖如春哭懈,著一層夾襖步出監(jiān)牢的瞬間灾馒,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工遣总, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留睬罗,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓旭斥,卻偏偏與公主長(zhǎng)得像容达,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子垂券,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354

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

  • 轉(zhuǎn)自bireme花盐,原地址:https://blog.ibireme.com/2015/05/18/runloop/...
    乜_啊_閱讀 1,369評(píng)論 0 5
  • 轉(zhuǎn)自http://blog.ibireme.com/2015/05/18/runloop 深入理解RunLoop ...
    飄金閱讀 983評(píng)論 0 4
  • RunLoop 的概念 一般來(lái)講,一個(gè)線程一次只能執(zhí)行一個(gè)任務(wù)菇爪,執(zhí)行完成后線程就會(huì)退出算芯。如果我們需要一個(gè)機(jī)制,讓線...
    Mirsiter_魏閱讀 618評(píng)論 0 2
  • http://www.cocoachina.com/ios/20150601/11970.html RunLoop...
    紫色冰雨閱讀 837評(píng)論 0 3
  • 我們赤裸著 像沙丁魚(yú)在每個(gè)冬季一樣 在沙灘上瘋狂地跳舞 瘋狂地做愛(ài)
    青木西閱讀 203評(píng)論 0 0