天天看點

RunLoop

簡介

什麼是 RunLoop ?

從字面意思看的話是運作循環、跑圈的意思;

RunLoop 的基本作用是什麼:

  • 保持程式的持續運作;
  • 處理 App 中的各種事件(比如觸摸事件、定時器事件、Selector 事件);
  • 節省 CPU 資源,提高程式性能:該做事時做事,該休息時休息;

如果沒有RunLoop

int main(int argc, char * argv[])
{
    // 程式開始
    NSLog(@"execute main function");
    // 程式結束
    return 0;
}
           

沒有 RunLoop 的情況下:第3行後程式就結束了;

如果有了RunLoop

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

有 RunLoop 的情況下:由于 main 函數裡面啟動了個 RunLoop ,是以程式并不會馬上退出,保持持續運作狀态

main函數中的RunLoop

int main(int argc, char * argv[])
{
    @autoreleasepool
    {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
           

第 5 行代碼的 UIApplicationMain 函數内部就啟動了一個 RunLoop 是以 UIApplicationMain 函數一直沒有傳回,保持了程式的持續運作。這個預設啟動的 RunLoop 是跟主線程相關聯的;

RunLoop對象

iOS 中哪些 API 來通路和使用 RunLoop?

  • Foundation

    NSRunLoop

  • Core Foundation

    CFRunLoopRef

NSRunLoop 和 CFRunLoopRef 都代表着 RunLoop 對象;

NSRunLoop 是基于 CFRunLoopRef 的一層 OC 包裝,是以要了解RunLoop内部結構,需要多研究CFRunLoopRef層面的API(Core Foundation層面);

RunLoop資料

RunLoop與線程

  • 每條線程都有唯一的一個與之對應的 RunLoop 對象;
  • 主線程的 RunLoop 已經自動建立好了,子線程的 RunLoop 需要主動建立;
  • RunLoop 在第一次擷取時建立,線上程結束時銷毀;

獲得 RunLoop 對象

// 獲得目前線程的RunLoop對象
[NSRunLoop currentRunLoop]; 
// 獲得主線程的RunLoop對象
[NSRunLoop mainRunLoop]; 
           
// 獲得目前線程的RunLoop對象
CFRunLoopGetCurrent(); 
 // 獲得主線程的RunLoop對象
CFRunLoopGetMain();
           

RunLoop相關類

Core Foundation 中有幾個關于RunLoop的類?

Core Foundation 中有5個關于RunLoop的類;

  • CFRunLoopModeRef
  • CFRunLoopSourceRef
  • CFRunLoopTimerRef
  • CFRunLoopObserverRef
RunLoop

RunLoop

  • CFRunLoopModeRef 代表 RunLoop 的運作模式;
  • 一個 RunLoop 包含若幹個 Mode,每個 Mode 又包含若幹個Source/Observer/Timer;
  • 每次 RunLoop 啟動時,隻能指定其中一個 Mode,這個 Mode 被稱作 CurrentMode;
  • 如果需要切換 Mode,隻能退出 Loop,再重新指定一個 Mode 進入
  • 這樣做主要是為了分隔開不同組的 Source/Observer/Timer,讓其互不影響;

RunLoop 中有幾種 Mode?

系統預設注冊了 5 種 Mode;

  • kCFRunLoopDefaultMode:App 的預設 Mode ,通常主線程是在這個 Mode 下運作;
  • UITrackingRunLoopMode:界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其他 Mode 影響;
  • UIInitializationRunLoopMode: 在剛啟動 App 時第進入的第一個 Mode ,啟動完成後就不再使用;
  • GSEventReceiveRunLoopMode: 接受系統事件的内部 Mode ,通常用不到;
  • kCFRunLoopCommonModes: 這是一個占位用的 Mode ,不是一種真正的Mode;

CFRunLoopSourceRef 是事件源(輸入源)

  • 按照 Apple 官方文檔 Sources 的分類:

    Port-Based Sources

    Custom Input Sources

    Cocoa Perform Selector Sources

  • 按照函數調用棧 Sources 的分類:

    Source0:非基于 Port;

    Source1:基于 Port,通過核心和其他線程通信,接受,分發系統事件;

CFRunLoopTimerRef 是基于時間的觸發器;

基本上說的就是 NSTimer;

CFRunLoopObserverRef 是觀察者,能夠監聽 RunLoop 的狀态改變;

可以監聽的時間點有以下幾個

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) 
{
    // 即将進入 RunLoop
    kCFRunLoopEntry = (1UL << 0) = 1,
    // 即将處理 Timer
    kCFRunLoopBeforeTimers = (1UL << 1) = 2,
    // 即将處理 Source
    kCFRunLoopBeforeSources = (1UL << 2) = 4,
    // 即将進入 睡眠
    kCFRunLoopBeforeWaiting = (1UL << 5) = 32,
    // 剛從 睡眠 中 喚醒
    kCFRunLoopAfterWaiting = (1UL << 6) = 64,
    // 即将退出 RunLoop
    kCFRunLoopExit = (1UL << 7) = 128,
    // 所有活動
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};
           

RunLoop 處理邏輯

官方版

RunLoop

官方版 RunLoop 處理邏輯

RunLoop

網友整理版

RunLoop

網友整理版 RunLoop 處理邏輯

RunLoop 應用

  • NSTimer
  • ImageView顯示
  • PerformSelector
  • 常駐線程
  • 自動釋放池

繼續閱讀