RunLoop的簡(jiǎn)單介紹

什么是RunLoop

  • 從字面意思來(lái)看:跑圈、運(yùn)動(dòng)循環(huán)
  • 基本用法:保持程序持續(xù)運(yùn)行、處理App中的各種事件(觸摸事件、定時(shí)器事件、SEL等等)
  • 為什么需要它:節(jié)省CPU資源、 提高性能

如果沒(méi)有RunLoop


    int main (int argc, char *argv[]) {
        NSLog(@"execute main function");
        return 0;
    }
    
  • 如果沒(méi)有RunLoop,第三行完成后程序就結(jié)束了

如果有了RunLoop

    
    int main(int argc, char *argv[]) {
        BOOL running = YES;
        do {
            // 執(zhí)行各種任務(wù),處理各種事件
            // ...
        } while (running);
        return 0;
    }

  • 由于main函數(shù)里面啟動(dòng)了RunLoop,所以程序保持持續(xù)運(yùn)行狀態(tài)

main函數(shù)里的RunLoop


    int main(int argc, char *argv[]) {
        @autoreleasepool {
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
  • 在UIApplicationMain函數(shù)內(nèi)部就啟動(dòng)了一個(gè)RunLoop
  • 所以UIApplicationMain函數(shù)一直沒(méi)有返回,保持了程序的持續(xù)運(yùn)行
  • 這個(gè)默認(rèn)啟動(dòng)的RunLoop是根主線程相關(guān)聯(lián)的

RunLoop對(duì)象

  • iOS中有兩套API來(lái)訪問(wèn)和使用RunLoop
  • Foundation中的NSRunloop
  • Core Foundation中的CFRunLoopRef
  • NSRunloop和CFRunLoopRef都代表著RunLoop對(duì)象
  • NSRunloop是基于CFRunLoopRef封裝的, 所以要了解RunLoop的內(nèi)部結(jié)構(gòu),還是要研究CFRunLoopRef(Core Foundation)層面的API

RunLoop與線程

  • 每條線程都有位移的一個(gè)與之對(duì)應(yīng)的RunLoop對(duì)象
  • 主線程的RunLoop一經(jīng)自動(dòng)創(chuàng)建好了,子線程的RunLoop需要主動(dòng)創(chuàng)建
  • RunLoop在第一次獲取時(shí)創(chuàng)建,在線程結(jié)束時(shí)銷毀

獲得RunLoop對(duì)象

  • Foundation

      // 獲得當(dāng)前線程的RunLoop對(duì)象 
    - [NSRunLoop currentRunLoop];
      // 獲得主線程的RunLoop對(duì)象
    - [NSRunLoop mainRunLoop];
    
  • Core Foundation

    // 獲得當(dāng)前線程的RunLoop對(duì)象
    CFRunLoopGetCurrent();
    // 獲得主線程的RunLoop對(duì)象
    CFRunLoopGetMain();

RunLoop的相關(guān)類

  • Core Foundation中關(guān)于RunLoop的5個(gè)類

    • CFRunLoopRef
    • CFRunLoopModeRef
    • CFRunLoopSourceRef
    • CFRunLoopTimerRef
    • CFRunLoopOberverRef
  • CFRunLoopModeRef

    • CFRunLoopModeRef代表RunLoop的運(yùn)行模式

    • 一個(gè)RunLoop包含若干個(gè)Mode,每個(gè)Mode有包含若干個(gè)Source/Timer/Observe

    • 每次RunLoop啟動(dòng)時(shí),只能指定其中一個(gè)Mode,這個(gè)Mode被稱作CurrentMode

    • 如果需要切換Mode,只能退出RunLoop,在重新指定一個(gè)RunLoop進(jìn)入

    • 這樣做主要是為了分隔開(kāi)不同組的Source/Timer/Observe,讓其互不影響

    • 系統(tǒng)默認(rèn)注冊(cè)了5個(gè)Mode

      • kCFRunLoopDefaultMode:App的默認(rèn)Mode,通常主線程再是這個(gè)Mode下運(yùn)行的
      • UITrackingRunLoopMode:界面跟蹤Mode, 用于ScrollView追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受其他Mode影響
      • UIInitializionRunLoopMode:在剛啟動(dòng)App是進(jìn)入的第一個(gè)Mode,啟動(dòng)時(shí)完成后就不再使用
      • GSEcentRunLoopMode:接受系統(tǒng)時(shí)間的內(nèi)部Mode,通常不會(huì)用到
      • kCFRunLoopCommonMode:這是一個(gè)占位用的Mode,不是真正的Mode
  • CFRunLoopSourceRef

    • CGRunLoopRef是事件源(輸入源)

      • 以前的分類方法

      • Port-Based Source

      • Custom Input Source

      • Cocoa Perform Selector Source

      • 現(xiàn)在的分類方法

      • Source0:不是基于Port的

      • Source1:基于Port的

  • CFRunLoopTimerRef

    • CFRunLoopTimerRef是基于事件的觸發(fā)器
    • 基本上說(shuō)的就是NSTimer
  • CFRunLoopObserverRef

    • CFRunLoopObserverRef是觀察者,能夠監(jiān)聽(tīng)RunLoop的狀態(tài)改變
    • 可以監(jiān)聽(tīng)的時(shí)間點(diǎn)有以下幾個(gè)
    
          typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity)    {
            // 即將進(jìn)入Loop
           kCFRunLoopEntry                 =(1UL << 0),
            // 即將處理Timer
           kCFRunLoopBeforeTimers          =(1UL << 1),
            // 即將處理Source
           kCFRunLoopBeforeSource          =(1UL << 2),
            // 即將進(jìn)入休眠
           kCFRunLoopBeforeWaiting         =(1UL << 5),
            // 剛從休眠中喚醒
           kCFRunLoopAfterWaiting          =(1UL << 6),
            // 即將退出Loop
           kCFRunLoopExit                  =(1UL << 7),   
          }
      ```
    
    

RunLoop處理邏輯 - 官方版

RunLoop的事件隊(duì)列

  • 每次運(yùn)行RunLoop,你的線程RunLoop會(huì)自動(dòng)處理之前未處理的消息,并通知相關(guān)的觀察者。具體順序如下

    • 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ā)生:

      • 某一時(shí)間到達(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被顯式喚醒而且事件還沒(méi)超時(shí),重啟RunLoop,進(jìn)入步驟2
    • 10.通知觀察者RunLoop結(jié)束

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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