因此,此页面上有一个有关后台执行的示例:https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html#//apple_ref/doc/uid/TP40007072-CH4-SW1,这是示例:

- (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之前?

最佳答案

每个对象都有一个bgTask实例,但这是在AppDelegate上,而不是某些常规VC或对象。因此,从技术上讲,只有一个bgTask实例在工作。

但这仍然会产生问题。由于如果两次调用此方法,它将覆盖bgTask的值。我的第一个想法是,退出该应用程序后,所有先前的任务都会多次失效。但是经过测试意识到情况并非如此(IMO很好)。确实发生的是bgTask被覆盖(如预期的那样),并且新值已传递给第一个endBackgroundTask:调用。之后立即将bgTask设置为UIBackgroundTaskInvalid,以将其清除,并将清除后的值传递给对endBackgroundTask:的任何后续调用。显然,这导致了不干净的终止,因为并非所有唯一ID都将被终止,从而导致expiration处理程序在任何剩余的后台任务上执行。

话虽如此,我相信您关于使用局部变量的假设是正确的。如果您尝试使用此代码(放置在AppDelegate applicationDidEnterBackground:中):

__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调用)。

08-05 11:11