iOS RunLoop(一)

級別: ★★☆☆☆
標籤:「iOS」「RunLoop」「線程常駐」
做者: 陳彬
審校: QiShare團隊php

前言:
這篇文章主要內容是介紹RunLoop的一些概念以及用法:使用RunLoop建立常駐線程、自定義輸入源進行線程通訊等。同時藉此機會但願可以和你們一塊兒討論RunLoop相關的知識,加深對RunLoop的理解。git

1、RunLoop是什麼?

RunLoop是與線程相關的基礎架構中的一部分,它是一個處理事件的循環(線程進入這個循環,運行事件處理程序來響應傳入的事件),RunLoop的目的是當有事件須要處理時,線程是活躍的、忙碌的,當沒有事件後,線程進入休眠。github

RunLoop結構以及事件來源:微信

一個RunLoop包含若干個Mode,每一個Mode包含若干個Source/Timer/Observer/Port。當啓動一個RunLoop時會先指定一個Mode,檢查指定Mode是否存在以及Mode中是否含有SourceTimer,若是Mode不存在或者Mode中無SourceTimer,認爲該Mode是一個空的ModeRunLoop就直接退出。架構

Input Source:
  • Port-Based Sources:監聽AppMach Port,由內核發出信號,輸入源收到信號後,執行相關的例程。oop

  • Custom Input Sources:監聽自定義的輸入源,須要在其它線程手動發送信號,輸入源收到信號後,執行相關的例程。學習

  • Cocoa Perform Selector Sources:Cocoa中自定義的輸入源,目的是在不一樣線程中執行任務,同一線程中的任務是順序執行的,當任務執行完成後系統會自動移除這個源。(注意:在目標線程中執行任務時,這個目標線程必須有活躍的RunLoop線程

Timer Source:

時間源會在預設的時間同步傳遞事件給對應的線程,計時器是線程通知本身作某事的一種方式。翻譯

計時器並非真正的實時的,當計時器未處於RunLoop當前監聽的Mode,那麼計時器是不會計時調度任務的,只有RunLoop當前監聽的Mode是計時器關聯的Mode時,計時器纔會開始執行任務,例如:NSTimer添加至主線程RunLoopDefaultMode中,此時滑動TableView/ScrollView時,RunLoop會切換至TrackMode,計時器是不會調度任務的。3d

若是RunLoop在執行一個例程時,計時器觸發了,那麼計時器會等待RunLoop將該例程執行完成,在下一次的循環中處理。在RunLoop未運行狀況下,計時器永遠不會觸發任務。

2、RunLoop怎麼使用?

應用啓動時,會自動在主線程上設置運行RunLoop,因此不須要在主線程上顯示的啓動RunLoop,無需調用[[NSRunLoop currentRunLoop] runUntilDate:]這些方法。那麼若是咱們顯示的在主線程中調用RunLooprun方法會出現什麼結果呢?經過Demo中顯示,主線程中顯示啓動RunLoop會影響當前事件處理,可是因爲RunLoop並無中止,因此其餘事件可以正常接收和處理。

而子線程也不併是必需要設置運行RunLoop才能執行任務,好比說只是簡單在子線程中處理個耗時任務等,以下場景是須要啓動RunLoop的:

  1. 使用NSPort或者自定義輸入源與其它線程通訊。
  2. 在線程上使用計時器。
  3. 在一個Cocoa應用中使用performSelector相關方法。
  4. 使線程常駐,在該線程按期執行任務。

正如前言中所說,本文主要說明線程常駐和自定義輸入源線程通訊。

線程常駐:

方式一:無條件的啓動RunLoop是最簡單的選擇,但它也是最不可取的選擇,它會將線程置於永久循環中,這樣幾乎沒法控制RunLoop自己,雖然能夠添加和刪除輸入源和計時器,但中止RunLoop的惟一方法是殺死RunLoop。(以上內容是經過Google翻譯的官網內容可能理解有些誤差屆時還望指正,事實上我在作實驗的過程當中,發現使用NSThreadcancel方法是沒法中止RunLoop的,cancel方法是更改線程的取消狀態,指示它應該退出。在當前線程下執行[NSThread exit]方法,退出了該線程,但demo中的LongLifeThreadViewController仍然未被釋放)

方式二:啓動RunLoop時設定時限,RunLoop將一直運行直到事件到達或分配的時間到期。若是事件到達,則將該事件分派給處理程序進行處理,而後退出這次RunLoop。能夠經過從新啓動RunLoop處理下一個事件。一樣若是分配的時間到期,也能夠從新啓動RunLoop來處理。這種方式能夠指定RunLoopMode,官網力薦。

自定義輸入源線程通訊:

定義輸入源:

  1. 提供輸入源要處理的信息。
  2. 接收到事件時的執行例程。
  3. 輸入源加到RunLoop時的執行例程。
  4. 輸入源失效時的執行例程。

我的感受能夠根據我的需求決定是否實現第三、4兩條內容。(注意定義輸入源只能經過CoreFoundation提供的對應API實現,其中的回調例程由C語言實現)

RunLoop上安裝輸入源:若是實現了上述的第3條內容時,將自定義的輸入源添加到RunLoop時,就會回調輸入源對應的schedule實現例程。

向輸入源發送信號:輸入源在接收到信號後,會執行對應的perform例程,perform例程就是對應事件處理程序。(注意若是線程處於休眠狀態,要喚醒線程,不然該事件沒法被處理。


結語:
關於RunLoop的內容還有不少,好比:RunLoopModesRunLoopObserverNSPortNSTimer等等,固然還有RunLoop的源碼,這些內容在此並未列出,若有感興趣的小夥伴能夠先行花時間去探索、學習,到時能夠一塊兒交流、討論。

源碼地址:QiRunLoopDemo1


小編微信:可加並拉入《QiShare技術交流羣》。

關注咱們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公衆號)

推薦文章:
iOS 經常使用調試方法:LLDB命令
iOS 經常使用調試方法:斷點
iOS 經常使用調試方法:靜態分析
iOS消息轉發
iOS 自定義拖拽式控件:QiDragView
iOS 自定義卡片式控件:QiCardView
iOS Wireshark抓包
iOS Charles抓包
奇舞週刊

相關文章
相關標籤/搜索