If you understood correctly, you have an NSOperationQueue , and if your application goes into the background, you would like
- cancel all operations and
- wait for the cancellation to complete.
Usually this should not take too much time, so this should be enough:
- (void)applicationDidEnterBackground:(UIApplication *)application { [_queue cancelAllOperations]; [_queue waitUntilAllOperationsAreFinished]; }
The definition of โtoo much timeโ here is about five seconds: if you block -applicationDidEnterBackground: for longer, your application will be terminated and cleared from memory.
Assume that the completion of canceled operations takes more than 5 seconds. Then you need to do the wait in the background (see Comments for explanation):
- (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; }); }); }
So, basically we tell iOS that we need some time to complete the task, and when the task ends, time ends, we tell iOS about the completion of our task.
Edit
To answer the question in your edit: To answer the cancellation, just mark the cancellation, please, and return from the -main method. A canceled operation does not immediately end, but ends when -main returns.
- (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 }
If you do not check the canceled flag at all in -main , the operation will not respond to cancellation, but will continue until it is completed.
Edit 2
If the operation is canceled, nothing happens to it, except that the isCancelled flag is true. The code above in my original answer waits in the background until the operation is completed (either responded to the cancellation, or just finished, assuming that it does not take 10 minutes to cancel).
Of course, when reacting to isCancelled in our operation, you must make sure that you leave the operation in a non-corrupt state, for example, immediately after loading (just ignoring the data) or after writing all the data.
You are right if the operation is canceled, but is still performed when switching to the foreground, this operation will complete the download, and then (if you programmed it this way) respond to the cancellation and basically discard the downloaded data,
Instead, you can not cancel the operations, but wait for their completion (provided that they take less than 10 minutes). To do this, simply delete the line [_queue cancelAllOperations]; .