[OC RunLoop_翻譯]一、介紹 & 二、剖析運作循環
[OC RunLoop_翻譯]三、 什麼時候使用運作循環 & 四、使用運作循環對象
[OC RunLoop_翻譯]五、配置運作循環源
注:pdf翻譯文檔百度雲下載下傳連結,密碼:zcs2
注: _Run Loops _連結
一、介紹
運作循環是與線程相關聯的基本基礎結構的一部分。運作循環是
事件處理循環
,可用于安排工作并協調收到的事件的接收。運作循環的
目的
是在
有工作要做時讓線程忙
,而在
沒有工作時讓線程進入睡眠狀态
。
運作循環管理不是完全自動的。您仍然必須設計線程的代碼以在适當的時候啟動運作循環并響應傳入的事件。
Cocoa
和
Core Foundation
均
提供運作循環對象
,以幫助您配置和管理線程的運作循環。您的應用程式
不需要顯式建立
這些對象。
每個線程(包括應用程式的主線程)都有一個關聯的運作循環對象
。但是,
隻有輔助線程需要顯式地運作其運作循環
。在應用程式啟動過程中,應用程式架構會自動在主線程上設定并運作運作循環。
以下各節提供有關運作循環以及如何為應用程式配置循環的更多資訊。有關運作循環對象的其他資訊,請參見 NSRunLoop Class Reference和 CFRunLoop Reference。
二、剖析運作循環
運作循環如其名一樣。它是
線程進入并用于運作事件處理程式以響應傳入事件的循環
。您的代碼提供了用于實作運作循環實際循環部分的控制語句,換句話說,您的代碼
提供了驅動運作循環的while或for循環
。在循環中,您可以使用運作循環對象來“運作”事件處理代碼,以接收事件并調用已安裝的處理程式。
運作循環從兩種不同類型的源接收事件。
輸入源傳遞異步事件
,通常是來自另一個線程或其他應用程式的消息。
計時器源傳遞同步事件
,這些事件
在計劃的時間或重複的間隔發生
。兩種類型的源都使用特定于應用程式的處理程式例程來處理事件到達時的事件。
圖3-1顯示了運作循環和各種源的概念結構。輸入源将異步事件傳遞給相應的處理程式,并導緻runUntilDate:方法(線上程的關聯NSRunLoop對象上調用)退出。計時器源向其處理程式例程傳遞事件,但不會導緻運作循環退出。
Figure 3-1 運作循環的結構及其源

除了處理輸入源之外,運作循環還生成有關運作循環行為的通知。注冊的runloop觀察者可以接收這些通知,并使用它們線上程上進行其他處理。您可以使用Core Foundation線上程上安裝runloop觀察器。
以下章節提供了有關運作循環元件及其運作模式的更多資訊。它們還描述了在處理事件期間的不同時間生成的通知。
2-1、運作循環模式
runloop
模式
是
要監視的輸入源和計時器的集合
,以及
要通知的運作循環觀察者的集合
。每次運作runloop時,都可以(顯式或隐式)指定運作的特定“模式”。在運作循環的整個過程中,僅監視與該模式關聯的源,并允許其傳遞事件。 (類似地,隻有與該模式相關聯的觀察者才被告知運作循環的進度。)與其他模式相關聯的源将保留任何新事件,直到随後以适當的模式通過該循環。
在代碼中,您可以通過名稱辨別模式。 Cocoa和Core Foundation都定義了預設模式和幾種常用模式,以及用于在代碼中指定這些模式的字元串。您可以通過簡單地為模式名稱指定自定義字元串來定義自定義模式。盡管您配置設定給自定義模式的名稱是任意的,但這些模式的内容卻不是。您必須確定将一個或多個輸入源,計時器或運作循環觀察器添加到您建立的任何模式中,以使其有用。
您可以使用模式從運作循環的特定周遊中過濾掉有害來源的事件。大多數時候,您将需要
在系統定義的“預設”模式下運作runloop
。但是,模式面闆可以在“模式”模式下運作。在這種模式下,隻有與模式面闆相關的源才會将事件傳遞給線程。對于輔助線程,您可以使用自定義模式來防止低優先級源在時間緊迫的操作期間傳遞事件。
注:
模式的差別取決于事件的來源,而不是事件的類型
。例如,您不會使用模式來隻比對滑鼠按下事件或鍵盤事件。您可以使用模式來監聽不同的端口集,暫時挂起計時器,或者更改目前正在監視的源和運作循環觀察器。
表3-1列出了Cocoa和Core Foundation定義的标準模式,以及何時使用該模式的說明。name列列出了用于在代碼中指定模式的實際常量。
**Table 3-1 **預定義的運作循環模式
Mode | Name | Description |
---|---|---|
Default | NSDefaultRunLoopMode (Cocoa)、kCFRunLoopDefaultMode (Core Foundation) | 。大多數時候,您應該使用此模式來啟動運作循環并配置輸入源。 |
Connection | NSConnectionReplyMode (Cocoa) | Cocoa将此模式與NSConnection對象結合使用來監視響應。你應該很少需要自己使用這種模式。 |
Modal | NSModalPanelRunLoopMode (Cocoa) | Cocoa使用此模式來 |
Event tracking | NSEventTrackingRunLoopMode (Cocoa) | |
Common modes | NSRunLoopCommonModes (Cocoa)、kCFRunLoopCommonModes (Core Foundation) | 這是一組 。将輸入源與此模式相關聯還将其與組中的每個模式相關聯。對于 應用程式,此集合 。最初,Core Foundation僅包括預設模式。您可以使用CFRunLoopAddCommonMode 函數将自定義模式添加到集合中 |
2-2、輸入源
輸入源以異步方式向線程傳遞事件。事件的源取決于輸入源的類型
,通常是兩種類型之一。
基于端口的輸入源監視應用程式的Mach端口
自定義輸入源監視事件的自定義源
。就運作循環而言,輸入源是基于端口還是自定義的并不重要。系統通常實作兩種類型的輸入源,您可以按原樣使用。這兩個信号源之間的
唯一差別
是它們是
如何發出信号的
基于端口的源由核心自動發出信号
,
自定義源必須從另一個線程手動發出信号
建立輸入源時,可以将其配置設定給運作循環的一種或多種模式。模式影響在任何給定時刻監控的輸入源。
大多數情況下,您可以在預設模式下運作runloop
,但也可以指定自定義模式。如果輸入源不在目前監視的模式下,它生成的任何事件都将被保留,直到運作循環以正确的模式運作。
以下各節描述了一些輸入源。
2-3、基于端口的源
Cocoa和corefoundation為使用端口相關的對象和函數建立基于端口的輸入源提供了内置支援。例如,在
Cocoa
中,根本不必直接建立輸入源。您隻需
建立一個port對象
并使用
NSPort
方法将該端口添加到運作循環中。
port對象為您處理所需輸入源的建立和配置
在
CoreFoundation
中,必須手
動建立端口及其運作循環源
。在這兩種情況下,都可以使用與端口不透明類型(CFMachPortRef, CFMessagePortRef, 或 CFSocketRef)關聯的函數來建立适當的對象。
有關如何設定和配置自定義基于端口的源的示例,請參閱配置基于端口的輸入源。
2-4、自定義輸入源
要
建立自定義輸入源
,必須使用與
Core Foundation
中的 CFRunLoopSourceRef不透明類型關聯的函數。您可以使用幾個回調函數配置自定義輸入源。Core Foundation在不同的點調用這些函數來配置源代碼,處理任何傳入事件,并在源代碼從運作循環中删除時将其銷毀。
除了定義事件到達時自定義源的行為外,還
必須定義事件傳遞機制
。源代碼的這一部分在
單獨的線程上運作
,負責
為輸入源提供其資料
,并在
準備好處理資料時向其發出信号
。事件傳遞機制由您決定,但不必過于複雜。
有關如何建立自定義輸入源的示例,請參見 定義自定義輸入源。有關自定義輸入源的參考資訊,請參見 CFRunLoopSource Reference
2-5、Cocoa執行選擇器(selector)源
除了基于端口的源代碼外,
Cocoa
還定義了一個
自定義輸入源
允許您在任何線程上執行選擇器
。與基于端口的源一樣,
perform selector請求
在目标線程上序列化,進而減輕了在一個線程上運作多個方法時可能出現的許多同步問題。與基于端口的源不同,
執行選擇器源在執行其選擇器後将自身從運作循環中移除
注意:在OS X v10.5之前,執行選擇器源主要用于向主線程發送消息,但在OS X v10.5及更高版本以及iOS中,您可以使用它們向任何線程發送消息。
在另一個線程上執行選擇器時,目标線程必須具有活動的運作循環。對于您建立的線程,這意味着等待直到您的代碼顯式啟動運作循環。但是,由于主線程啟動其自己的運作循環,是以隻要應用程式調用應用程式委托的**applicationdiffinishlaunching:**方法,就可以開始對該線程發出調用。運作循環每次通過循環處理所有排隊的執行選擇器調用,而不是在每次循環疊代期間處理一個
表3-2列出了
NSObject
上定義的方法,這些方法可
用于在其他線程上執行選擇器(perform selector)
。由于這些方法是在NSObject上聲明的,是以
可以在任何有權通路Objective-C對象的線程中使用
它們,包括POSIX線程。這些方法實際上并不建立新的線程來執行選擇器。
Table 3-2 在其他線程上執行選擇器
Methods | |
---|---|
performSelectorOnMainThread:withObject:waitUntilDone:、performSelectorOnMainThread:withObject:waitUntilDone:modes: | 在應用程式的主線程的下一個運作循環周期中對該線程執行指定的選擇器。這些方法提供了在執行選擇器之前阻塞目前線程的選項。 |
performSelector:onThread:withObject:waitUntilDone:、performSelector:onThread:withObject:waitUntilDone:modes: | 對具有 NSThread 對象的任何線程執行指定的選擇器。這些方法提供了在執行選擇器之前阻塞目前線程的選項。 |
performSelector:withObject:afterDelay:、performSelector:withObject:afterDelay:inModes: | 在下一個運作循環周期中以及可選的延遲時間之後,在目前線程上執行指定的選擇器。因為它一直等到下一個運作循環周期執行選擇器,是以這些方法提供了目前執行代碼的最小自動延遲。多個排隊的選擇器按照排隊的順序依次執行。 |
cancelPreviousPerformRequestsWithTarget:、cancelPreviousPerformRequestsWithTarget:selector:object: | 使您可以使用 performSelector:withObject:afterDelay:或performSelector:withObject:afterDelay:inModes: 方法取消發送到目前線程的消息。 |
有關這些方法的詳細資訊,請參見NSObject Class Reference.
2-6、定時器源(NSTimer)
計時器源在将來的預設時間将事件
同步傳遞
到線程。
計時器是線程通知自身執行某項操作的一種方式
。例如,搜尋字段可以使用計時器在使用者連續按鍵之間經過一定時間後啟動自動搜尋。使用這個延遲時間,使用者就有機會在開始搜尋之前鍵入盡可能多的所需搜尋字元串。
盡管計時器生成基于時間的通知,但它
不是一種實時機制
。與輸入源一樣,
計時器與運作循環的特定模式相關聯
如果計時器未處于運作循環目前監視的模式,則在以計時器支援的模式之一運作運作循環之前,它不會觸發
。類似地,
如果在運作循環正在執行處理程式例程的過程中觸發計時器,則計時器将等到下一次通過運作循環調用其處理程式例程
如果運作循環根本沒有運作,計時器就不會觸發
您可以将計時器配置為
僅生成一次事件
或
重複生成事件
重複計時器根據預定的觸發時間而不是實際觸發時間自動重新排程自己
。例如,如果一個計時器被安排在某個特定時間觸發,并且此後每隔5秒觸發一次,那麼即使實際觸發時間被延遲,計劃的觸發時間也将始終落在原來的5秒時間間隔上。
如果觸發時間延遲太久,以緻錯過了一個或多個預定的觸發時間,則對于錯過的時間段,計時器隻觸發一次。在為錯過的時間段觸發後,計時器将重新安排為下一個預定的觸發時間
有關配置計時器源的更多資訊,請參見配置計時器源。有關參考資訊,請參見NSTimer Class Reference 或 CFRunLoopTimer Reference。
2-7、運作循環觀察者(RunLoop Observer)
與在發生适當的異步或同步事件時觸發的源不同。
運作循環觀察器在運作循環本身執行期間的特殊位置觸發
。您可以使用運作循環觀察器來準備線程以處理給定事件,或者線上程進入睡眠狀态之前準備線程。您可以
将運作循環觀察者與
運作循環中的
以下事件相關聯
:
- 運作循環的
入口
- 當運作循環
将要處理計時器時
-
将要處理輸入源時
-
即将進入睡眠狀态時
-
,但需要在處理該事件之前将其喚醒。已喚醒
- 從運作循環中
退出
您可以
使用Core Foundation将運作循環觀察器添加到應用程式
。要建立運作循環觀察器,請建立
CFRunLoopObserverRef
透明類型的新執行個體。此類型跟蹤自定義回調函數及其感興趣的活動。
與計時器類似,
運作循環觀察器可以使用一次或重複使用
。一次觸發的觀察者在觸發後将自己從運作循環中删除,而重複的觀察者仍保持連接配接。您可以指定觀察者在建立時是運作一次還是重複運作。
有關如何建立運作循環觀察器的示例,請參閱 配置運作循環。有關參考資訊,請參閱 CFRunLoopObserver Reference。
2-8、事件的運作循環序列(重要!!!)
每次運作它時,線程的運作循環都會
處理未決事件
,并為所有附加的觀察者
生成通知
。它的
執行順序
非常明确,如下所示:
-
- 通知觀察者已進入運作循環。
-
- 通知觀察者準備就緒的計時器即将觸發。.
-
- 通知觀察者任何不基于端口的輸入源都将被觸發。
-
- 觸發所有準備觸發的非基于端口的輸入源。
-
- 如果基于端口的輸入源已準備好并等待啟動,請立即處理事件。轉到步驟9。
-
- 通知觀察者線程即将休眠。.
-
- 使線程進入睡眠狀态,直到發生以下事件之一:
- 基于端口的輸入源的事件到達.
- 計時器觸發.
- 運作循環設定的逾時值已過期.
- 運作循環被顯式喚醒
-
- 通知觀察者線程剛剛醒來。
-
- 處理挂起的事件。.
- 如果觸發了使用者定義的計時器,請處理計時器事件并重新啟動循環。轉到步驟2。.
- 如果觸發了輸入源,則傳遞事件
- 如果運作循環已顯式喚醒但尚未逾時,請重新啟動循環。轉到步驟2.
-
- 通知觀察者運作循環已退出.