2014-10-30 61 views
2

我剛剛創建使用NSRunLoopNSTimer帶有計時器的後臺任務在我ViewController停止NSRunLoop在全局隊列

- (void)runBackgroundTask: (int) time{ 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 
     NSTimer* t = [NSTimer scheduledTimerWithTimeInterval:time target:self selector:@selector(startTrackingBg) userInfo:nil repeats:YES]; 
     [[NSRunLoop currentRunLoop] addTimer:t forMode:NSDefaultRunLoopMode]; 
     [[NSRunLoop currentRunLoop] run]; 
    }); 
} 

要調用一個函數將驗證令牌有效性等是否有可能從函數內部結束這個循環?例如:

-(void)startTrackingBg 
{ 
    if(TOKEN IS NOT VALID) 
    { 
     STOP_THREAD; 
     dispatch_sync(dispatch_get_main_queue(), ^{ 
      [self alertStatus:@"Session Lost!" :@"Error!"]; 
      [self popToLogin]; 
     }); 
    } 
} 

回答

3

一對夫婦的想法:

  1. 如果你看一下run的文檔,它們表明,解決您的問題的模式:如果沒有輸入源或定時器連接到

    運行循環,此方法立即退出;否則,它通過反覆調用runMode:beforeDate:來運行NSDefaultRunLoopMode中的接收器。換句話說,這種方法有效地開始了一個處理來自運行循環的輸入源和定時器的數據的無限循環。

    從運行循環中手動刪除所有已知輸入源和定時器並不能保證運行循環將退出。 OS X可以根據需要安裝和刪除額外的輸入源,以處理針對接收者線程的請求。這些來源因此可以防止運行循環退出。

    如果你想運行循環終止,你不應該使用這個方法。相反,使用其他運行方法之一,並在循環中檢查您自己的其他任意條件。一個簡單的例子是:

    BOOL shouldKeepRunning = YES;  // global 
    NSRunLoop *theRL = [NSRunLoop currentRunLoop]; 
    while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]); 
    

    其中shouldKeepRunning設置爲NO在程序中其他地方。

  2. 話說回來,如果我要開始另一個線程,我不會用了全局工作線程,而我只是實例化了我自己的NSThread

  3. 更重要的是,根據您在其他線程中要做的事情,通常比建立自己的運行循環要好得多。

    例如,如果我想有定時器運行東西在另一個隊列中,我會使用一個調度定時器來代替:

    @property (nonatomic, strong) dispatch_source_t timer; 
    

    然後實例,並開始派遣定時器源在您指定的GCD隊列中運行:

    dispatch_queue_t queue = dispatch_queue_create("com.domain.app.polltimer", 0); 
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); 
    dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), kPollFrequencySeconds * NSEC_PER_SEC, 1ull * NSEC_PER_SEC); 
    
    dispatch_source_set_event_handler(self.timer, ^{ 
        <#code to be run upon timer event#> 
    }); 
    
    dispatch_resume(self.timer); 
    

    或者,如果你想使用NSTimer,只是安排以主runloop,並有它調用調度那個費時的任務到後臺排隊時間的方法。但是,無論如何,我會避免增加第二個運行循環的開銷。

  4. 已經向您展示了更好的方法在後臺線程中使用計時器,現在您已經描述了這個意圖(輪詢服務器),實際上我建議不要使用計時器。當你想要以某個固定的時間間隔啓動一些​​動作時,定時器很有用。但在這種情況下,您可能想要在先前的請求完成後一段時間後啓動下一個服務器請求。因此,在先前請求完成塊,你可以這樣做:

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 20.0 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
        <#code to initiate next request#> 
    }); 
    
  5. 另外,我個人希望確保有一個非常令人信服的理由來輪詢服務器。輪詢似乎總是非常吸引人且合乎邏輯,但它對用戶的電池,CPU和數據計劃的使用極其繁重。在需要客戶機快速響應服務器更改的情況下,通常會有更好的體系結構(套接字,推送通知等)。

+0

我想創建一個後臺程序來驗證每X秒令牌的狀態(使我的REST調用服務器) – 2014-10-30 11:49:09

+0

@DiogoMendonça我更新了我的答案來說明如何您可以設置定時器來執行的東西在後臺隊列上不創建新的運行循環。順便說一句,雖然,你幾乎肯定不想在這種情況下使用計時器。你可能希望在事先請求完成之後手動'dispatch_after'(你不希望緩慢的服務器導致大量的請求積壓,例如,如果你每20秒請求一次,服務器需要60秒才能回覆任何一種或者原因,最終可能會有巨大的積壓)或者使用完全不同的體系結構(套接字,推送通知等)。 – Rob 2014-10-30 11:51:20

+0

我會嘗試找到dispatch_after的一些例子,但他們都似乎非常相似xD – 2014-10-30 12:26:15

0

你做一個調度異步,並在你添加一個計時器到runloop定期運行一個方法?您應該添加線程以確保您一次使用所有的並行系統。 ;-)

說真的:使用dispatch_after()並在塊內部做出決定,如果你想再做一次。