I have some problems on elaborating a useful strategy to support background for NSOperationQueue
class. In particular, I have a bunch of NSOperation
s that perform the following actions:
- Download a file from the web
- Parse the file
- Import data file in Core Data
The operations are inserted into a serial queue. Once an operation completes, the next can start.
I need to stop (or continue) the operations when the app enters the background. From these discussions ( Does AFNetworking have backgrounding support? and Queue of NSOperations and handling application exit ) I see the best way is to cancel the operations and the use the isCancelled
property within each operation. Then, checking the key point of an operation against that property, it allows to roll back the state of the execution (of the running operation) when the app enters background.
Based on Apple template that highlights background support, how can I manage a similar situation? Can I simply cancel the operations or wait the current operation is completed? See comments for details.
- (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;
});
}
Thank you in advance.
Edit
If NSOperation
main
method perform the code below, how is it possible to follow the Responding to Cancellation Events pattern?
- (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
}
Each method I described contains various steps (non atomic operations, except the download one). So, a cancellation could happen within each of these step (in a not predefined manner). Could I check the isCancelled
property before each step? Does this work?
Edit 2 based on Tammo Freese' edit
I understand what do you mean with your edit code. But the thing I'm worried is the following. A cancel request (the user can press the home button) can happen at any point within the main
execution, so, if I simply return, the state of the operation would be corrupted. Do I need to clean its state before returning? What do you think?
The problem I described could happen when I use sync operations (operations that are performed in a sync fashion within the same thread they run). For example, if the main
is downloading a file (the download is performed through +sendSynchronousRequest:returningResponse:error
) and the app is put in background, what could it happen? How to manage such a situation?
// download
if ([self isCancelled])
return;
// downloading here <-- here the app is put in background
Obviously, I think that when the app is then put in foreground, the operation is run again since it has been cancelled. In other words, it is forced to not maintain its state. Am I wrong?