Run loops是線程相關的的基礎架構的一部分。一個run loop就是一個事件處理的迴圈,用來不停的調度工作以及處理輸入事件。使用run loop的目的是讓你的線程在有工作的時候忙於工作,而沒工作的時候處於休眠狀態。
Run loop的管理並不完全自動的。你仍然需要設計你的線程代碼在合適的時候啟動run loop並正確響應輸入事件。Cocoa和Core Fundation都提供了run loop objects來協助配置和管理你線程的run loop。你的應用程式不需要顯式的建立這些對象(run loop objects);每個線程,包括程式的主線程都有與之對應的run loop object。只有輔助線程才需要顯式的運行它的run loop。在Carbon和Cocoa程式中,主線程會自動建立並運行它run loop,作為一般應用程式啟動過程的一部分。
以下各部分提供更多關於run loops以及如何為你的應用程式配置它們。關於run loop object的額外資訊,參閱NSRunLoop Class Reference和CFRunLoop Reference文檔。 1.1 Run Loop剖析
Run loop本身聽起來就和它的名字很像。它是一個迴圈,你的線程進入並使用它來運行響應輸入事件的事件處理常式。你的代碼要提供實現迴圈部分的控制語句,換言之就是要有while或for迴圈語句來驅動run loop。在你的迴圈中,使用run loop object來運行事件處理代碼,它響應接收到的事件並啟動已經安裝的處理常式。
Run loop接收輸入事件來自兩種不同的來源:輸入源(input source)和定時源(timer source)。輸入源傳遞非同步事件,通常訊息來自於其他線程或程式。定時源則傳遞同步事件,發生在特定時間或者重複的時間間隔。兩種源都使用程式的某一特定的處理常式來處理到達的事件。
圖3-1顯示了run loop的概念結構以及各種源。輸入源傳遞非同步訊息給相應的處理常式,並調用runUntilDate:方法來退出(線上程裡面相關的NSRunLoop對象調用)。定時源則直接傳遞訊息給處理常式,但並不會退出run loop。
Figure 3-1 Structure of a run loop and its sources
除了處理輸入源,run loops也會產生關於run loop行為的通知(notifications)。註冊的run loop觀察者(run-loop Observers)可以收到這些通知,並線上程上面使用它們來做額外的處理。你可以使用Core Foundation在你的線程註冊run-loop觀察者。
下面部分介紹更多關於run loop的構成,以及其啟動並執行模式。同時也提及在處理事件中不同時間產生的通知。 1.1.1 Run Loop 模式
Run loop模式是所有要監視的輸入源和定時源以及要通知的run loop註冊觀察者的集合。每次運行你的run loop,你都要指定(無論顯示還是隱式)其運行個模式。在run loop運行過程中,只有和模式相關的源才會被監視並允許他們傳遞事件訊息。(類似的,只有和模式相關的觀察者會通知run loop的進程)。和其他模式關聯的源只有在run loop運行在其模式下才會運行,否則處於暫停狀態。
通常在你的代碼中,你可以通過指定名字來標識模式。Cocoa和Core foundation定義了一個預設的和一些常用的模式,在你的代碼中都是用字串來標識這些模式。當然你也可以給模式名稱指定一個字串來自訂模式。雖然你可以給模式指定任意名字,但是模式的內容則不能是任意的。你必須添加一個或多個輸入源,定時源或者run loop的觀察者到你建立的模式中讓他們有價值。
通過指定模式可以使得run loop在某一階段過濾來源於源的事件。大多數時候,run loop都是運行在系統定義的預設模式上。但是模態面板(modal panel)可以運行在 “modal”模式下。在這種模式下,只有和模式面板相關的源才可以傳遞訊息給線程。對於輔助線程,你可以使用自訂模式在一個時間周期操作上屏蔽優先順序低的源傳遞訊息。
注意:模式區分基於事件的源而非事件的種類。例如,你不可以使用模式只選擇處理滑鼠按下或者鍵盤事件。你可以使用模式監聽連接埠,暫停定時器或者改變其他源或者當前模式下處於監聽狀態run loop觀察者。
表1-3列出了Cocoa和Core Foundation定義的標準模式,並且介紹何時使用他們。名稱那列列出了你用來在你代碼中指定模式實際的常量。
Table 3-1 Predefined run loop modes
Mode |
Name |
Description |
Default |
NSDefaultRunLoopMode(Cocoa) kCFRunLoopDefaultMode (Core Foundation) |
The default mode is the one used for most operations. Most of the time, you should use this mode to start your run loop and configure your input sources. |
Connection |
NSConnectionReplyMode(Cocoa) |
Cocoa uses this mode in conjunction with NSConnection objects to monitor replies. You should rarely need to use this mode yourself. |
Modal |
NSModalPanelRunLoopMode(Cocoa) |
Cocoa uses this mode to identify events intended for modal panels. |
Event tracking |
NSEventTrackingRunLoopMode(Cocoa) |
Cocoa uses this mode to restrict incoming events during mouse-dragging loops and other sorts of user interface tracking loops. |
Common modes |
NSRunLoopCommonModes(Cocoa) kCFRunLoopCommonModes (Core Foundation) |
This is a configurable group of commonly used modes. Associating an input source with this mode also associates it with each of the modes in the group. For Cocoa applications, this set includes the default, modal, and event tracking modes by default. Core Foundation includes just the default mode initially. You can add custom modes to the set using theCFRunLoopAddCommonMode function. |
1.1.2 輸入源
輸入源非同步發送訊息給你的線程。事件來源取決於輸入源的種類:基於連接埠的輸入源和自訂輸入源。基於連接埠的輸入源監聽程式相應的連接埠。自訂輸入源則監聽自訂的事件來源。至於run loop,它不關心輸入源的是基於連接埠的輸入源還是自訂的輸入源。系統會實現兩種輸入源供你使用。兩類輸入源的區別在於如何顯示:基於連接埠的輸入源由核心自動發送,而自訂的則需要人工從其他線程發送。
當你建立輸入源,你需要將其分配給run loop中的一個或多個模式。模式只會在特定事件影響監聽的源。大多數情況下,run loop運行在預設模式下,但是你也可以使其運行在自訂模式。若某一源在當前模式下不被監聽,那麼任何其產生的訊息只在run loop運行在其關聯的模式下才會被傳遞。
基於連接埠的輸入源
Cocoa和Core Foundation內建支援使用連接埠相關的對象和函數來建立的基於連接埠的源。例如,在Cocoa裡面你從來不需要直接建立輸入源。你只要簡單的建立連接埠對象,並使用NSPort的方法把該連接埠添加到run loop。連接埠對象會自己處理建立和配置輸入源。
在Core Foundation,你必須人工建立連接埠和它的run loop源.在兩種情況下,你都可以使用連接埠相關的函數(CFMachPortRef,CFMessagePortRef,CFSocketRef)來建立合適的對象。
更多例子關於如何設定和配置一個自訂連接埠源,參閱“配置一個基於連接埠的輸入源”部分。
自訂輸入源
為了建立自訂輸入源,必須使用Core Foundation裡面的CFRunLoopSourceRef類型相關的函數來建立。你可以使用回呼函數來配置自訂輸入源。Core Fundation會在配置源的不同地方調用回呼函數,處理輸入事件,在源從run loop移除的時候清理它。
除了定義在事件到達時自訂輸入源的行為,你也必須定義訊息傳遞機制。源的這部分運行在單獨的線程裡面,並負責在資料等待處理的時候傳遞資料給源並通知它處理資料。訊息傳遞機制的定義取決於你,但最好不要過於複雜。
關於建立自訂輸入源的例子,參閱“定義一個自訂輸入源”。關於自訂輸入源的資訊,參閱CFRunLoopSource Reference。
Cocoa 執行 Selector 的源
除了基於連接埠的源,Cocoa定義了自訂輸入源,允許你在任何線程執行selector。和基於連接埠的源一樣,執行selector請求會在目標線程上序列化,減緩許多線上程上允許多個方法容易引起的同步問題。不像基於連接埠的源,一個selector執行完後會自動從run loop裡面移除。
注意:在Mac OS X v10.5之前,執行selector多半可能是給主線程發送訊息,但是在Mac OS X v10.5及其之後和在iOS裡面,你可以使用它們給任何線程發送訊息。
當在其他線程上面執行selector時,目標線程須有一個活動的run loop。對於你建立的線程,這意味著線程在你顯式的啟動run loop之前處於等待狀態。由於主線程自己啟動它的run loop,那麼在程式通過委託調用applicationDidFinishlaunching:的時候你會遇到線程調用的問題。因為Run loop通過每次迴圈來處理所有隊列的selector的調用,而不是通過loop的迭代來處理selector。
表3-2列出了NSObject中可在其它線程執行的selector。由於這些方法時定義在NSObject中,你可以在任何可以訪問Objective-C對象的線程裡面使用它們,包括POSIX的所有線程。這些方法實際上並沒有建立新的線程執行selector。
Table 3-2 Performing selectors on other threads
Methods |
Description |
performSelectorOnMainThread:withObject:waitUntilDone: performSelectorOnMainThread:withObject:waitUntilDone:modes: |
Performs the specified selector on the application’s main thread during that thread’s next run loop cycle. These methods give you the option of blocking the current thread until the selector is performed. |
performSelector:onThread:withObject:waitUntilDone: performSelector:onThread:withObject:waitUntilDone:modes: |
Performs the specified selector on any thread for which you have an NSThreadobject. These methods give you the option of blocking the current thread until the selector is performed. |
performSelector:withObject:afterDelay: performSelector:withObject:afterDelay:inModes: |
Performs the specified selector on the current thread during the next run loop cycle and after an optional delay period. Because it waits until the next run loop cycle to perform the selector, these methods provide an automatic mini delay from the currently executing code. Multiple queued selectors are performed one after another in the order they were queued. |
cancelPreviousPerformRequestsWithTarget: cancelPreviousPerformRequestsWithTarget:selector:object: |
Lets you cancel a message sent to the current thread using theperformSelector:withObject:afterDelay:orperformSelector:withObject:afterDelay:inModes:method. |
關於更多介紹這些方法的資訊,參閱NSObject Class Reference。