我正在开发一款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”是一个非常长的方法,它调用其他支持方法(包括创建和保存核心数据对象),并且当后台任务不允许所有这些方法完成时它会破坏我的数据。
有没有更好的方法来处理这种操作?我是否需要将所有原始代码直接放入后台任务块?
答案 0 :(得分:3)
事实证明,我有两个奇怪的事情正在绊倒后台任务:
这是我现在使用的代码(它到目前为止有效):
__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]);
}
希望这有助于将来遇到这种情况的任何人!