I'm working on a NSOperation subclass and I came across this very weird issue were the completion block is called twice in a row. The KVO calls seem fine but the completionBlock is still strangely called twice. Am I misunderstanding NSOperation? The documentation says that the completion block is called when isFinished
becomes YES
and that only happens once in my code:
- (void)main {
@autoreleasepool {
[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isReady"];
executing = YES;
[self didChangeValueForKey:@"isReady"];
[self didChangeValueForKey:@"isExecuting"];
//start the operation
}
}
I then simply set the completionBlock
like this:
self.completionBlock = ^{
NSLog(@"Completed");
}
When it finishes this method is called (it is called just ONCE, i double checked that)
- (void)completeOperation {
[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
executing = NO;
completed = YES;
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
}
But the completionBlock is called twice and prints "Completed" twice into the console.
And here are the methods that indicate the current state:
- (BOOL)isReady {
if (executing || cancelled || completed) {
return NO;
}
return YES;
}
- (BOOL)isCancelled {
return cancelled;
}
- (BOOL)isConcurrent {
return YES;
}
- (BOOL)isExecuting {
return executing;
}
- (BOOL)isFinished {
return completed;
}
isCancelled
never turns to YES
in my testing code so that couldn't be the cause of it.
I really don't get why the completionBlock is called twice. Even when setting the completion block to nil from inside the completion block it is sometimes called twice which is even stranger.
Not sure if this is the cause, but in my experience, there is no need to override the read-only state properties. You are responsible for checking isCancelled periodically in the main loop and if it's set bailing out of whatever you are doing, but I believe the other state flags (isReady, isFinished, isExecuting) are taken care of automatically.
How many times does it fire if you strip away the state flag handling and just do your process in -main
?
EDIT: Assuming you are overriding those flags to allow concurrency, then you should read through the notes in the docs
By the looks of it, you should never need to override isReady
or isCancelled
and instead override -start
according to the instructions in the docs.