iOS 后台执行指南示例的 Apple 编程指南?
Apple programming guide for iOS background execution guide example?
所以在这个页面上有一个关于后台执行的例子: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
在class中定义为变量。因此 class(对象)的每个实例都有一个 bgTask
属性。如果 applicationDidEnterBackground
在异步块完成之前被多次调用,这不是竞争条件的危险吗?我的意思是 bgTask
会改变它的值,而 endBackgroundTask
会根据新任务值而不是旧值被调用?
这里不是更好的解决方案吗:
__block UIBackgroundTaskIdentifier bgTask;
在调用 beginBackgroundTaskWithName
之前?
我认为您要解决的问题如下:
应用程序被发送到 Background
状态,并且 iOS 调用 applicationDidEnterBackground:
.
后台任务已启动,最多可能需要几分钟。
在此期间,应用程序再次激活,并再次发送到后台,再次调用applicationDidEnterBackground:
,并启动另一个后台任务,如果变量bgTask
不是块变量,则会被覆盖。这是对的。因此,bgTask
确实应该是一个块变量。
与此问题相关的问题是,如果已启动多个后台任务,您如何完成后台执行。 here.
给出了一个示例,说明如何执行此操作
这个想法是有一个变量来计算活动的后台任务。一旦全部完成,就可以终止后台执行。
每个对象都有一个 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
调用)。
你是对的,第二次调用时,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];
}
所以在这个页面上有一个关于后台执行的例子: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
在class中定义为变量。因此 class(对象)的每个实例都有一个 bgTask
属性。如果 applicationDidEnterBackground
在异步块完成之前被多次调用,这不是竞争条件的危险吗?我的意思是 bgTask
会改变它的值,而 endBackgroundTask
会根据新任务值而不是旧值被调用?
这里不是更好的解决方案吗:
__block UIBackgroundTaskIdentifier bgTask;
在调用 beginBackgroundTaskWithName
之前?
我认为您要解决的问题如下:
应用程序被发送到 Background
状态,并且 iOS 调用 applicationDidEnterBackground:
.
后台任务已启动,最多可能需要几分钟。
在此期间,应用程序再次激活,并再次发送到后台,再次调用applicationDidEnterBackground:
,并启动另一个后台任务,如果变量bgTask
不是块变量,则会被覆盖。这是对的。因此,bgTask
确实应该是一个块变量。
与此问题相关的问题是,如果已启动多个后台任务,您如何完成后台执行。 here.
给出了一个示例,说明如何执行此操作
这个想法是有一个变量来计算活动的后台任务。一旦全部完成,就可以终止后台执行。
每个对象都有一个 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
调用)。
你是对的,第二次调用时,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];
}