0

I have a UIViewController that does the following in viewDidLoad

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    items = [[DataFetcher sharedInstance] getItems handler:^(NSArray *currentItems){
        if (currentItems.count % 30 == 0) { //don't wait for all the items to be ready show by chunks of 30
            items = currentItems;
            [tableView reloadData];
        }
        items = currentItems;
    }];//Pretty complex call that takes some time to finish there is WebService calls, data parsing, storing some results ...
    dispatch_async(dispatch_get_main_queue(), ^{
        [tableView reloadData];
    });
});

What I need to do is to stop getItems when I pop this viewController. It's pointless and it takes CPU Time and energy (This call may take up to a minute on some cases).

I am assuming I should be doing this in viewWillDisappear but how exactly?

Ayu
  • 926
  • 1
  • 9
  • 15

2 Answers2

1

You can use NSBlockOperation. Periodically check if it's been cancelled, and stop doing work if it has been:

- (void)getItemsWithHandler:(void (^)(NSArray *currentItems))handler {
    self.operation = [NSBlockOperation blockOperationWithBlock:^{
        if (self.operation.isCancelled) {
            return;
        }

        // Do something expensive

        if (self.operation.isCancelled) {
            return;
        }

        // Do something else expensive

        for (int i = 0; i < 10000; i++) {
            if (self.operation.isCancelled) {
                return;
            }

            // Do expensive things in a loop
        }
    }];
}

- (void) cancelGetItemsRequest {
    [self.operation cancel];
    self.operation = nil;
}

Alternatively, you can put a bunch of NSBlockOperations in an NSOperationQueue. You can set dependencies for the work, and cancel the entire queue at once if you want.

Aaron Brager
  • 65,323
  • 19
  • 161
  • 287
  • 2
    Note that strongly capturing `self` in an operation also owned by `self` creates a retain cycle. This example clears the dependency in the cancellation case, but not in the successful completion case. – ipmcc Sep 17 '14 at 16:11
  • 1
    Yeah, I should've mentioned that. Thanks for pointing it out. See [this question for further discussion](http://stackoverflow.com/q/8113268/1445366). – Aaron Brager Sep 17 '14 at 16:18
0

Cancelling asynchronous operations is nicely supported in NSOperation and NSOperationQueue, but it's quite a bit more complicated. In any case, your asynchronous code has to check from time to time whether it is cancelled or should still continue.

Best thing is to have a property "cancelled" somewhere, that you set when you don't want any more work to be done, and whenever you try to do more work, you check that property.

gnasher729
  • 51,477
  • 5
  • 75
  • 98
  • I think it would actually be *less* complicated with [`NSBlockOperation`](https://developer.apple.com/library/mac/documentation/cocoa/reference/NSBlockOperation_class/Reference/Reference.html). – Aaron Brager Sep 17 '14 at 15:40
  • (It doesn't even require an operation queue, and already has a cancelled property.) – Aaron Brager Sep 17 '14 at 15:40