我在制定有用的策略以支持NSOperationQueue
课程的背景方面遇到了一些问题。特别是,我有一堆NSOperation
个执行以下操作:
将操作插入到串行队列中。一旦操作完成,下一个操作就可以开始了。
我需要在应用进入后台时停止(或继续)操作。从这些讨论(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 Freese编辑2'编辑
我理解你的编辑代码是什么意思。但我担心的是以下几点。取消请求(用户可以按下主页按钮)可以在main
执行中的任何时刻发生,因此,如果我只是返回,操作的状态将被破坏。在返回之前我需要清理它的状态吗?你觉得怎么样?
我描述的问题可能发生在我使用同步操作时(在他们运行的同一个线程中以同步方式执行的操作)。例如,如果main
正在下载文件(通过+sendSynchronousRequest:returningResponse:error
执行下载)并且应用程序被置于后台,那么它会发生什么?如何处理这种情况?
// download
if ([self isCancelled])
return;
// downloading here <-- here the app is put in background
显然,我认为当应用程序放在前台时,操作会再次运行,因为它已被取消。换句话说,它被迫不能保持其状态。我错了吗?
答案 0 :(得分:15)
如果我理解正确,你有一个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];
。