NSRunLoop類聲明的編程接口對象管理輸入源。NSRunLoop對象處理鼠標和鍵盤等輸入來源來自窗口系統的事件,NSPort對象,NSConnection對象。還一個NSRunLoop對象流程NSTimer事件編程
- (void)addTimer:(NSTimer *)timer forMode:(NSString *)mode;xcode
+ (NSRunLoop *)currentRunLoop;異步
+ (NSRunLoop *)mainRunLoop NS_AVAILABLE(10_5, 2_0);函數
消息處理模式,對消息處理過程進行了更好的抽象和封裝oop
1.輸入事件來源:輸入源(input source)和定時源(timer source).使用程序的某一特定的處理例程來處理到達的事件。線程
當你建立輸入源,你須要將其分配給runloop中的一個或多個模式。模式只會在特定事件影響監聽的源。大多數狀況下,runloop運行在默認模式下,可是你也可使其運行在自定義模式下。若某一源在當前模式下不被監聽,那麼任何其生成的消息只在runloop運行在其關聯的模式下才會被傳遞。code
傳遞異步事件,一般消息來自於其餘線程或程序。輸入源傳遞異步消息給相應的處理例程,並調用runUntilDate:方法來退出(在線程裏面相關的NSRunLoop對象調用)orm
1.1 基於端口的輸入源由內核自動發送對象
cocoa和core fundation內置支持使用端口相關的對象和函數來建立的基於端口的源。就是說,在cocoa裏,歷來不須要直接建立輸入源。只要簡單的建立端口對象,並使用NSPort的方法把該端口添加到runloop。端口隊形會本身處理建立和配置輸入源。接口
在core foundation中,你必須人工建立端口和它的runloop源。可使用端口相關的函數(CFMachPortRef,CFMessagePortRef,CFSocketRef)
來建立合適的對象。
如何建立一個基於端口的輸入源, 將其添加到runloop並啓動:
1.2自定義輸入源須要人工從其餘線程發送
自定義的輸入源須要人工從其餘線程發送。
爲了建立自定義輸入源,必須使用Core Foundation裏面的CFRunLoopSourceRef類型相關的函數來建立。你可使用回調函數來配置自定義輸入源。Core Fundation會在配置源的不一樣地方調用回調函數,處理輸入事件,在源從run loop移除的時候清理它。
除了定義在事件到達時自定義輸入源的行爲,你也必須定義消息傳遞機制。源的這部分運行在單獨的線程裏面,並負責在數據等待處理的時候傳遞數據給源並通知它處理數據。消息傳遞機制的定義取決於你,但最好不要過於複雜。建立並啓動自定義輸入源的示例以下
1.3 cocoa上的Selector源
除了基於端口的源,cocoa定義了自定義輸入源,容許你在任何線程執行selector方法。和基於端口的源同樣,執行selector請求會在目標線程上序列化,減緩許多在線程上容許方法容易引發的同步問題。不像基於端口的源,一個selector執行完後會自動從runloop裏面移除。
當在其餘線程上面執行selector時,目標線程需有一個活動的runloop。對於你建立的線程,這意味着線程在你顯示的啓動runloop以前是不會執行selector方法的,而是一直處於休眠狀態。
1.4定時源
定時源在預設的時間點同步方式傳遞消息,這些消息都會發生在特定時間或者重複的時間間隔。定時源則直接傳遞消息給處理例程,不會當即退出run loop。
須要注意的是,儘管定時器能夠產生基於時間的通知,但它並非實時機制。和輸入源同樣,定時器也和你的run loop的特定模式相關。若是定時器所在的模式當前未被run loop監視,那麼定時器將不會開始直到run loop運行在相應的模式下。相似的,若是定時器在run loop處理某一事件期間開始,定時器會一直等待直到下次run loop開始相應的處理程序。若是run loop再也不運行,那定時器也將永遠不啓動。
建立定時器源有兩種方法,
二。runloop觀察者
2.1 源是在合適的同步或異步事件發生時觸發,而run loop觀察者則是在run loop自己運行的特定時候觸發。你可使用run loop觀察者來爲處理某一特定事件或是進入休眠的線程作準備。你能夠將run loop觀察者和如下事件關聯:
1>. Runloop入口
2>. Runloop什麼時候處理一個定時器
3>. Runloop什麼時候處理一個輸入源
4>. Runloop什麼時候進入睡眠狀態
5>. Runloop什麼時候被喚醒,但在喚醒以前要處理的事件
6>. Runloop終止
2.2 和定時器相似,在建立的時候你能夠指定run loop觀察者能夠只用一次或循環使用。若只用一次,那麼在它啓動後,會把它本身從run loop裏面移除,而循環的觀察者則不會。定義觀察者並把它添加到run loop,只能使用Core Fundation。下面的例子演示瞭如何建立run loop的觀察者
三。runloop的事件隊列
每次運行run loop,你線程的run loop對會自動處理以前未處理的消息,並通知相關的觀察者。具體的順序以下:
由於定時器和輸入源的觀察者是在相應的事件發生以前傳遞消息,因此通知的時間和實際事件發生的時間之間可能存在偏差。若是須要精確時間控制,你可使用休眠和喚醒通知來幫助你校對實際發生事件的時間。
由於當你運行run loop時定時器和其它週期性事件常常須要被傳遞,撤銷run loop也會終止消息傳遞。典型的例子就是鼠標路徑追蹤。由於你的代碼直接獲取到消息而不是經由程序傳遞,所以活躍的定時器不會開始直到鼠標追蹤結束並將控制權交給程序。
Run loop能夠由run loop對象顯式喚醒。其它消息也能夠喚醒run loop。例如,添加新的非基於端口的源會喚醒run loop從而能夠當即處理輸入源而不須要等待其餘事件發生後再處理。
從這個事件隊列中能夠看出:
①若是是事件到達,消息會被傳遞給相應的處理程序來處理, runloop處理完當次事件後,run loop會退出,而無論以前預約的時間到了沒有。你能夠從新啓動run loop來等待下一事件。
②若是線程中有須要處理的源,可是響應的事件沒有到來的時候,線程就會休眠等待相應事件的發生。這就是爲何run loop能夠作到讓線程有工做的時候忙於工做,而沒工做的時候處於休眠狀態。
四。何時使用runloop
僅當在爲你的程序建立輔助線程的時候,你才須要顯式運行一個run loop。Run loop是程序主線程基礎設施的關鍵部分。因此,Cocoa和Carbon程序提供了代碼運行主程序的循環並自動啓動run loop。IOS程序中UIApplication的run方法(或Mac OS X中的NSApplication)做爲程序啓動步驟的一部分,它在程序正常啓動的時候就會啓動程序的主循環。相似的,RunApplicationEventLoop函數爲Carbon程序啓動主循環。若是你使用xcode提供的模板建立你的程序,那你永遠不須要本身去顯式的調用這些例程。
對於輔助線程,你須要判斷一個run loop是不是必須的。若是是必須的,那麼你要本身配置並啓動它。你不須要在任何狀況下都去啓動一個線程的run loop。好比,你使用線程來處理一個預先定義的長時間運行的任務時,你應該避免啓動run loop。Run loop在你要和線程有更多的交互時才須要,好比如下狀況:
若是你決定在程序中使用run loop,那麼它的配置和啓動都很簡單。和全部線程編程同樣,你須要計劃好在輔助線程退出線程的情形。讓線程天然退出每每比強制關閉它更好。