2016-01-22 71 views
4

所以這個網頁上有關於後臺執行的例子:https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html#//apple_ref/doc/uid/TP40007072-CH4-SW1,這裏是例子:蘋果編程指南iOS背景執行指南的例子?

- (void)applicationDidEnterBackground:(UIApplication *)application { 

    bgTask = [application beginBackgroundTaskWithName:@"MyTask" expirationHandler:^{ 

     // Clean up any unfinished task business by marking where you 
     // stopped or ending the task outright. 
     [application endBackgroundTask:bgTask]; 

     bgTask = UIBackgroundTaskInvalid; 
    }]; 

    // Start the long-running task and return immediately. 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 

     // Do the work associated with the task, preferably in chunks. 

     [application endBackgroundTask:bgTask]; 
     bgTask = UIBackgroundTaskInvalid; 
    }); 

} 

據說bgTask在類作爲變量定義。因此,每個類(對象)的實例都有一個bgTask屬性。如果applicationDidEnterBackground在異步塊完成之前被多次調用,是否存在競態條件的危險?我的意思是bgTask會改變它的值,而endBackgroundTask將被調用新的任務值,而不是舊值?

這裏不是更好的解決方案來做到這一點:

__block UIBackgroundTaskIdentifier bgTask; 

調用beginBackgroundTaskWithName過嗎?

+0

,你應該檢查是否bgTask是不是無效,並分配給它新的價值之前結束它。 –

+0

if(_bgTask!= UIBackgroundTaskInvalid) [[UIApplication sharedApplication] endBackgroundTask:_bgTask]; WEAK_SELF weakSelf = self; _bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^ {[UIApplication sharedApplication] endBackgroundTask:weakSelf.bgTask]; weakSelf.bgTask = UIBackgroundTaskInvalid; }]; –

回答

2

對於每個對象,有一個bgTask的實例,但這是AppDelegate,而不是一些常規的VC或對象。所以在技術上將只有一個bgTask實例在工作。

但是,這仍然會產生問題。因爲如果此方法被調用兩次,它將覆蓋bgTask的值。我的第一個想法是,在退出應用程序時,不止一次,所有以前的任務都會過期。但經過測試意識到情況並非如此(這是IMO的一件好事)。發生的是bgTask被覆蓋(如預期)並且新值被傳遞給第一個endBackgroundTask:調用。之後立即bgTask設置爲UIBackgroundTaskInvalid,將其清除並將清除的值傳遞給任何後續調用endBackgroundTask:。這顯然導致了不清潔的終止,因爲不是所有的唯一ID都會被終止,導致expiration處理器在任何左側的後臺任務上執行。

話雖這麼說,我相信你對使用局部變量的假設是正確的。如果你嘗試這個代碼(放在AppDelegateapplicationDidEnterBackground:):

__block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithName:@"MyTask" expirationHandler:^{ 
    // Clean up any unfinished task business by marking where you 
    // stopped or ending the task outright. 
    NSLog(@"Expired"); 
    [application endBackgroundTask:bgTask]; 
    bgTask = UIBackgroundTaskInvalid; 
}]; 

NSLog(@"Backgrounded: %@", @(bgTask)); 

// Start the long-running task and return immediately. 
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 

    // Do the work associated with the task, preferably in chunks. 
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
     NSLog(@"Done! %@", @(bgTask)); 
     [application endBackgroundTask:bgTask]; 
     bgTask = UIBackgroundTaskInvalid; 
    }); 
}); 

你會看到每個本地bgTask被分配一個獨特的價值和正確完成10秒後(按照dispatch_after調用)。

1

我認爲你遇到的問題如下:
應用程序發送到Background狀態,iOS調用applicationDidEnterBackground:
後臺任務已啓動,可能需要幾分鐘的時間。
在此期間,應用程序被再次激活,並再次發送到後臺,這就要求再次applicationDidEnterBackground:,並啓動其他後臺任務,從而可變bgTask將被重寫,如果它不是一個塊變量。這是對的。因此,bgTask確實應該是一個塊變量。
與此相關的問題是問題,你如何能完成後臺執行,如果多個後臺任務已啓動。一個例子,你如何做到這一點,給出here
這個想法是有一個變量來計算活動的後臺任務。只要所有這些完成,就可以終止後臺執行。

1

你是對的,第二次調用時,applicationDidEnterBackground會導致問題。但爲了再次調用該方法,應用程序首先需要再次放到前臺。所以解決方案很簡單。只需撥打您的到期處理從applicationWillEnterForeground

- (void)expireBackgroundTask { 
    // Clean up any unfinished task business by marking where you 
    // stopped or ending the task outright. 
    [application endBackgroundTask:bgTask];    
    bgTask = UIBackgroundTaskInvalid; 
} 

- (void)applicationDidEnterBackground:(UIApplication *)application { 

    bgTask = [application beginBackgroundTaskWithName:@"MyTask" expirationHandler:^{ 
     [self expireBackgroundTask]; 
    }]; 

    // Start the long-running task and return immediately. 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 

     // Do the work associated with the task, preferably in chunks. 

     [application endBackgroundTask:bgTask]; 
     bgTask = UIBackgroundTaskInvalid; 
    }); 

} 

- (void)applicationWillEnterForeground:(UIApplication *)application 
{ 
    [self expireBackgroundTask]; 
}