iOS知識梳理 - runloop

iOS知識梳理 - runloop

runloop其實是個很普遍的東西,基本上是個應用框架都有類似的東西,比如js或flutter裏的event loop,Android的looper。

下面我們從最簡單的例子來看一下,runloop的本質

溯源

最初學編程的時候,有個經典題目是用c/c++實現四則運算。大體上要下面這個可以一直輸入的效果:

img

代碼大概像這樣:

int main()
{
    string str;
    double result;//保存結果
    while(getline(cin,str))
    {
    // calculate
    cout << "Result is: " << result << endl;
    }
}

由於可以一直輸入新的算式,我們自然地使用了一個while循環來處理輸入,有新的一行輸入就去計算結果並輸出,沒有新的輸入時其實就是阻塞等待。

這其實就是最簡單的runloop。

實際應用程序中,無非就是輸入的形式更多了一些,執行的任務更復雜了一些,基本框架,其實還是一個while循環。

網上看到一個僞代碼挺清晰的:

function loop() {
  initialize();
  do {
      var message = get_next_message();
      process_message(message);
  } while (message != quit);
}

深入理解runloop

runloop的實際實現還是有大量細節的,core foundation框架也是開源的,這裏其實可以挖得很深。

iOS 多線程:『RunLoop』詳盡總結,這篇文章講得挺好,推薦閱讀。很多細節本文就不再做太多展開了。

Mode

runloop在運行時有個mode的概念,runloop只會處理當前mode的event。

比如,我們有mode1和mode2,mode1對應了3個event,mode2對應了2個event,如果當前runloop處於mode1,就只會處理mode1對應的3個event。

具體而言,runloop暴露出來的是kCFRunLoopDefaultMode和UITrackingRunLoopMode兩個mode,通常主線程處於DefaultMode,但是當滑動scrollview時,主線程處於TrackingMode。而默認地,我們的NSTimer事件是綁定在DefaultMode的,這就會導致,當滑動scrollview時,就不會觸發NSTimer事件。

爲了解決這種問題,runloop給出了一個CommonMode的概念,CommonMode不是一個具體的Mode,可以理解成一組Mode的集合,這裏實際上就是DefaultMode和TrackingMode。把Timer加入到CommonMode,則在DefaultMode和TrackingMode都會執行這個Timer了。

Mode這個東西,在實際使用中,除了給Timer的使用帶來困擾之外,幾乎從未被使用過。

那麼,爲什麼蘋果會在Runloop中設計Mode這個東西?

網上並沒有找到有價值的討論,不過可以簡單揣測一下,目的可能是,在某些場景下,可以只處理對應場景需要的事件。這可能是基於一種特殊情況的假設:在某種場景下,CPU忙不過來,因此需要停掉其它任務,專注於當前場景的任務。比如,任務A需要90%的CPU,其它雜七雜八事件處理需要40%的CPU,那麼,停掉那些雜事專注當前場景,可能是有意義的行爲。

如果CPU並不會跑滿的話,那麼Mode的拆分就意義不大,充其量是副作用極大的優先級調整(低優先級的直接被卡住了誒)。

參考其它平臺的EventLoop實現,通常只是提供簡單的優先級設置能力即可。

總的來說,個人覺得Mode的設計是比較雞肋的。

子線程的Runloop

默認地,子線程並不會開啓Runloop。想一想,這是非常合理的。開啓了Runloop後,即使在沒有事件時,線程也會進入阻塞等待的狀態,需要的時候那叫保活,不需要的時候就是白佔資源了。因此,子線程默認情況下是肯定不能開啓Runloop的。而沒有開啓Runloop的情況下,涉及到Runloop的異步調用就都不能用了,主要就是NSTimer,performSelector:afterDelay:,網絡請求的回調等。

解決方案是通過[[NSRunLoop currentRunLoop] run]啓動當前線程的runloop。

跟Runloop相關的特性

  1. autoreleasepool:每次runloop開始時進入pool,結束時離開pool。從而在未顯式聲明autoreleasepool的時候釋放autorelease對象。
  2. 界面更新:操作UI時會把對應的UIView/CALayer標記爲待處理,在每次迭代即將結束時會執行實際的繪製。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章