I've been trying to fix an issue in our NSOperation subclass and I feel it may be related to our manual change notifications for KVO. All the sources I've checked seem to do the following when updating the NSOperation state:
[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:@"isFinished"];
[self didChangeValueForKey:@"isExecuting"];
In contrast, we have been doing it like this:
[self willChangeValueForKey:@"isExecuting"];
_isExecuting = NO;
[self didChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
_isFinished = YES;
[self didChangeValueForKey:@"isFinished"];
Can anybody tell me why the former seems to be the recommended way of doing this?
It also seems that Apple's KVO docs recommend the first approach as well. Unfortunately they don't explain why.(https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/KeyValueObserving/Articles/KVOCompliance.html#//apple_ref/doc/uid/20002178-SW3)/
The reason is that, conceptually, the operation is changing both states together. You want observers to be notified only after the internal state has been updated for both properties, so that, when handling the notification, the observers see consistent state.
If you do:
then observers will get the change notification for the
isExecuting
property during thatdidChange...
call. If they check the operation properties in their handler, they could see that the operation is not executing (isExecuting
returnsNO
) but also not finished (isFinished
still returnsNO
). That doesn't make sense. The observers could do something odd as a result.