RunLoop 二 runLoopMode 的四種訊息
一、回答問題
- runLoop切換 Mode 時,程式是否會退出?
答:不會退出。
解釋:切換模式不會導致程式退出。
剛才 CFRunLoopModeRef 中的定義:“如果需要切換Mode,只能退出當前Loop,在重新選擇一個Mode進入。”這句話是說:“退出當前迴圈的程式碼,重新進入”。 相當於:
- 切換 mode 是在 這兩句程式碼裡面做的。程式碼為:
- 切換mode 並不會導致 while (0 == retVal) 這句迴圈的結果發生變化。所以不會導致程式退出。
二、RunLoop 模式
- runLoop 會選擇一種模式執行。當它選擇了一種模式執行後,它會執行 上面程式碼 的 sleep_and_wait() 方法。有訊息就會執行。
- 它會執行什麼訊息? 它會處理 Sources0,Sources1, observers, timers.
三、RunLoop 模式中所要處理的4種訊息
- Source0
- 觸控事件處理
- performSelector:onThread:
- touchesBegan:withEvent:
例如:- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
方法
-
這個方法就是通過 runLoop 進行處理的,因為點選事件就是通過 runLoop處理的。
-
執行程式,在 NSLog這句程式碼上 打斷點。
-
再次執行程式,可以看到左邊的程式棧,但是系統隱藏了一些。只能看到1,14,15,16. 沒有 2 - 13 。
-
可以通過 lldb 指令 bt 就可以列印函式呼叫棧。就可以看到了 1 - 15 全部的方法。 列印結果為:
-
可以看到裡面第8個 是
frame #8: 0x0000000106453101 CoreFoundation__CFRUNLOOP_IS _CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
-
這句話裡面就可以看到 由 source0 出發 呼叫 第7個
__handleEventQueueInternal
-
所以:處理點選事件,是由 Source0 處理的
另外: performSelector:onThread: 方法也是由 Source0 處理的。
例如:
[self performSelector:<#(nonnull SEL)#> onThread:<#(nonnull NSThread *)#> withObject:<#(nullable id)#> waitUntilDone:<#(BOOL)#>]
- 傳入一個執行緒物件給onThread引數,performSelector引數裡傳入的方法 可以放入到 onThread 引數 的執行緒中去執行。
- 比如說給 onThread 引數一個子執行緒,就可以讓 performSelector引數裡傳入的方法 在子執行緒中執行。
- Source1
-
基於Port的執行緒間通訊
-
系統事件捕捉
-
基於Port的執行緒間通訊
- 可以理解成,基於 介面進行通訊。比如說: 執行緒1 有一個介面,執行緒2 有一個介面,那麼只能通過 這兩個介面進行通訊。
- 可以理解成,基於 介面進行通訊。比如說: 執行緒1 有一個介面,執行緒2 有一個介面,那麼只能通過 這兩個介面進行通訊。
-
系統事件捕捉
- 這個也是 Source1 來管理。
- 例如:在螢幕上任意地方點選了一下,所產生的點選事件。是通過 Source1 去捕捉 點選事件,再包裝成 Source0 去處理。
- 相當於事件是通過 Source1 去捕捉,在分發到 Source0 去處理。
- Timers
- NSTimer
- performSelector:withObject:afterDelay:(屬於 Timers 裡面的)
例如:[self performSelector:@"方法名稱" withObject:<#(nullable id)#> afterDelay:2]
- 兩秒後執行 方法。
- 這句程式碼的底層就是 NSTimer,就是定時器。
- 定時器 屬於 timers 的範疇
- Observers
- 用於監聽RunLoop的狀態
- UI重新整理(BeforeWaiting)
- Autorelease pool(BeforeWaiting)
UI重新整理:
-
它就是通過監聽 RunLoop 的狀態 來改變的。
-
比如:監聽到 RunLoop 要睡覺的話,就會在 RunLoop 睡覺之前重新整理UI介面。
-
例如:
self.view.backgroundColor = [UIColor redColor]
這句程式碼。它不是寫完後立即生效,而是執行到這句程式碼時先記住有這個事情,在什麼時候這句程式碼會生效?這句程式碼如何會生效呢? -
這句程式碼如何會生效呢?
- 通過 Observers 讓這句程式碼生效。Observers 一旦監聽到 RunLoop即將睡覺,會在RunLoop的執行緒 睡覺之前重新整理UI介面。這個時候 這句程式碼就會生效。
Autorelease pool
- 通過 Observers 來實現。
- 會在RunLoop的執行緒 睡覺之前,清空下釋放池
四、RunLoop 的執行邏輯
01、通知Observers:進入Loop
02、通知Observers:即將處理Timers
03、通知Observers:即將處理Sources
04、處理Blocks
05、處理Source0(可能會再次處理Blocks)
06、如果存在Source1,就跳轉到第8步
07、通知Observers:開始休眠(等待訊息喚醒)
08、通知Observers:結束休眠(被某個訊息喚醒)
01>處理Timer
02> 處理GCD Async To Main Queue
03> 處理Source1
09、處理Blocks
10、根據前面的執行結果,決定如何操作
01> 回到第02步
02> 退出Loop
11、通知Observers:退出Loop