views:

1475

answers:

4

I have an NSInvocationOperation that would download and parse a series of NSXMLDocuments in the background to my UI responsive.

My attempt at stopping the Invocation operation is to call my NSOperationQueue's cancellAllOperations. But it seems that this won't stop the invocation's execution.

Any ideas on how would I go about this problem?

+3  A: 

It's up to the implementation of your NSOperation object to actually stop what it's doing, clean up, and exit when it gets notified that it's been cancelled. Messaging that you want to cancel all operations on the queue will cause the queue to stop dequeueing new operations to run and will send the cancel message to any operations currently running.

In your operation's main method, you should be checking for isCancelled and handling that state when you are actually cancelled.

For more information, see the Creating and Managing Operation Objects in the Threading Programming Guide.

Jason Coco
+1  A: 

The post above is great, but to more directly answer the original question: it appears that you can't stop NSInvocationOperation object because it doesn't support cancellations. You're going to have to subclass it.

EightyEight
+1  A: 

UPDATE: Instruments shows leaks-a-plenty when I do this. Proceed with caution! I'm keeping this here in case I'm actually on to something and someone else can figure out how to get over the leak hurdle.

Here's a twisted idea, which I'm re-trying as I type this:

Set the operation as the object for NSInvocationOperation's initWithTarget:selector:object: method. Presuming you already have a NSOperationQueue (we'll call it queue):

NSInvocationOperation *operation = [NSInvocationOperation alloc];
operation = [operation initWithTarget:self selector:@selector(myOperation:) object:operation];
[queue addOperation:operation];
[operation release];

Note that we have to break apart the alloc into its own call. Otherwise we can't set object to operation!

Then, within your operation method, cast the object back and sprinkle checks for isCancelled as desired. For example:

  - (void)myOperation:(id)object {
    NSInvocationOperation *operation = (NSInvocationOperation *)object;
    if ([operation isCancelled]) return;
    ...
  }

Make sure your selector ends with a colon back in the initWithTarget:... call, since you'll be passing in an object now.

So far, so good. Now if I can force cancelAllOperations, I'll know if this actually works. :)

Joe D'Andrea
A: 

You need to check if NSInvocationOperation isCancelled is YES. To do that in NSInvocationOperation, you can use Key Value Observing:

Add your object as NSInvocationOperation isCancelled observer when running the operation:

NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:<targetObj> selector:@selector(<targetMethod>) object:nil];
[operation addObserver:<targetObj> forKeyPath:@"isCancelled" options:NSKeyValueObservingOptionNew context:nil];
[operQueue addOperation:operation];
[operation release];

Then in targetObj implement

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;

to watch for isCancelled being changed by NSOperationQueue's cancellAllOperations. You can set a private flag here and targetMethod can check it and cancel if needed.

komorian