0
votes

I would like to understand what is the correct behavior for an NSOperation subclass. I have my subclasses with different isReady conditions. Yes, I check in the code if the operation is cancelled and I act in consequence. This is great while the operation is in execution. It stops its task, sets finished to true and it gets deleted from the queue. But what about its dependencies? They are not yet executing, so they stay in the NSOperationQueue in cancelled state forever.

Should I set ready = true for cancelled operations so the queue will call the start method that will set in executing and immediately finish the task setting finished to true?

2
if b depends on a, b will be execute after a.finished == true and b.ready == truenRewik
You didn't get the problem. The dependency behavior is clear. what is not clear is what i should do for the cancel logic. If b depends on a, a is executing and get cancelled, b will be cancelled as well, but the ready can't be true, so the queue will not remove the operation from the array. That's the problem. What i'm interested in now is the logic for the cancel when the operation is not yet started. thanks.Giuseppe Lanza
Which cancel do you call operationQueue.cancelAllOperations() or a.cancel() ?nRewik
I have an operation that get cancelled. no matter which way. the cancel method is called. what should happen to dependencies?Giuseppe Lanza
The dependent operation will be executed. No matter a is cancelled or not. You should set operation.finished==true at the final state to make NSOperationQueue removes the operation. You should set .ready==true whenever it's ready to begin the operation. In life cycle of operation, you should frequently check .cancelled whenever .cancelled == true, you should stop operation and set .finished == true.nRewik

2 Answers

1
votes

The dependent operation will be executed. No matter a is cancelled or not.

You should set operation.finished==true at the final state to make NSOperationQueue removes the operation.

You should set .ready==true whenever it's ready to begin the operation.

In life cycle of operation, you should frequently check .cancelled whenever .cancelled == true, you should stop operation and set .finished == true.

0
votes

I just dealt with this problem myself. I could not understand why when I cancelled my NSOperation subclass, it was never removed from the NSOperationQueue. It turns out that my overridden isReady method was to blame. The isReady method was checking the super implementation AND whether a property had been set, which it hadn't, which is why the operation was getting cancelled. However, apparently if an NSOperation instance never reaches the ready state, even if it is cancelled, the NSOperationQueue will not remove that operation until is has reached the ready state. Hence, the solution is to OR your custom conditions for readiness with the isCancelled property value. Alternatively, you can change the logic such that the operation will always be ready, but do nothing in the main method if your conditions for readiness are not met, depending on your application logic.

Example:

// for proper KVO compliance
+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key
{
    NSSet *keys = [super keyPathsForValuesAffectingValueForKey:key];

    NSString *readyKey = NSStringFromSelector(@selector(isReady));
    if ([readyKey isEqualToString:key]) {
        keys = [keys setByAddingObjectsFromArray:@[NSStringFromSelector(@selector(url)), NSStringFromSelector(@selector(isCancelled))]];
    }

    return keys;
}

- (BOOL)isReady
{
    return (this.url != nil || this.isCancelled) && super.isReady;
}

- (void)setUrl:(NSURL *)url
{
    if (self.url == url) {
        return;
    }

    NSString *urlKey = NSStringFromSelector(@selector(url));

    [self willChangeValueForKey:urlKey];

    _url = url;

    [self didChangeValueForKey:urlKey];
}

Thanks to NSHipster's KVO article for the above KVO-related code.