1. 程式人生 > >RunLoop 二 runLoopMode 的四種訊息

RunLoop 二 runLoopMode 的四種訊息

一、回答問題

  1. runLoop切換 Mode 時,程式是否會退出?
    答:不會退出。
    解釋:切換模式不會導致程式退出。

剛才 CFRunLoopModeRef 中的定義:“如果需要切換Mode,只能退出當前Loop,在重新選擇一個Mode進入。”這句話是說:“退出當前迴圈的程式碼,重新進入”。 相當於:

  • 切換 mode 是在 這兩句程式碼裡面做的。程式碼為:
  • 在這兩句中切換mode
  • 切換mode 並不會導致 while (0 == retVal) 這句迴圈的結果發生變化。所以不會導致程式退出。

二、RunLoop 模式

  1. runLoop 會選擇一種模式執行。當它選擇了一種模式執行後,它會執行 上面程式碼 的 sleep_and_wait() 方法。有訊息就會執行。
  2. 它會執行什麼訊息? 它會處理 Sources0,Sources1, observers, timers.
    sleep_and_wait()方法所要執行的4個訊息

三、RunLoop 模式中所要處理的4種訊息

  1. Source0
  • 觸控事件處理
  • performSelector:onThread:
  • touchesBegan:withEvent:

例如:- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 方法

  • 這個方法就是通過 runLoop 進行處理的,因為點選事件就是通過 runLoop處理的。

  • 執行程式,在 NSLog這句程式碼上 打斷點。
    touchesBegan方法

  • 再次執行程式,可以看到左邊的程式棧,但是系統隱藏了一些。只能看到1,14,15,16. 沒有 2 - 13 。
    touchesBegan 方法的程式棧,只能看到一小部分 2-13看不見

  • 可以通過 lldb 指令 bt 就可以列印函式呼叫棧。就可以看到了 1 - 15 全部的方法。 列印結果為:
     lldb 指令 bt 就可以列印全部的函式呼叫棧

  • 可以看到裡面第8個 是 frame #8: 0x0000000106453101 CoreFoundation__CFRUNLOOP_IS _CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17

  • 這句話裡面就可以看到 由 source0 出發 呼叫 第7個 __handleEventQueueInternal

    處理這個事件,在呼叫到 touchesBegin 這個方法

  • 所以:處理點選事件,是由 Source0 處理的

另外: performSelector:onThread: 方法也是由 Source0 處理的。

例如:
[self performSelector:<#(nonnull SEL)#> onThread:<#(nonnull NSThread *)#> withObject:<#(nullable id)#> waitUntilDone:<#(BOOL)#>]

  • 傳入一個執行緒物件給onThread引數,performSelector引數裡傳入的方法 可以放入到 onThread 引數 的執行緒中去執行。
  • 比如說給 onThread 引數一個子執行緒,就可以讓 performSelector引數裡傳入的方法 在子執行緒中執行。
  1. Source1
  • 基於Port的執行緒間通訊

  • 系統事件捕捉

  • 基於Port的執行緒間通訊

    • 可以理解成,基於 介面進行通訊。比如說: 執行緒1 有一個介面,執行緒2 有一個介面,那麼只能通過 這兩個介面進行通訊。
      基於port的執行緒間通訊
  • 系統事件捕捉

    • 這個也是 Source1 來管理。
    • 例如:在螢幕上任意地方點選了一下,所產生的點選事件。是通過 Source1 去捕捉 點選事件,再包裝成 Source0 去處理。
    • 相當於事件是通過 Source1 去捕捉,在分發到 Source0 去處理。
  1. Timers
  • NSTimer
  • performSelector:withObject:afterDelay:(屬於 Timers 裡面的)

例如:[self performSelector:@"方法名稱" withObject:<#(nullable id)#> afterDelay:2]

  • 兩秒後執行 方法。
  • 這句程式碼的底層就是 NSTimer,就是定時器。
  • 定時器 屬於 timers 的範疇
  1. 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