NSOperationQueue和背景支持
我在阐述支持NSOperationQueue
类的背景的有用策略时遇到了一些问题。特别是,我有一堆NSOperation
s表示执行以下操作:NSOperationQueue和背景支持
- 从网络
- 解析文件
- 导入数据文件中的核心数据
的下载文件操作被插入到串行队列中。一旦操作完成,下一个可以开始。
我需要在应用程序进入后台时停止(或继续)操作。从这些讨论(Does AFNetworking have backgrounding support?和Queue of NSOperations and handling application exit)我看到最好的办法是取消操作并在每个操作中使用isCancelled
属性。然后,根据该属性检查操作的关键点,它允许在应用程序进入后台时回滚执行状态(正在运行的操作)。
基于强调背景支持的Apple模板,我该如何管理类似的情况?我可以简单地取消操作还是等待当前操作完成?详情请参阅评论。
- (void)applicationDidEnterBackground:(UIApplication *)application
{
bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
// Do I have to call -cancelAllOperations or
// -waitUntilAllOperationsAreFinished or both?
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// What about here?
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
在此先感谢您。
编辑
如果NSOperation
main
方法执行下面的代码,怎么可能跟随响应取消事件模式?
- (void)main
{
// 1- download
// 2- parse
// 2.1 read file location
// 2.2 load into memory
// 3- import
// 3.1 fetch core data request
// 3.2 if data is not present, insert it (or update)
// 3.3 save data into persistent store coordinator
}
我描述的每种方法都包含各种步骤(非原子操作,除下载之外)。因此,在每个步骤中都可能会发生取消(以未预定义的方式)。我可以在每一步之前检查isCancelled
属性吗?这是否工作?基于Tammo福瑞斯”编辑
我明白你的意思是你编辑代码
编辑2。但我担心的是以下内容。取消请求(用户可以按主页按钮)可能会在main
执行过程中的任何位置发生,所以,如果我简单地返回,操作状态将被破坏。我需要在返回之前清理它的状态吗?你怎么看?
当我使用同步操作(在同一个线程内以同步方式执行的操作)时,我描述的问题可能会发生。例如,如果main
正在下载文件(通过+sendSynchronousRequest:returningResponse:error
执行下载),并且该应用程序置于后台,则会发生什么情况?如何管理这种情况?
// download
if ([self isCancelled])
return;
// downloading here <-- here the app is put in background
很明显,我认为当应用程序被放在前台时,操作会再次运行,因为它已被取消。换句话说,它被迫不保持其状态。我错了吗?
如果我理解正确的话,你有一个NSOperationQueue
,如果你的应用程序进入后台,你想
- 取消所有的操作和
- 等待,直到取消处理。
通常这不应该花太多时间,所以应该足以做到这一点:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[_queue cancelAllOperations];
[_queue waitUntilAllOperationsAreFinished];
}
的“太多时间”这里的定义是大约五秒钟:如果您阻止-applicationDidEnterBackground:
长除此之外,你的应用程序将被终止并从内存中清除。
假设完成取消的操作需要5秒以上的时间。然后你要做的等待在后台(见说明中的注释):
- (void)applicationDidEnterBackground:(UIApplication *)application
{
bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
// If this block is called, our background time of normally 10 minutes
// is almost exceeded. That would mean one of the cancelled operations
// is not finished even 10 minutes after cancellation (!).
// This should not happen.
// What we do anyway is tell iOS that our background task has ended,
// as otherwise our app will be killed instead of suspended.
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Normally this one is fast, so we do it outside the asynchronous block.
[_queue cancelAllOperations];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Wait until all the cancelled operations are finished.
[_queue waitUntilAllOperationsAreFinished];
// Dispatch to the main queue if bgTask is not atomic
dispatch_async(dispatch_get_main_queue(), ^{
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
});
}
所以基本上我们告诉我们需要一些时间来执行任务的iOS,当任务完成时运行的我们告诉iOS我们的任务已经结束。
编辑
要回答你编辑的问题:为了应对取消,只是检查取消时,您可以和来自-main
方法返回。取消的操作不会立即完成,但在-main
返回时完成。
- (void)main
{
// 1- download
if ([self isCancelled]) return;
// 2- parse
// 2.1 read file location
// 2.2 load into memory
while (![self isCancelled] && [self hasNextLineToParse]) {
// ...
}
// 3- import
// 3.1 fetch core data request
if ([self isCancelled]) return;
// 3.2 if data is not present, insert it (or update)
// 3.3 save data into persistent store coordinator
}
如果你没有为取消标志检查-main
可言,操作不会发生反应,消除,但运行,直到它完成。
编辑2
如果操作被取消,没有任何反应它除了isCancelled标志设置为true。上述原始答案中的代码将在后台等待,直到操作完成(对取消作出反应或完成,假设不需要10分钟即可取消)。
当然,在我们的操作中,当对isCancelled
作出反应时,您必须确保将操作保持在未损坏状态,例如,直接在下载后(仅忽略数据)或写完所有数据后。
如果一个操作被取消但仍然在运行,当你切换回前台时,操作将完成下载,然后(如果你这样编程)反应取消并基本丢弃下载数据。
你可以做的是不取消操作,但等待它们完成(假设它们不到10分钟)。要做到这一点,只需删除行[_queue cancelAllOperations];
。
+1您的回复。关于你的理解,是的,这是正确的。但是,你认为还有其他解决方案可以实现吗?在一个操作的* main *中,我执行三个不同的任务。那么,怎样才能以正确的方式取消手术呢?关于你的回复的第二部分,我无法理解*如果这个块被调用,我们的背景时间通常是10分钟 几乎超过*评论。你能说一下吗?最后,第二部分(* dispatch_async *)执行什么?你为什么要检索主线程?感谢您的支持。 –
Hi @flexaddicted,要以正确的方式取消操作,您应该对文档中描述的取消事件做出反应:http://developer.apple.com/library/mac/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationObjects/ OperationObjects.html#// apple_ref/doc/uid/TP40008091-CH101-SW24。 –
关于第二部分:如果您使用iOS注册后台任务,通常会让您花10分钟完成它。当你的任务完成时,通过调用'-endBackgroundTask:'让iOS知道。假设您的操作在10分钟后仍未完成。只有在这种情况下,iOS才会调用过期处理程序块给你一个清理机会。如果你不在该块中调用'-endBackgroundTask:',你的应用将会被杀死,因为它行为不当。你必须平衡对'-beginBackgroundTaskWithExpirationHandler:'和'-endBackgroundTask:'的调用。 –