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

RunLoop 系列文章

深刻淺出 RunLoop(一):初識
深刻淺出 RunLoop(二):數據結構
深刻淺出 RunLoop(三):事件循環機制
深刻淺出 RunLoop(四):RunLoop 與線程
深刻淺出 RunLoop(五):RunLoop 與 NSTimer
深刻淺出 RunLoop(六):相關面試題面試

CFRunLoopRef

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

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

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會立馬退出。
typedef struct __CFRunLoopMode *CFRunLoopModeRef;

struct __CFRunLoopMode {
    CFStringRef _name;             // mode 類型,如:NSDefaultRunLoopMode
    CFMutableSetRef _sources0;     // CFRunLoopSourceRef
    CFMutableSetRef _sources1;     // CFRunLoopSourceRef
    CFMutableArrayRef _observers;  // CFRunLoopObserverRef
    CFMutableArrayRef _timers;     // CFRunLoopTimerRef
    ...
};
複製代碼

RunLoop 的常見模式

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

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

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

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

CFRunLoopSourceRef

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

CFRunLoopTimerRef

  • CFRunloopTimerNSTimer是 toll-free bridged 的,能夠相互轉換;
  • performSelector:withObject:afterDelay:方法會建立timer並添加到RunLoop中。
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

  • 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)
相關文章
相關標籤/搜索