Оказывается, у меня были две странные вещи, которые вызывали фоновую задачу:
- Асинхронные URL-соединения (когда их метод инициализации завершился, iOS решила, что фоновая задача выполнена, даже еслиответ еще не получен)
- Диспетчер местоположения, специфичный для фоновой задачи (очевидно, крупный нет-нет ... Apple имеет некоторую документацию по этому вопросу, но консоль иногда выдаёт ошибку об этом)
Вот код, который я сейчас использую (пока он работает):
__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, чтобы они осталисьживы достаточно долго, чтобы закончить свое дело.Хотя это не самый изящный способ его обработки, он работает до тех пор, пока вы можете корректно обработать сбой, если цикл выполнения завершается без завершения обмена сервером.
цикл выполнения (с учетом различных таймаутов в зависимости от задачи):
//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]);
}
Надеюсь, это поможет любому, кто столкнется с этим в будущем!