18

I'm using NSOperationQueue's addOperationWithBlock. From within the block, how do I check to see if I'm supposed to cancel the operation? Or access any NSOperation properties/methods?

[myOperationQueue addOperationWithBlock: ^{

  while ( /* long running loop */ )
  {
      // how to determine here if I need to cancel?
      // for that matter, access any NSOperation properties/methods?

  }

}];

Is the better way to do this to use a NSBlockOperation?

TomSwift
  • 39,369
  • 12
  • 121
  • 149

2 Answers2

51

A better solution might be to use NSBlockOperation and add that to the queue instead of a raw block. You could do something like:

__block NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
  while(![operation isCancelled]){
    //Some long operation
  }
}];

[[self queue] addOperation:operation];

This lets you use blocks while giving you a little more control over the operation... and a few more NSOperation niceties as well (like the ability to add completion blocks, for example).

jemmons
  • 18,605
  • 8
  • 55
  • 84
  • that's almost exactly what i want. but it only works in that particular block. in my case my block calls another function which another block and at that point I dont think i have access to 'operation' anymore. I think the only way my case would work is if I sublass NSOperation. – roocell Nov 21 '12 at 15:56
  • i was able to resolve my case by passing in the operation through my blocks. thank you - i like this approach because it avoids subclass NSOperation. – roocell Nov 21 '12 at 16:12
  • This solution looks great. The only small question I have is, does the variable 'operation' have to be tagged with __block? I don't think so because the variable's value doesn't need to be changed inside the block. – Jake Dec 21 '12 at 19:36
  • 3
    @Jake Try it and see! It shouldn't work because `operation` will be `nil` (or junk memory) at the time the block is defined, which means it will be `nil` inside the block. Be careful of the gotcha: `![nil isCancelled]` will always evaluate to `YES`, so it may *look* like it's working if you don't ever actually try to cancel the operation. – jemmons Dec 23 '12 at 19:11
  • @jemmons Your reasoning makes sense. now I understand it needs __block. Thanks for the clarification. – Jake Dec 29 '12 at 20:39
  • This implementation will produce memory leak. Check my solution with solves memory leak problem. – Aliaksandr Bialiauski Feb 06 '15 at 22:54
2

You can't really check to see if you need to cancel the operation if it's in a block. If it's in a block and it's supposed to be canceled then it is canceled. Accessing NSOperation properties is not possible because the block is not an NSOperation instance per se.

Example code:

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[])
{

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    NSOperationQueue *q = [[NSOperationQueue alloc] init];
    [q addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:10];
        NSLog(@"Block 1");
    }];
    [q addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:3];
        NSLog(@"Block 2");
    }];
    [q cancelAllOperations];
    [NSThread sleepForTimeInterval:15];

    [pool drain];
    return 0;
}

If you remove the cancelAllOperations call then the blocks fire as you would expect.

I would suggest that if you need to have finer grained control over the operation's cancel state and interplay with the NSOperationQueue that you would be better off using an NSOperation rather than an NSBlockOperation. You can subclass NSOperation to accomplish this.

Joshua Smith
  • 6,561
  • 1
  • 30
  • 28