Run Loops是與線(xiàn)程想關(guān)聯(lián)的基礎(chǔ)部分。一個(gè)Run Loop就是事件處理循環(huán),它是用來(lái)調(diào)度和協(xié)調(diào)接收到的事件處理。使用Run Loop的目的,就是使得線(xiàn)程有工作需要做時(shí)可以忙碌起來(lái),而當(dāng)沒(méi)有事可做時(shí),又可以使得線(xiàn)程睡眠。
Run Loop管理不都是自動(dòng)的。我們必須手動(dòng)設(shè)計(jì)線(xiàn)程代碼,在合適的時(shí)候來(lái)啟動(dòng)Run Loop,并回應(yīng)到來(lái)的事件。Cocoa和Core Foundation都提供了run loop對(duì)象來(lái)幫助我們配置和管理線(xiàn)程的run loop。我們的應(yīng)用沒(méi)有必要顯式地創(chuàng)建這些對(duì)象;每個(gè)線(xiàn)程,包括應(yīng)用程序的主線(xiàn)程,都有一個(gè)與之關(guān)聯(lián)的run loop。只有子線(xiàn)程才需要顯式地運(yùn)行其run loop。App會(huì)將自動(dòng)配置和來(lái)運(yùn)行主線(xiàn)程的run loop的任務(wù)作為應(yīng)用程序啟動(dòng)處理的一部分。
對(duì)于想要更深入地了解Run Loop Objects,閱讀NSRunLoop Class Reference、CFRunLoop Reference
成都創(chuàng)新互聯(lián)是專(zhuān)業(yè)的豐潤(rùn)網(wǎng)站建設(shè)公司,豐潤(rùn)接單;提供成都網(wǎng)站建設(shè)、成都做網(wǎng)站,網(wǎng)頁(yè)設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專(zhuān)業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行豐潤(rùn)網(wǎng)站開(kāi)發(fā)網(wǎng)頁(yè)制作和功能擴(kuò)展;專(zhuān)業(yè)做搜索引擎喜愛(ài)的網(wǎng)站,專(zhuān)業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來(lái)合作!
Run Loop就像它的名字一樣,它使得線(xiàn)程進(jìn)入事件循環(huán),能對(duì)到來(lái)的事件啟動(dòng)事件處理。你的代碼中提供了流程控制說(shuō)一句來(lái)實(shí)現(xiàn)run loop實(shí)實(shí)在在的循環(huán)部分,換句話(huà)說(shuō),你的代碼提供了while或者for循環(huán)來(lái)驅(qū)動(dòng)run loop。在你的循環(huán)中,你使用run loop對(duì)象在事件到達(dá)時(shí),運(yùn)行事件處理的代碼并調(diào)起已安裝的處理程序。
Run Loop接收來(lái)自?xún)煞N不同類(lèi)型的源(sources)的事件:
輸入源:異步傳遞事件,通常是來(lái)自不同的線(xiàn)程或不同的應(yīng)用的消息。輸入源異步傳遞事件到對(duì)應(yīng)的處理程序和在線(xiàn)程關(guān)聯(lián)的NSRunLoop對(duì)象調(diào)起runUntilDate:方法來(lái)退出事件處理。
Timer源:同步地傳遞事件,發(fā)生在每個(gè)定時(shí)器調(diào)用或周期性地調(diào)用。Timer源傳遞事件到他們的處理程序,但是不會(huì)調(diào)用run loop來(lái)退出處理。
這兩種源在事件到達(dá)時(shí)都使用應(yīng)用程序特定的處理程序來(lái)處理事件。
如下圖所示,展示了run loop和不同的源的概述結(jié)構(gòu)。
p_w_picpath
除了處理輸入源之外,run loops還發(fā)出關(guān)于run loop行為的通知。我們可以注冊(cè)成為run loop的觀察者,就可以接收這些通知和使用它在線(xiàn)程上做一些額外處理。我們可以使用Core Foundation在對(duì)應(yīng)的線(xiàn)程上注冊(cè)成為run loop的觀察者。
Run Loop模式是一個(gè)監(jiān)視輸入源和定時(shí)器的集合和注冊(cè)成為run loop的觀察者的集合。每次要運(yùn)行run loop,都需要顯示或隱式地指定某種運(yùn)行的mode。只有與這種指定的mode關(guān)聯(lián)的源才會(huì)被監(jiān)視和允許傳遞他們的事件,同樣地,只有與這種模式關(guān)聯(lián)的觀察者都會(huì)收到run loop行為變化的通知。與其它模式想關(guān)聯(lián)的源,直到隨后在合適的模式通過(guò)循環(huán)后,都會(huì)接收到新的事件(比如,將timer加入run loop default模式下,當(dāng)滾動(dòng)時(shí),timer不會(huì)收到回調(diào),直到停止?jié)L動(dòng)回到default模式下)。
在我們的代碼中,我們通過(guò)名稱(chēng)來(lái)唯一標(biāo)識(shí)mode。在Cocoa和Core Foundation中都定義了default模式和幾個(gè)常用的模式,都是通過(guò)字符串名稱(chēng)來(lái)指定。我們也可以自定義模式,但是我們需要手動(dòng)添加至少一個(gè)input source/timers/observers。
我們可以通過(guò)使用mode來(lái)過(guò)濾掉我們不希望接收到來(lái)自不想要的通過(guò)run loop的源。大部分情況下,我們都是使用系統(tǒng)定義的default模式。對(duì)于子線(xiàn)程,我們可以使用自定義模式在關(guān)鍵性操作時(shí)阻止低優(yōu)先級(jí)的源傳遞事件。
注意:Modes是通過(guò)事件源來(lái)區(qū)分,而不是事件類(lèi)型來(lái)區(qū)分。比如說(shuō),我們不能使用mode來(lái)匹配只有mouse-down事件或者只有鍵盤(pán)事件。我們可以使用modes來(lái)監(jiān)聽(tīng)不同系統(tǒng)的端口,臨時(shí)掛起定時(shí)器,甚至改變正在被監(jiān)視的sources和run loop觀察者。
表格.png
輸入源異步傳遞事件到你的線(xiàn)程。事件的源由輸入源的類(lèi)型來(lái)決定,也就是兩種源中的其中一種:
Port-based:基于端口號(hào)的輸入源監(jiān)聽(tīng)?wèi)?yīng)用程序的Mach端口。
Custom Input Sources:自定義輸入源監(jiān)聽(tīng)自定義的事件源。
系統(tǒng)通常實(shí)現(xiàn)了這兩種輸入源。唯一的不同點(diǎn)是它們是如何被發(fā)出信號(hào)的。port-based源是由內(nèi)核(kernel)自動(dòng)發(fā)出信號(hào),而custom sources必須手動(dòng)從其它線(xiàn)程發(fā)出信號(hào)。
當(dāng)我們創(chuàng)建輸入源時(shí),可以指定mode。Modes會(huì)影響任何時(shí)刻被監(jiān)視的輸入源。大部分情況下,我們都讓run loop在default mode下運(yùn)行,但是也可以指定自定義的mode。如果一個(gè)輸入源不是當(dāng)前所監(jiān)視的model,它所產(chǎn)生的任何事件都會(huì)被保留直接進(jìn)入正常的mode。
Cocoa和Core Foundation提供了內(nèi)建支持,可以使用與port相關(guān)的對(duì)象和函數(shù)來(lái)創(chuàng)建基于端口的輸入源。舉個(gè)例子,在Cocoa中永遠(yuǎn)不需要手動(dòng)創(chuàng)建輸入源。我們只需要簡(jiǎn)單地創(chuàng)建一個(gè)port對(duì)象和使用NSPort的方法。port對(duì)象為我們處理所需要的輸入源的創(chuàng)建和配置。
在Core Foundation中,我們必須手動(dòng)創(chuàng)建port和source。在這兩種情況下,我們可以使用與port opaque type關(guān)聯(lián)的函數(shù)(CFMessagePortRef, or CFSocketRef) 來(lái)創(chuàng)建合適的對(duì)象。
在Core Foundation中,要?jiǎng)?chuàng)建自定義輸入源,我們必須使用與CFRunLoopSourceRef關(guān)聯(lián)的函數(shù)。我們配置自定義輸入源可以使用幾個(gè)回調(diào)函數(shù)。Core Foundation會(huì)在不同點(diǎn)回調(diào)這些函數(shù)來(lái)配置source,處理任何到達(dá)的事件和銷(xiāo)毀已從run loop移除的source。
除了定義在事件到達(dá)時(shí)自定義源的行為之外,我們也必須定義事件傳遞機(jī)制。這部分源運(yùn)行在單獨(dú)的線(xiàn)程,負(fù)責(zé)提供輸入源的數(shù)據(jù),當(dāng)數(shù)據(jù)準(zhǔn)備好可以處理時(shí),signaling(通知相關(guān)線(xiàn)程)這個(gè)消息。事件傳遞機(jī)制是我們自己來(lái)決定,但是不需要過(guò)于復(fù)雜。
除了基于端口的源之外,Cocoa還定義了自定義輸入源允許我們?cè)谌我饩€(xiàn)程上執(zhí)行selector。就像port-based源一樣,執(zhí)行selector請(qǐng)求會(huì)在目標(biāo)線(xiàn)程上序列化,以減少在同一個(gè)線(xiàn)程中出現(xiàn)多個(gè)方法同步執(zhí)行的問(wèn)題。與port-based源不同的是,執(zhí)行selector源在執(zhí)行完畢后會(huì)自動(dòng)將自己從run loop中移除。
當(dāng)執(zhí)行在其它線(xiàn)程執(zhí)行selector時(shí),目標(biāo)線(xiàn)程必須要有運(yùn)行的run loop。當(dāng)我們創(chuàng)建線(xiàn)程時(shí),這意味著直到啟動(dòng)了run loop都會(huì)顯式地執(zhí)行selector代碼。
Run Loop每次經(jīng)過(guò)一個(gè)循環(huán),就會(huì)處理隊(duì)列中所有的selector,而不僅僅是處理一個(gè)。
方法和說(shuō)明。
Timer源在未來(lái)設(shè)定的時(shí)間會(huì)同步地傳遞事件到你的線(xiàn)程。Timers是線(xiàn)程通知自己去做一些事情的一種方式。比如說(shuō),搜索框可以使用定時(shí)器來(lái)初始化在一定時(shí)間就自動(dòng)搜索,以便提供更多地聯(lián)想詞給用戶(hù)。
盡管它發(fā)送基于時(shí)間的通知,但定時(shí)器并不是一種實(shí)時(shí)的機(jī)制。像輸入源一樣,定時(shí)器只有與run loop的mode一樣才會(huì)發(fā)送通知。如果timer在run loop中并不是所被監(jiān)視的mode,它不會(huì)觸發(fā)定時(shí)器,直到run loop的mode與timer所支持的mode一樣。
同樣地,如果run loop正在處理中,timer已經(jīng)fire了,這時(shí)候會(huì)被中斷,直到下一次通過(guò)run loop才會(huì)調(diào)志處理程序。如果run loop已經(jīng)不再運(yùn)行了,則timer永遠(yuǎn)不會(huì)再fire。
我們可以配置timer只產(chǎn)生事件一次或者重復(fù)產(chǎn)生。重復(fù)的timer會(huì)自動(dòng)根據(jù)調(diào)度的firing time自動(dòng)調(diào)度,而不是真實(shí)的firing time。比如說(shuō),如果一個(gè)timer在特定的時(shí)間調(diào)度,然后每5秒重復(fù)一次。如果firing time被延遲導(dǎo)致缺少一或多次調(diào)用,那么timer在缺失的周期中只會(huì)調(diào)用一次。
與sources在適當(dāng)時(shí)機(jī)異步或同步發(fā)出事件不同,observers在run loop本身執(zhí)行期間,會(huì)在特定的地方發(fā)出。你可能需要到run loop observers去準(zhǔn)備線(xiàn)程處理特定的事件或者在進(jìn)入睡眠之前。我們可以通過(guò)以下事件來(lái)關(guān)聯(lián)run loop observers:
進(jìn)入run looprun loop將要處理timer run loop將要處理輸入源 run loop將要進(jìn)入睡眠 run loop被喚醒,但是還沒(méi)有處理事件 退出run loop
我們可以通過(guò)Core Foundation來(lái)添加run loop observers。要?jiǎng)?chuàng)建run loop observer,可以通過(guò)CFRunLoopObserverRef來(lái)創(chuàng)建新的實(shí)例。這個(gè)類(lèi)型會(huì)跟蹤你所定義的回調(diào)函數(shù)和所感興趣的活動(dòng)。
與timers類(lèi)型,run-loop observers可以使用一次或者重復(fù)多次。一次性的observer會(huì)在fire之后自動(dòng)從run loop移除,而重復(fù)性的observer會(huì)繼續(xù)持有。
本小節(jié)講的是RunLoop事件順序。每次運(yùn)行它,你的線(xiàn)程的run loop處理待處理的事件和給所有attached observers發(fā)出通知。處理的順序如下:
1.通知observers run loop已經(jīng)進(jìn)入2.通知observers timers準(zhǔn)備要fire3.通知observers有不是基于port-based的輸入源即將要fire4.fire任何已經(jīng)準(zhǔn)備好的non-port-based輸入源5.如果port-based輸入源準(zhǔn)備好且等待fire,則立即處理這個(gè)事件。然后進(jìn)入步驟96.通知observers線(xiàn)程即將進(jìn)入睡眠7.讓線(xiàn)程進(jìn)入睡眠,直到以下任何一種事件到達(dá): * port-based輸入源有事件到達(dá) * timer fire * run loop超時(shí) * run loop被顯式喚醒8.通知observers線(xiàn)程被喚醒9.處理待處理的事件: * 如果用戶(hù)定義的timer fired了,處理timer事件并重新啟動(dòng)循環(huán)。進(jìn)入步驟2 * 如果輸入源fired了,則傳遞事件 * 如果run loop被顯式喚醒,但是又未超時(shí),則重啟循環(huán),進(jìn)入步驟210.通知observers run loop退出
由于observer對(duì)timer和輸入源的通知會(huì)在事件真正發(fā)生之前被傳遞,這樣就產(chǎn)生了間隙。如果這個(gè)間隙是很關(guān)鍵的,那么我們可以通過(guò)使用sleep和awake-from-sleep通知來(lái)幫助我們糾正這個(gè)時(shí)間間隔問(wèn)題。
什么時(shí)候應(yīng)該使用run loop呢?
只有當(dāng)我們需要?jiǎng)?chuàng)建子線(xiàn)程的時(shí)候,才會(huì)需要到顯示地運(yùn)行run loop。應(yīng)用程序的主線(xiàn)程的run loop是應(yīng)用啟動(dòng)的基礎(chǔ)任務(wù),在啟動(dòng)時(shí)就會(huì)自動(dòng)啟動(dòng)run loop。所以我們不需要手動(dòng)啟動(dòng)主線(xiàn)程的run loop。
對(duì)于子線(xiàn)程,我們需要確定線(xiàn)程是否需要run loop,如果需要,則配置它并啟動(dòng)它。我們并不問(wèn)題需要啟動(dòng)run loop的。比如說(shuō),如果我們開(kāi)一個(gè)子線(xiàn)程去執(zhí)行一些長(zhǎng)時(shí)間的和預(yù)先決定的任務(wù),我們可能不需要啟動(dòng)run loop。Run loop是用于那么需要在線(xiàn)程中有更多地交互的場(chǎng)景。比如說(shuō),我們會(huì)在下面的任何一種場(chǎng)景中需要開(kāi)啟run loop:
使用端口源或者自定義輸入源與其它線(xiàn)程通信 在線(xiàn)程中使用定時(shí)器 使用Cocoa中的任何performSelector…方法 保持線(xiàn)程來(lái)執(zhí)行周期性的任務(wù)
Run Loop對(duì)象給添加輸入源、定時(shí)器和觀察者到run loop提供了主接口。每個(gè)線(xiàn)程都有一個(gè)單獨(dú)的run loop與之關(guān)聯(lián)(對(duì)于子線(xiàn)程,若沒(méi)有調(diào)用過(guò)任何獲取run loop的方法是不會(huì)有run loop的,只有調(diào)用過(guò),才會(huì)創(chuàng)建或者直接使用)。
在Cocoa中,通過(guò)NSRunLoop來(lái)創(chuàng)建實(shí)例,在low-level應(yīng)用中,可以使用CFRunLoopRef類(lèi)型,它是指針。
通過(guò)以下兩種方式來(lái)獲取run loop對(duì)象:
在Cocoa中,使用[NSRunLoop currentRunLoop]獲取
使用CFRunLoopGetCurrent()函數(shù)獲取
在子線(xiàn)程運(yùn)行run loop之前,你必須至少添加一種輸入源或者定時(shí)器。如果run loop沒(méi)有任何的源需要監(jiān)視,它就會(huì)立刻退出。
除了添加sources之外,你還可以添加觀察者來(lái)檢測(cè)runloop不同的執(zhí)行狀態(tài)。要添加觀察者,可以使用CFRunLoopObserverRef指針類(lèi)型和使用CFRunLoopAddObserver函數(shù)來(lái)添加到run loop中。我們只能通過(guò)Core Foundation來(lái)創(chuàng)建run loop觀察者,即使是Cocoa應(yīng)用。
下面這段代碼展示主線(xiàn)程如何添加觀察者到run loop以及如何創(chuàng)建run loop觀察者:
- (void)threadMain { // 應(yīng)用程序使用垃圾收集,因此不需要autorelease池。 NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop]; // 創(chuàng)建一個(gè)運(yùn)行循環(huán)觀察者并將它附加到運(yùn)行循環(huán)。 CFRunLoopObserverContext context = {0, self, NULL, NULL, NULL}; CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context); if (observer) { CFRunLoopRef cfLoop = [myRunLoop getCFRunLoop]; CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode); } //創(chuàng)建和安排計(jì)時(shí)器。 [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(doFireTimer:) userInfo:nil repeats:YES]; NSInteger loopCount = 10; do { // 運(yùn)行運(yùn)行循環(huán)10次讓計(jì)時(shí)器。 [myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]]; loopCount--; } while (loopCount); }
在為長(zhǎng)時(shí)間存在的線(xiàn)程配置run-loop時(shí),最好添加至少一個(gè)輸入源來(lái)接收事件。盡管我們可以只用timer源,但是一旦timer調(diào)用后,經(jīng)常會(huì)被invalidate,這會(huì)導(dǎo)致run loop退出。
只有子線(xiàn)程才有可能需要啟動(dòng)run loop。Run loop必須至少有一種輸入源或者timer源來(lái)監(jiān)視。如果沒(méi)有任何源,則run loop會(huì)退出。
下面的幾種方式可以啟動(dòng)run loop:
無(wú)條件地:無(wú)條件進(jìn)入run loop是最簡(jiǎn)單的方式,但也是最不希望這么做的,因?yàn)檫@樣會(huì)導(dǎo)致run loop會(huì)進(jìn)入永久地循環(huán)??梢蕴砑?、刪除輸入源和timer源,但是只能通過(guò)kill掉run loop才能停止。而且還不能使用自定義mode。
限時(shí):與無(wú)條件運(yùn)行run loop不同,最好是給run loop添加一個(gè)超時(shí)時(shí)間。
在特定的mode:除了添加超時(shí)時(shí)間,還可以指定mode。
下面是運(yùn)行run loop的一段代碼:
- (void)skeletonThreadMain { //建立一個(gè)autorelease如果不使用垃圾收集池。 BOOL done = NO; //添加你的來(lái)源或計(jì)時(shí)器運(yùn)行循環(huán),做其他任何設(shè)置。 do {/ /啟動(dòng)運(yùn)行循環(huán)但每個(gè)源處理后返回。 SInt32 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, YES); / /如果源明確停止運(yùn)行循環(huán),或如果沒(méi)有源或計(jì)時(shí)器,然后退出。 if ((result == kCFRunLoopRunStopped) || (result == kCFRunLoopRunFinished)) done = YES; / /檢查任何其他退出條件和設(shè)置完成所需的變量。 } while (!done); / /清理代碼。一定要釋放任何生成自動(dòng)分配池。 }
有兩種方法使run loop在處理事件之前,退出run loop:
給run loop設(shè)定超時(shí)時(shí)間 告訴run loop要stop
設(shè)定超時(shí)時(shí)間是比較推薦的。我們可以通過(guò)CFRunLoopStop函數(shù)來(lái)停止run loop。
Core Foundation中的Run Loop API是線(xiàn)程安全的(以CF開(kāi)頭的API),而Cocoa中的NSRunLoop不是線(xiàn)程安全的。
下面是展示如何配置不同類(lèi)型的輸入源。
創(chuàng)建自定義輸入源涉及到以下部分:
想要處理的輸入源的信息
讓感興趣的客戶(hù)端知道如何聯(lián)系輸入源的調(diào)度程序
執(zhí)行任何客戶(hù)端發(fā)送的請(qǐng)求處理程序
使輸入源失效的取消程序
NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop]; / /創(chuàng)建和安排第一個(gè)定時(shí)器。NSDate* futureDate = [NSDate dateWithTimeIntervalSinceNow:1.0];NSTimer* myTimer = [[NSTimer alloc] initWithFireDate:futureDate interval:0.1 target:self selector:@selector(myDoFireTimer1:) userInfo:nil[myRunLoop addTimer:myTimer forMode:NSDefaultRunLoopMode];
或者使用Core Foundation:
CFRunLoopRef runLoop = CFRunLoopGetCurrent();CFRunLoopTimerContext context = {0, NULL, NULL, NULL, NULL};CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault, 0.1, 0.3, 0, 0, &myCFTimerCallback, &context);CFRunLoopAddTimer(runLoop, timer, kCFRunLoopCommonModes);
最后
本篇文章主要是官方文檔的部分翻譯版本,不過(guò)有很多無(wú)關(guān)的都省略了,而且有都轉(zhuǎn)換成筆者的語(yǔ)言來(lái)表達(dá)出來(lái),如果讀不懂,最好還是去看官方文檔吧。畢竟,英文與中文翻譯不管怎么翻譯都存在很大的問(wèn)題。
疑問(wèn)
官方文檔中提到,每個(gè)線(xiàn)程都有一個(gè)run loop與之關(guān)聯(lián)。但是實(shí)質(zhì)上子線(xiàn)程在沒(méi)有訪(fǎng)問(wèn)過(guò)run loop時(shí),是不存在的。當(dāng)訪(fǎng)問(wèn)時(shí),若不存在則創(chuàng)建run loop并放到全局?jǐn)?shù)組中。
文章標(biāo)題:IOS開(kāi)發(fā)需要知道的知識(shí)-RunLoops
轉(zhuǎn)載源于:http://aaarwkj.com/article46/jjipeg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站策劃、建站公司、商城網(wǎng)站、網(wǎng)站內(nèi)鏈、動(dòng)態(tài)網(wǎng)站、面包屑導(dǎo)航
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)