Search code examples
iosasynchronousnsoperationnsoperationqueue

How do I cancel an NSOperation's dependencies?


I have some NSOperations in a dependency graph:

NSOperation *op1 = ...;
NSOperation *op2 = ...;

[op2 addDependency:op1];

Here's how I'm running them:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:op1];
[queue addOperation:op2];

Now I need to cancel them. How do I ensure that all the NSOperations in a dependency graph are cancelled, and that no other NSOperations are cancelled?


what I've tried:

Calling cancel on either NSOperation doesn't cancel the other (as far as I can tell):

[op1 cancel]; // doesn't cancel op2
// -- or --
[op2 cancel]; // doesn't cancel op1

Cancelling the queue would also cancel operations that aren't part of the dependency graph of op1 and op2 (if there are any such operations in the queue):

[queue cancelAllOperations];

So I solved this using a custom method that recursively looks through an NSOperation's dependencies and cancels them. However, I'm not happy with this solution because I feel like I'm fighting the framework:

- (void)recursiveCancel:(NSOperation *)op
{
    [op cancel];
    for (NSOperation *dep in op.dependencies)
    {
        [self recursiveCancel:op];
    }
}

Solution

  • There does not exist a notion of an NSOperation automatically cancelling its dependencies. This is because multiple NSOperations may be dependent on the same other NSOperation. The dependency relationship exists strictly to delay execution of a particular NSOperation until all of its dependency NSOperations are complete.

    You may consider writing a subclass of NSOperation:

    @interface NSOperationOneToOne : NSOperation
    - (void)addOneToOneDependency:(NSOperation *)operation;
    - (void)removeOneToOneDependency:(NSOperation *)operation;
    @end
    
    @implementation NSOperationOneToOne {
      NSMutableArray *oneToOneDependencies;
    }
    - (void)addOneToOneDependency:(NSOperation *)operation {
      [oneToOneDependencies addObject:operation];
      [self addDependency:operation];
    }
    - (void)removeOneToOneDependency:(NSOperation *)operation {
      [oneToOneDependencies removeObject:operation];
      [self removeDependency:operation];
    }
    - (void)cancel {
      [super cancel];
      [oneToOneDependencies makeObjectsPerformSelector:@selector(cancel)];
    }
    @end
    

    Note: The above code is not guaranteed to be bug-free.