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. 通知觀察者任何即將要開啟的定時(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ì)失效
    • 在子線程中開啟一個(gè)RunLoop, 做為常駐線程
    • 自動(dòng)釋放池
    • 手勢(shì)識(shí)別等等
最后編輯于
?著作權(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)容

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

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