Matt Fenwick
Matt Fenwick

Reputation: 49085

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];
    }
}

Upvotes: 11

Views: 3483

Answers (2)

malhal
malhal

Reputation: 30571

Sounds to me like you are trying to group operations together in the same queue. To achieve that it's best to split them up using a queue per group. So for each group create an NSOperation subclass in concurrent mode, include a queue, add each sub-operation to the queue. Override cancel and call [super cancel] then [self.queue cancelAllOperations].

A huge advantage of this approach is you can retry operations by adding again to the sub-queue, without affecting the order of the main queue.

Upvotes: 2

Ian MacDonald
Ian MacDonald

Reputation: 14010

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.

Upvotes: 10

Related Questions