Run Loops基礎概念篇二

When Would You Use a Run Loop?

你唯一要使用run loop,就是當你要在application中創(chuàng)建線程的時候。你Application的主線程是架構很重要的一部分。所以,iOS系統(tǒng)為app的提供了runloop代碼,并自動開始。在iOS中運行Main loop作為app啟動步驟的一部分。

對于其他的線程,你需要考慮是否必須要使用run loop。如果有需要,配置并自己啟動它。你不需要在每個線程中都啟用run loop。例如,如果你使用一個線程來執(zhí)行一些很長時間的運行和已經(jīng)準備好的任務,你可能要避免使用它。Run loop的目的主要是用來解決線程間的通信。例如,如果你打算做下面的事情,你需要開啟一個run loop。

  • 使用port或者自定義input source來和其他線程通訊
  • 在線程中使用timer
  • 在Cocoa application使用performSelector…方法
  • 維持線程來執(zhí)行周期性的任務

如果你選擇使用run loop,配置和設置就是接下來要做的??v觀整個線程編碼過程,你應該有一個明確的認識在什么時候退出線程,這比強制退出好很多。關于如何配置和退出run loop的信息在這Using Run Loop Objects。

Using Run Loop Objects

run loop對象提供了主要的接口來添加input sources,timers,run-loop observers到run loop中再運行它。每個線程有一個和它相關的run loop object。在Cocoa中,這個對象是NSRunLoop的實例。在應用的底層,它是CFRunLoopRef類的指針。

Getting a Run Loop Object

獲取當前線程的runloop對象,你使用下面的一種方式:

盡管它們不是無縫橋接的類,當需要的時候你可以從NSRunLoop對象獲得一個CFRunLoopRef指針。NSRunLoop類定義了一個getCFRunLoop方法,它返回了一個CFRunLoopRef類型,這你可以傳入到Core Foundation中。因為兩個對象指向同一個run loop,需要的時候你可以混合使用。

Configuring the Run Loop

當你在自定義線程中跑run loop的時候,你至少添加一個input source或timer到run loop中,如果一個run loop沒有任何source來監(jiān)聽,當你嘗試運行它的時候,它會立即退出。如何向run loop中添加一個source,請看這里Configuring Run Loop Sources。

除了裝載sources,你也可以裝載run loop observers,使用它們來監(jiān)聽run loop的執(zhí)行步驟。為了裝載run loop observer,你創(chuàng)建了一個CFRunLoopObserverRef指針,并且使用CFRunLoopAddObserver方法并把它添加到你的run loop中。Run loop observer必須要使用Core Foundation來創(chuàng)建。

下面展示了在一個線程中,添加一個run loop observer到run loop中的主要的代碼部分。

- (void)threadMain
{
    //The application use garbage collection,so no autorelease pool is needed.
    NSRunLoop *myRunLoop = [NSRunLoop currentRunLoop];  
    //Create a run loop observer and attach it to the run loop.
    CFRunLoopObserverContent 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);        
    }   
    //Create and schedule the timer.
        [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(doFireTimer:) userInfo:nil repeats:YES];
        NSInteger loopCount = 10;
    do 
    {
        //Run the run loop 10 times to let the timer fire
        [myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
        loopCount--;
    }
    while(loopCount);
}

當為一個活躍的線程配置run loop的時候,至少添加一個input source到接受的消息。盡管你能夠只需要通過一個timer來啟動run loop中,但是,一旦timer觸發(fā)之后,它就會無效的,這會引起run loop的退出。傳入一個repeat的timer可以保持run loop運行很長一段時間。但是將會周期性的調用觸發(fā)的timer來喚醒你的線程。這是維持線程中run loop的另一種方式,一個input source等待事件的到達,讓你的線程處于睡眠中,直到它被喚醒。

Starting the Run Loop

一個run loop必須裝載至少一個input source或者timer來。如果沒有,run loop立即退出。
下面有幾種辦法開啟一個run loop。

  • 無條件的啟動run loop
  • 設置一個超時時間啟動run loop
  • 通過一種特殊的模式啟動run loop

無條件的啟動runloop是最簡單的,但是這也是最不推薦的一種方式。無條件的啟動你的runloop,把線程作為runloop的一個參數(shù)。這種方式對runloop的控制權很小。你可以添加或移除input source和timers,唯一停止runloop的方式就是kill。這種啟動方式?jīng)]有辦法在自定義mode。

除了無條件的啟動一個runloop,使用time out value來開始一個runloop更好。當你使用time out value,runloop運行直到事件到達或者超時。如果事件抵達,事件會被派發(fā)到一個handler來處理,然后run loop退出。你的code又能夠重新開始runloop去處理下一個事件。如果超時,你可以簡單的重啟runloop,或者利用這段時間做任何需要的事。

除了time out value,你也可以使用一個指定的mode來運行runloop。Modes和timeout value并不是互斥的。當運行runloop的時候也可以同時使用它們。Modes限制了source的type。詳細被描述在Run Loop Modes。

下面的code主要部分展示了runloop的基本結構。本質上,你添加你的input source和timer到runloop。重復調用其中一個routine來開始一個run loop。每次run loop routine 返回的時,你檢查run lop是否滿足某些條件導致退出thread。例子中使用Core Foundation的run loop routines,以至于它能檢查返回的結果并檢測run loop為什么退出。你也能使用NSRunLoop的方法以一種相似的方式來運行runloop,如果你正在使用Cocoa框架中的NSRunLoop,它是不需要檢查return value的。

- (void)skeletonThreadMain {
    //Set up an autorelease pool here if not using garbage collection.
    BOOL done = NO;
    //Add your sources or timer to the run loop and do any other setup
    do
    {
        //Start the run loop but return after earch source is handled
        SInt32 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode,10,YES)
        
        //If a source explicitly stopped the run loop,or if there no sources or timers,go head and exit.
        if ((result == kCFRunLoopRunStopped) || (result == kCFRunLoopRunFinished))
        done = YES;
        
        //Check for any other exit conditions here and set the done variable as needed
    }
    while(!done);
    //Clean up code here. Be sure to release any allocated autorelease pools.
}

遞歸運行run loop也是可能的。換句話說,你能夠調用CFRunLoopRunCFRunLoopRunInMode,任何NSRunLoop的方法,從input source 或者 timer的handler routine開始runloop。當這樣做的時候,你可以使用你想要的mode來運行內嵌的runloop。包括被outer run loop正在使用的mode。

Exiting the Run Loop

有兩種辦法使runloop在處理事件之前退出。

  • Runloop配置一個time out時間
  • 主動告訴Runloop停止

使用time out時間是一種比較好的選擇。在退出之前,指定一個time out時間讓runloop完成所有的任務,包括向runloop observer發(fā)送通知。

使用CFRunLoopStop方法停止一個runloop,產(chǎn)生一個類似于超時的結果。使用runloop發(fā)送完剩下的runloop通知然后退出。唯一不同的就是只有當你無條件的啟動Runloop的時候才能使用這種方式退出。

盡管移除runloop的input source和timers可以引起runloop的退出,但是這不是一種穩(wěn)定的方式。一些系統(tǒng)一般添加input source到run loop來處理需要的事件。因為你的code不需要關心這些input source。所以你不能移除input sources,讓run loop退出。

Thread Safety and Run Loop Objects

線程安全取決于你使用操作runloop的API。在Core Foundation中的方法一般是線程安全的,可以被任何其他線程調用。如果你正在執(zhí)行操作,這些操作改變了runloop的配置。無論什么時候,這樣做任然是一個很好的實踐。

Cocoa的NSRunLoop類。Cocoa的NSRunLoop和Core Foundation不一樣,不是線程安全的。如果你使用NSRunLoop類來修改你的runloop,你應該在原來的線程中做這些操作。在其他線程添加一個input source或者timer到原來runloop可能引起crash,或者一個意想不到的行為。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容