2012-01-10 34 views
2

我正在研究一個iphone應用程序,偶爾會在後臺觸發一個任務來重新排列某些數據並將其上傳到服務器。我使用了很多Grand Central Dispatch (GCD) with CoreData的原理來運行,因爲我正在編輯持久存儲在Core Data中的對象,但代碼只是偶爾地完成運行,儘管應用程序說它幾乎還剩下600秒的執行時間。後臺任務塊功能沒有完成

我正在使用的代碼:

__block UIBackgroundTaskIdentifier bgTask; 
UIApplication *application = [UIApplication sharedApplication]; //Get the shared application instance 

NSLog(@"BackgroundTimeRemaining before block: %f", application.backgroundTimeRemaining); 

bgTask = [application beginBackgroundTaskWithExpirationHandler:^{ 
    // 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. 

    NSLog(@"BackgroundTimeRemaining after block: %f", application.backgroundTimeRemaining); 
    NSLog(@"Fixing item in the background"); 

    //Create secondary managed object context for new thread 
    NSManagedObjectContext *backgroundContext = [[NSManagedObjectContext alloc] init]; 
    [backgroundContext setPersistentStoreCoordinator:[self.managedObjectContext persistentStoreCoordinator]]; 

    /* Save the background context and handle the save notification */ 
    [[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(backgroundContextDidSave:) 
               name:NSManagedObjectContextDidSaveNotification 
               object:backgroundContext]; 

    //creating runloop to kill location manager when done 
    NSDate *stopDate = [[NSDate date] dateByAddingTimeInterval:60]; 
    [[NSRunLoop currentRunLoop] runUntilDate:stopDate]; 
    NSLog(@"Stop time = %@", stopDate); 

    MasterViewController *masterViewContoller = [[MasterViewController alloc] init]; 
    masterViewContoller.managedObjectContext = backgroundContext; 
    [[masterViewContoller locationManager] startUpdatingLocation]; 
    NSLog(@"Successfully fired up masterViewController class"); 

    [masterViewContoller adjustDataInBackground:FALSE]; 
    NSLog(@"Fixed Object!"); 

    //save background context 
    [backgroundContext save:NULL]; 

    //unregister self for notifications 
    [[NSNotificationCenter defaultCenter] removeObserver:self 
                name:NSManagedObjectContextDidSaveNotification 
                object:backgroundContext]; 

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

的問題是,「adjustDataInBackground:FALSE」是調用額外的支持方法(包括創建和核心數據對象的保存)一個相當長的方法,當後臺任務不允許所有這些方法完成它會破壞我的數據。

有沒有更好的方法來處理這種操作?我是否需要直接將所有原始代碼放入後臺任務塊中?

回答

3

因此,原來我有兩個奇怪的事情要去上被絆倒了後臺任務:

  • 異步URL連接(當它們啓動方法完成,iOS的思想後臺任務已完成即使響應尚未收到)
  • 特定於後臺任務(顯然是一大禁忌......蘋果得到了一些文檔上這一點,但在控制檯會吐出一個關於它的錯誤有時)
位置管理

這裏'小號現在我使用(它的工作原理至今)代碼:

__block UIBackgroundTaskIdentifier bgTask; 
UIApplication *application = [UIApplication sharedApplication]; //Get the shared application instance 

NSLog(@"BackgroundTimeRemaining before block: %f", application.backgroundTimeRemaining); 

bgTask = [application beginBackgroundTaskWithExpirationHandler:^{ 
    // 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. 

    NSLog(@"BackgroundTimeRemaining after block: %f", application.backgroundTimeRemaining); 

    //Create secondary managed object context for new thread 
    NSManagedObjectContext *backgroundContext = [[NSManagedObjectContext alloc] init]; 
    [backgroundContext setPersistentStoreCoordinator:[self.managedObjectContext persistentStoreCoordinator]]; 

    /* Save the background context and handle the save notification */ 
    [[NSNotificationCenter defaultCenter] addObserver:self 
              selector:@selector(backgroundContextDidSave:) 
               name:NSManagedObjectContextDidSaveNotification 
               object:backgroundContext]; 

    //Set a grace period during which background updates can't stack up... 
    //This number should be more than the longest combo of timeout values in adjustDataInBackground 
    NSDate *stopDate = [[NSDate date] dateByAddingTimeInterval:90]; 
    __lastBackgroundSnapshot = stopDate; 

    NSLog(@"Stop time = %@", stopDate); 

    MasterViewController *masterViewContoller = [[MasterViewController alloc] init]; 
    masterViewContoller.managedObjectContext = backgroundContext; 
    NSLog(@"Successfully fired up masterViewController class"); 

    [masterViewContoller adjustDataInBackground]; 
    NSLog(@"adjustDataInBackground!"); 

    //just in case 
    [[self locationManager] stopUpdatingLocation]; 

    //save background context 
    [backgroundContext save:NULL]; 

    NSLog(@"Uploading in background"); 
    //send results to server 
    postToServer *uploadService = [[postToServer alloc] init]; 
    uploadService.managedObjectContext = backgroundContext; 
    [uploadService uploadToServer]; 

    //save background context after objects are marked as uploaded 
    [backgroundContext save:NULL]; 

    //unregister self for notifications 
    [[NSNotificationCenter defaultCenter] removeObserver:self 
                name:NSManagedObjectContextDidSaveNotification 
                object:backgroundContext]; 

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

此外,我增加了以下runloop我異步URLConnection對象,使他們活着呆足夠長的時間來完成他們的業務。雖然它不是處理它的最優雅的方式,但只要您能夠在沒有完成服務器交換的情況下結束runloop時正常處理故障,它就會有效。

一個runloop(調整根據任務不同超時):

//marks the attempt as beginning 
self.doneUpload = [NSNumber numberWithBool:FALSE]; 

[[uploadAttempt alloc] fireTheUploadMethod]; 

//if uploading in the background, initiate a runloop to keep this object alive until it times out or finishes 
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) 
{ 
    //Timeout length to wait in seconds to allow for async background execution 
    NSDate *stopDate = [[NSDate date] dateByAddingTimeInterval:120]; 

    do { 
     NSLog(@"Waiting for upload to return, time left before timeout: %f", [stopDate timeIntervalSinceNow]); 
     [[NSRunLoop currentRunLoop] runUntilDate:stopDate]; 
    } while ([stopDate timeIntervalSinceNow] > 0 && self.doneUpload == [NSNumber numberWithBool:FALSE]); 
} 

希望這有助於誰在將來運行到這個人!

+0

謝謝 - 這是我見過的最完整的代碼。我想強調任何讀取異步連接的人:您肯定希望在像這樣的異步塊中執行同步URL請求。 – LordParsley 2013-11-21 12:38:54