RunLoop整理

本文在下面兩篇文章的基礎上,進行了理解和梳理。html

深刻理解RunLoop - ibiremebash

[轉]iOS 事件處理機制與圖像渲染過程網絡

1 RunLoop是什麼

RunLoop,是一個消息處理模式。併發

  1. 只要用戶、系統沒主動殺掉程序,RunLoop就讓程序一直活着,而且節省資源消耗。
  2. 運行的機制,簡單說就是,有事就作,沒事就歇。
  3. 他經過mac port等,使他能夠從休眠狀態被喚醒。

2 生命週期

CFRunLoopObserverRef 是觀察者,每一個 Observer 都包含了一個回調(函數指針),當 RunLoop 的狀態發生變化時,觀察者就能經過回調接受到這個變化。能夠觀測的時間點有如下幾個:函數

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0), // 即將進入Loop
    kCFRunLoopBeforeTimers = (1UL << 1), // 即將處理 Timer
    kCFRunLoopBeforeSources = (1UL << 2), // 即將處理 Source
    kCFRunLoopBeforeWaiting = (1UL << 5), // 即將進入休眠
    kCFRunLoopAfterWaiting = (1UL << 6), // 剛從休眠中喚醒
    kCFRunLoopExit = (1UL << 7), // 即將退出Loop
};
複製代碼

2-1 source0和source1

處理如UIEvent,CFSocket這類事件,須要手動觸發。 觸摸事件實際上是Source1接收系統事件後在回調 __IOHIDEventSystemClientQueueCallback() 內觸發的 Source0,Source0 再觸發的 _UIApplicationHandleEventQueue()。 Source0必定是要喚醒runloop及時響應並執行的,若是runloop此時在休眠等待系統的 mach_msg事件,那麼就會經過source1來喚醒runloop執行oop

這樣,上圖如今就能夠看明白啦ui

  1. source0如UI事件、網絡事件的來源,都是source1建立
  2. 因此RunLoop的一次循環,先把全部Timer、source0事件解決掉,其間由於不響應source1和Timer,所以不會產生新的source0事件。
  3. 上圖第4步結束後,纔去檢查有沒有新的source1事件,若是就休眠。
  4. 只有source一、Timer、dispatch block能夠喚醒RunLoop,而且誰喚醒就只處理誰。(其中source1會產生source0事件,再由RunLoop處理)

補充一個圖this

3 RunLoop結構、模式,以及與線程的關係

3-1 與線程的關係

子線程,默認沒有RunLoop,這樣默認狀況下,子線程執行完,就結束。(符合預期)spa

  1. 一一對應
  2. 延遲建立
  3. 結束銷燬
  4. 私有(mainThread的RunLoop除外)

3-2 RunLoop內部結構

struct __CFRunLoop {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;	/* locked for accessing mode list */
    __CFPort _wakeUpPort;	// used for CFRunLoopWakeUp 
    Boolean _unused;
    volatile _per_run_data *_perRunData; // reset for runs of the run loop
    pthread_t _pthread;
    uint32_t _winthread;
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFTypeRef _counterpart;
};


struct __CFRunLoopMode {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;	/* must have the run loop locked before locking this */
    CFStringRef _name;
    Boolean _stopped;
    char _padding[3];
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
    CFMutableDictionaryRef _portToV1SourceMap;
    __CFPortSet _portSet;
    CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
    dispatch_source_t _timerSource;
    dispatch_queue_t _queue;
    Boolean _timerFired; // set to true by the source when a timer has fired
    Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
    mach_port_t _timerPort;
    Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
    DWORD _msgQMask;
    void (*_msgPump)(void);
#endif
    uint64_t _timerSoftDeadline; /* TSR */
    uint64_t _timerHardDeadline; /* TSR */
};
複製代碼
  1. Observer 能夠經過建立本身的Observer,監聽RunLoop自己的生命週期

3-3 Mode指定、Mode切換

  1. mode一次只能指定一個
  2. 切換時,先退出前一個Loop,切換Model,進入另外一個Model。
  3. CommonModes

4 RunLoop和AutoReleasePool的關係

  1. 進入RunLoop時,建立AutoReleasePool
  2. beforeWaiting時,由於全部事都處理完了,因此釋放舊池,並建立新池。(這裏沒有延後建立新池,多是由於如今最空,先建立出來,由於是高頻使用的)
  3. 線程退出時,也就是RunLoop即將銷燬時,釋放AutoReleasePool。

5 RunLoop和GCD的關係

  1. 對於主線程中的task queue,至關於加入到了main RunLoop的dispatch block線程

  2. 對於併發隊列中的task,至關於加入到了子線程RunLoop的dispatch block中,若是子線程尚未RunLoop會自動建立。

dispatch_after 3秒後交給RunLoop,可是RunLoop何時處理,就不必定了。

6 RunLoop和AFN2.0的關係

建立了一個本身的RunLoop(藍色),並添加了一個空的port,用於避免RunLoop結束。

相關文章
相關標籤/搜索