RunLoop

1. 簡介
RunLoop可以理解爲一個do-while循環:

function loop(){
	initialize();
	do{
		event = getEvent();
		HandleEvent(event);
		}while(isWeakUp);
	}

當然肯定還有一些其他的處理邏輯在內部,才能保證其一一對應的線程能夠在有任務時被喚醒,沒有任務時休眠。
OSX/iOS系統提供了兩個RunLoop對象:NSRunLoopCFRunLoopRef;

CFRunLoopRef是蘋果提供的CoreFoundation框架內一套純c的函數API,而且API的線程都是安全的;
NSRunLoop則是基於CFRunLoopRef的封裝,提供了面向對象的API,但是線程是不安全的。

首先看一下RunLoop結構體

struct __CFRunLoop {
    CFRuntimeBase _base;
    _CFRecursiveMutex _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
    _CFThreadRef _pthread;
    uint32_t _winthread;
    CFMutableSetRef _commonModes;//set
    CFMutableSetRef _commonModeItems;//set
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes; //set
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFAbsoluteTime _runTime;
    CFAbsoluteTime _sleepTime;
    CFTypeRef _counterpart;
    _Atomic(uint8_t) _fromTSD;
    CFLock_t _timerTSRLock;
};

主要是_wakeUpPort、_commonModes、_commonModeItems、_currentMode

_wakeUpPort:是指當前喚醒runloop的端口
_commonModes:是一個集合,存放的是當前runloop所有Mode,而每個Mode包含若干個 Source/Timer/Observer
_commonModeItems:這個集合主要存放的是喚醒runloop的事件源:timer、source(分爲source0,source1)、observer;
_currentMode:每次調用RunLoop的主函數,只能指定其中的一個Mode,故當前被指定的Mode就是_currentMode,RunLoop如需切換Mode只能退出當前loop重新指定Mode再進入
引用自

source0和source1的區別
source0不具備喚醒線程的能力,只包含一個回調,不能主動觸發事件,使用時需先調用CFRunLoopSourceSignal(source),將這個source標記爲待處理,然後手動調用 CFRunLoopWeakUp(runnloop)來喚醒RunLoop,讓其處理這個事件。
source1:則能夠主動喚醒線程,包含了一個mach_port和一個回調(函數指針),被用於通過內核和其他線程相互發送消息,這種Source能主動喚起RunLoop的線程。內核端口mach_port可以直接喚醒RunLoop,如觸摸事件。

系統默認註冊了5個mode
1. kCFRunLoopDefaultMode:App的默認Mode,通常主線程是在這個Mode下運行的。
2. UITrackingRunLoopMode:界面跟蹤Mode,用於ScrollView追蹤觸摸滑動,保證界面滑動時不受其他Mode影響
3. UIInitializationRunLoopMode:在剛啓動App時進入的第一個Mode,啓動完成後就不再使用
4. GSEventReceiveRunLoopMode:接受系統事件的內部Mode,通常用不到。
5. kCFRunLoopCommonModes,這是一個佔位的Mode,沒有實際作用
2. RunLoop實現邏輯
根據蘋果在文檔(https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW23)裏的說明,RunLoop內部邏輯大致如下:

  1. Notify observers that the run loop has been entered.
  2. Notify observers that any ready timers are about to fire.
  3. Notify observers that any input sources that are not port based are about to fire.
  4. Fire any non-port-based input sources that are ready to fire.
  5. If a port-based input source is ready and waiting to fire, process the event immediately. Go to step 9.
  6. Notify observers that the thread is about to sleep.
  7. Put the thread to sleep until one of the following events occurs:
    • An event arrives for a port-based input source.
    • A timer fires.
    • The timeout value set for the run loop expires.
    • The run loop is explicitly woken up.
  8. Notify observers that the thread just woke up.
  9. Process the pending event.
    • If a user-defined timer fired, process the timer event and restart the loop. Go to step 2.
    • If an input source fired, deliver the event.
    • If the run loop was explicitly woken up but has not yet timed out, restart the loop. Go to step 2.
  10. Notify observers that the run loop has exite

流程圖引用自(https://blog.ibireme.com/wp-content/uploads/2015/05/RunLoop_1.png)
流程圖引用自
3. 什麼時候使用RunLoop

When Would You Use a Run Loop? The only time you need to run a run
loop explicitly is when you create secondary threads for your
application.

Thread Safety and Run Loop Objects Thread safety varies depending on
which API you are using to manipulate your run loop. The functions in
Core Foundation are generally thread-safe and can be called from any
thread. If you are performing operations that alter the configuration
of the run loop, however, it is still good practice to do so from the
thread that owns the run loop whenever possible.

The Cocoa NSRunLoop class is not as inherently thread safe as its Core
Foundation counterpart. If you are using the NSRunLoop class to modify
your run loop, you should do so only from the same thread that owns
that run loop. Adding an input source or timer to a run loop belonging
to a different thread could cause your code to crash or behave in an
unexpected way.

摘自蘋果文檔,可以看出,主要用於線程間的通信。

細節還是需要自己去下載源碼看了,重在自己領悟。源碼鏈接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章