深刻淺出 RunLoop(二):數據結構

RunLoop 系列文章

深刻淺出 RunLoop(一):初識
深刻淺出 RunLoop(二):數據結構
深刻淺出 RunLoop(三):事件循環機制
深刻淺出 RunLoop(四):RunLoop 與線程
深刻淺出 RunLoop(五):RunLoop 與 NSTimer
iOS - 聊聊 autorelease 和 @autoreleasepool:RunLoop 與 @autoreleasepoolmarkdown

CFRunLoopRef

RunLoop對象的底層就是一個CFRunLoopRef結構體,它裏面存儲着:數據結構

  • _pthread:RunLoop與線程是一一對應關係
  • _commonModes:存儲着 NSString 對象的集合(Mode 的名稱)
  • _commonModeItems:存儲着被標記爲通用模式的Source0/Source1/Timer/Observer
  • _currentMode:RunLoop當前的運行模式
  • _modes:存儲着RunLoop全部的 Mode(CFRunLoopModeRef)模式
// CFRunLoop.h
typedef struct __CFRunLoop * CFRunLoopRef;
// CFRunLoop.c
struct __CFRunLoop {
    pthread_t _pthread;  // 與線程一一對應
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
    ...
};
複製代碼

CFRunLoopModeRef

  • CFRunLoopModeRef表明RunLoop的運行模式;
  • 一個RunLoop包含若干個 Mode,每一個 Mode 又包含若干個Source0/Source1/Timer/Observer
  • RunLoop啓動時只能選擇其中一個 Mode,做爲 currentMode;
  • 若是須要切換 Mode,只能退出當前 Loop,再從新選擇一個 Mode 進入,切換模式不會致使程序退出;
  • 不一樣 Mode 中的Source0/Source1/Timer/Observer能分隔開來,互不影響;
  • 若是 Mode 裏沒有任何Source0/Source1/Timer/ObserverRunLoop會立馬退出。
// CFRunLoop.h
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
// CFRunLoop.c
struct __CFRunLoopMode {
    CFStringRef _name;             // mode 類型,如:NSDefaultRunLoopMode
    CFMutableSetRef _sources0;     // CFRunLoopSourceRef
    CFMutableSetRef _sources1;     // CFRunLoopSourceRef
    CFMutableArrayRef _observers;  // CFRunLoopObserverRef
    CFMutableArrayRef _timers;     // CFRunLoopTimerRef
    ...
};
複製代碼

RunLoop 的常見模式

ModeName 描述
NSDefaultRunLoopMode / KCFRunLoopDefaultMode 默認模式
UITrackingRunLoopMode 界面追蹤模式,用於 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其餘 Mode 影響;
NSRunLoopCommonModes / KCFRunLoopCommonModes 通用模式(默認包含 KCFRunLoopDefaultMode 和 UITrackingRunLoopMode)

該模式不是實際存在的一種模式,它只是一個特殊的標記,是同步Source0/Source1/Timer/Observer到多個 Mode 中的技術方案。被標記爲通用模式的Source0/Source1/Timer/Observer都會存放到 _commonModeItems 集合中,會同步這些Source0/Source1/Timer/Observer到多個 Mode 中。

備註:框架

  • NSDefaultRunLoopModeNSRunLoopCommonModes屬於Foundation框架;
  • KCFRunLoopDefaultModeKCFRunLoopCommonModes屬於Core Foundation框架;
  • 前者是對後者的封裝,做用相同。

CFRunLoopModeRef 這樣設計有什麼好處?Runloop爲何會有多個 Mode?

  • Mode 作到了屏蔽的效果,當RunLoop運行在 Mode1 下面的時候,是處理不了 Mode2 的事件的;
  • 好比NSDefaultRunLoopMode默認模式和UITrackingRunLoopMode滾動模式,滾動屏幕的時候就會切換到滾動模式,就不用去處理默認模式下的事件了,保證了 UITableView 等的滾動順暢。

CFRunLoopSourceRef

  • RunLoop中有兩個很重要的概念,一個是上面提到的模式,還有一個就是事件源事件源分爲輸入源(Input Sources)定時器源(Timer Sources)兩種;
  • 輸入源(Input Sources)又分爲Source0Source1兩種,如下__CFRunLoopSource中的共用體union中的version0version1就分別對應Source0Source1
// CFRunLoop.h
typedef struct __CFRunLoopSource * CFRunLoopSourceRef;
// CFRunLoop.m
struct __CFRunLoopSource {
    CFRuntimeBase _base;
    uint32_t _bits;
    pthread_mutex_t _lock;
    CFIndex _order;                       /* immutable */
    CFMutableBagRef _runLoops;
    union {
        CFRunLoopSourceContext version0;  /* immutable, except invalidation */
        CFRunLoopSourceContext1 version1; /* immutable, except invalidation */
    } _context;
};
複製代碼

Source0 和 Source1 的區別:oop

Input Sources 區別
Source0 須要手動喚醒線程:添加Source0RunLoop並不會主動喚醒線程,須要手動喚醒)
① 觸摸事件處理
performSelector:onThread:
Source1 具有喚醒線程的能力
① 基於 Port 的線程間通訊
② 系統事件捕捉:系統事件捕捉是由Source1來處理,而後再交給Source0處理

CFRunLoopTimerRef

  • CFRunloopTimerNSTimer是 toll-free bridged 的,能夠相互轉換;
  • performSelector:withObject:afterDelay:方法會建立timer並添加到RunLoop中。
// CFRunLoop.h
typedef struct CF_BRIDGED_MUTABLE_TYPE(NSTimer) __CFRunLoopTimer * CFRunLoopTimerRef;
// CFRunLoop.c
struct __CFRunLoopTimer {
    CFRuntimeBase _base;
    uint16_t _bits;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop;           // 添加該 timer 的 RunLoop
    CFMutableSetRef _rlModes;        // 全部包含該 timer 的 modeName
    CFAbsoluteTime _nextFireDate;
    CFTimeInterval _interval;        /* immutable 理想時間間隔 */    
    CFTimeInterval _tolerance;       /* mutable 時間誤差 */  
    uint64_t _fireTSR;	             /* TSR units */
    CFIndex _order;                  /* immutable */
    CFRunLoopTimerCallBack _callout; /* immutable 回調入口 */
    CFRunLoopTimerContext _context;  /* immutable, except invalidation */
};
複製代碼

CFRunLoopObserverRef

做用post

  • CFRunLoopObserverRef用來監聽RunLoop的 6 種活動狀態
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),          // 即將進入 RunLoop
    kCFRunLoopBeforeTimers = (1UL << 1),   // 即將處理 Timers
    kCFRunLoopBeforeSources = (1UL << 2),  // 即將處理 Sources
    kCFRunLoopBeforeWaiting = (1UL << 5),  // 即將進入休眠
    kCFRunLoopAfterWaiting = (1UL << 6),   // 剛從休眠中喚醒
    kCFRunLoopExit = (1UL << 7),           // 即將退出 RunLoop
    kCFRunLoopAllActivities = 0x0FFFFFFFU  // 表示以上全部狀態
};
複製代碼
  • UI 刷新(BeforeWaiting)
  • Autorelease pool(BeforeWaiting)

定義ui

// CFRunLoop.h
typedef struct __CFRunLoopObserver * CFRunLoopObserverRef;
// CFRunLoop.c
struct __CFRunLoopObserver {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop;              // 添加該 observer 的 RunLoop
    CFIndex _rlCount;
    CFOptionFlags _activities;	        /* immutable 監聽的活動狀態 */
    CFIndex _order;                     /* immutable */
    CFRunLoopObserverCallBack _callout;	/* immutable 回調入口 */
    CFRunLoopObserverContext _context;	/* immutable, except invalidation */
};
複製代碼

CFRunLoopObserverRef中的_activities用來保存RunLoop的活動狀態。當RunLoop的狀態發生改變時,經過回調_callout通知全部監聽這個狀態的Observerspa

相關文章
相關標籤/搜索