Run Loops官方文檔翻譯(一)

Run Loops

Run loops是與線程相關的基礎框架的一部分。一個run loop是一個循環,在這個循環中你可以分配任務,並協調安排接收到的各種事件。run loop存在的目的是使線程在有任務的時候處於工作狀態,並且當沒有任務的時候使線程處於休眠狀態.
Run loop的管理不完全是自動的,你仍然需要設計跟線程有關的代碼,以便在適當的時候啓動運行循環,並響應傳入的事件。
Cocoa和Core Foundation都提供一些與run loop相關的對象來幫助你配置和管理線程的run loop。你的應用程序不需要顯式的創建這些對象;每一個線程,包括主線程都有一個相關聯的run loop對象。子線程的run loop對象需要手動開啓,而系統的app framework會在app啓動時候 自動設置並開啓主線程的run loop。

Run Loop剖析

run loop就像它的名字含義一樣。它是一個線程可以進入其中並用來運行事件處理程序以響應傳入事件的循環。我們可以通過代碼控制循環,換句話說,我們可以在代碼中用for循環或者while循環控制run loop。在循環內部,我們可以用run loop對象去運行事件處理代碼,這些代碼可以接收外部事件,並且調用已經綁定到run loop對象的回調函數。
run loop接收來自兩種不同類型的源的事件。輸入源提供異步事件,通常是來自另一個線程或不同應用程序的消息。定時器源提供同步事件,發生在預定時間或重複間隔。這兩種類型的源都使用應用程序特別指定的處理程序來處理到達的事件。
下圖顯示了run loop和各種源的結構。輸入源將異步事件傳遞給相應的處理程序,並導致runUntilDate:方法(在與線程關聯的NSRunLoop對象上調用)退出。定時器源將事件傳遞到其處理程序例程,但不會導致run loop退出。
圖片.png
除了處理輸入源之外,run loop同時也生成了有關run loop各種狀態的通知。註冊run loop的observers可以收到這些通知,並且可以在線程上做一些其他操作。通過Core Foundation框架可以在線程上添加observer

Run Loop Modes

Run Loop Modes是要監視的輸入源和定時器的集合,以及要通知的運行循環observer的集合。每次運行run loop時,都需要(顯式或隱式)指定特定的“模式”來運行。在這次run loop過程中,只有與該模式相關的源會被監聽,並允許其發送事件。 (類似地,只有與該模式相關的observer纔會被通知run loop的狀態。)與其他模式相關的源會保留髮送給它們事件,直到隨後以適當的模式運行。
在我們的代碼中,我們可以通過名字來區分模式。 Cocoa和Core Foundation都定義了一個默認模式和若干個常用模式,以及用於在代碼中標誌這些模式的字符串。我們可以通過自定義一個字符串作爲名字來定義模式。雖然這個字符串可以是任意的,但是模式的內容卻不是任意的。爲了確保創建的模式(models)可用,必須向其中添加至少一個或多個輸入源(input sources),定時器(timers),或者run loop觀察者(run loop observer)。
我們可以使用模式在特定的運行循環模式下過濾掉不需要的源事件。在大多數情況下,我們是在系統已經定義好的”default”模式下。然而,模態面板可能以”模態”模式運行(注:這段不是很懂,不瞭解模態面板和模態模式)。在這種模式下,只有與該模式相關的源纔會將事件傳遞給線程。對於其他次級線程,我們可以通過自定義models用來阻值低優先級的源在限時操作中傳遞事件。

注意:模式基於事件的來源而不是事件的類型進行區分。 例如,你不會使用模式來匹配手指觸摸事件或鍵盤輸入事件。 你可以使用模式來監聽一組不同的端口,計時器臨時的暫停,或者更改當前正在監視的源(source)以及run loop observer。

輸入源(input source)

輸入源異步發送事件到線程中。輸入源的類型決定了事件的類型,通常有兩種事件。Port-based輸入源監聽應用的Mach端口。自定義輸入源監聽自定義事件。就run loop而言,輸入源是基於Mach端口或者其他端口並不重要。
當創建一個輸入源後,可以將它添加run loop的一個或多個model中。在任何時刻,models決定了哪些輸入源處於被監聽狀態。大多數時間,run loop處於default model下,但是可以切換到custom model下。如果一個輸入源沒有被添加到當前正在運行的模式下,它的所有事件都會被阻塞,直到run loop切換到該模式下。
下面幾部分介紹了其中一些輸入源(input source)

Port-Based Sources

Cocoa框架和Core Foundation框架內部都提供了與port相關的對象和方法,可以用來創建輸入源。在Cocoa框架中,不用直接創建輸入源,只需要創建一個port對象,並使用NSPort提供的方法將這個對象加入到run loop即可。port對象會爲你管理輸入源的創建和配置。
在Core Foundation中,你必須手動創建port和它的輸入源。在這兩種情況下,你可以使用與port類型(CFMachPortRef, CFMessagePortRef, or CFSocketRef)相關的函數去創建合適的對象。

Custom Input Sources

爲了創建一個自定義輸入源,你必須使用Core Foundation中與CFRunLoopSourceRef相關的函數。你使用幾個回調函數來配置一個輸入源。Core Foundation框架會在不同時刻調用這些函數以配置源、處理任何輸入事件,並且當source被移除run loop時候會被自動釋放掉。
除了需要定義回調函數之外,還必須定義事件傳遞機制。這部分的源代碼在單獨的線程上運行,負責爲輸入源提供數據,並在數據準備好處理時用信號通知它。 事件傳遞機制取決於你,但不必過於複雜。

Cocoa Perform Selector Sources

除了基於端口的源之外,Cocoa還定義了一個自定義輸入源,允許你在任何線程上執行選擇器。 像基於端口的源一樣,執行選擇器請求在目標線程上被序列化,減輕了在一個線程上運行多個方法時可能發生的許多同步問題。 與基於端口的源不同,執行選擇器源在執行選擇器之後將自身從運行循環中移除。

注意:在OS X v10.5之前,執行選擇器源主要用於向主線程發送消息,但在OS X v10.5及更高版本和iOS中,可以使用它們向任何線程發送消息。

在另一個線程上執行選擇器時,目標線程必須有一個活動的運行循環。 對於你創建的線程,這意味着等待直到你的代碼明確地啓動運行循環。 因爲主線程啓動自己的運行循環,所以只要應用程序調用應用程序委託的applicationDidFinishLaunching:方法,就可以開始在該線程上發出調用。 運行循環每次通過循環處理所有排隊的執行選擇器調用,而不是在每個循環迭代中處理一個。

timers

定時器源在未來的預設時間將事件同步傳遞給你的線程。定時器是線程通知自己做某事的一種方式。例如,search field可以使用定時器來在用戶的連續輸入之間經過一定的時間後啓動自動搜索。這個延遲時間的使用使用戶有機會在開始搜索之前儘可能多地輸入想要的搜索字符串
雖然它會生成基於時間的通知,但計時器不是實時機制。像輸入源一樣,定時器與run loop的特定模式相關聯。如果一個定時器不在當前被運行循環監視的模式下,它不會被觸發,直到處於定時器支持的一種模式下運行時纔會被觸發。同樣的,如果一個定時器在run loop處於執行處理程序的過程中觸發,定時器會等待下一次通過run loop來調用它的處理程序。如果run loop根本沒有運行,定時器不會啓動。
你可以將定時器配置爲僅生成一次或重複生成事件,重複的計時器會根據預訂的觸發時間而不是實際的觸發時間自動重新安排時間。例如,如果計時器在某個時間以及之後的每5秒鐘觸發一次,則即使實際的觸發被延遲,計劃的觸發時間將總是以原來的5秒的時間間隔下降。如果觸發時間延遲過多,以至於錯過了一個或多個預定的觸發時間,則計時器僅在錯過的時間段內觸發一次。在錯過的時間觸發之後,定時器重新計劃下一個預定的觸發時間。

Run Loop Observers

與源不同,run loop observers是當某些同步事件或者異步事件發生的時候被觸發。run loop observers在run loop中的一個特定狀態被觸發。你可以通過run loop observer去讓線程在run loop的開始狀態或者休眠狀態執行特定的動作。你可以將run loop observer與run loop中的以下幾種狀態關聯:
1.即將進入run loop
2.run loop即將處理timer
3.run loop即將處理輸入源
4.run loop即將進入休眠狀態
5.run loop已經被喚醒,但還沒處理喚醒它的事件
6.run loop退出循環
你可以通過Core Foundation去添加run loop observer。如果要創建run loop observer,首先創建CFRunLoopObserverRef的實例。這個實例會將你自定義的回調函數與它關心的run loop狀態關聯。
同timer類似,run loop observer可以被一次或重複使用。一個一次性的run loop observer會在它觸發後將自己從run loop移除。但是重複使用的observer不會這樣。你可以在創建observer的時候指定它是否重複。

run loop事件隊列

每次運行run loop時候,線程的run loop會處理即將發生的事件,並且會爲所有observer生成通知。它會以特定的順序執行這些操作,順序如下:
1.通知觀察者run loop已經進入循環
2.通知觀察者任何準備好的timer即將被觸發
3.通知觀察者任何port端口之外的輸入源將要觸發
4.觸發任何非基於端口(source0)的輸入源事件
5.如果有基於端口的輸入源事件(source1),立刻處理這些事件,並跳轉到第9步
6.通知觀察者,線程將要休眠
7.將線程置於休眠狀態,直到下列事件發生:
* port端口事件到達
* timer 觸發
* 到達run loop超時時間
* run loop被顯示喚醒

8.通知觀察者線程剛被喚醒
9.處理喚醒run loop的事件
* 如果是用戶定義的timer觸發時間到了,處理timer事件,並重新開啓run loop。跳轉到第二步
* 如果是輸入源事件,處理這個事件
* 如果run loop是被顯式調用,並且還未到run loop的超時時間,重新開啓runloop 跳轉到第二步

10.通知observer,run loop已經退出循環。
圖片.png
由於timer和輸入源的觀察者是先收到通知,在觸發事件。所以通知的時間和實際事件觸發的時間之間可能存在差距。如果這些事件之間的時間很關鍵,則可以通過使用休眠和從休眠中醒來的通知來獲得實際事件發生之前的時間。

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