3

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)/

fyell
  • 221
  • 1
  • 7

2 Answers2

2

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:

[self willChangeValueForKey:@"isExecuting"];
_isExecuting = NO;
[self didChangeValueForKey:@"isExecuting"];

then observers will get the change notification for the isExecuting property during that didChange... call. If they check the operation properties in their handler, they could see that the operation is not executing (isExecuting returns NO) but also not finished (isFinished still returns NO). That doesn't make sense. The observers could do something odd as a result.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • Interesting. Seems like this pattern requires an understanding of how the NSOperation internal observer handles state changes. Do you know if there is further documentation on this? – fyell Jun 23 '15 at 00:24
  • You mean how the operation queue observes its operations? It's not necessarily about that. Other things can observe operation properties. I was writing about the general case. No, I don't have further documentation about how queues observe their operations and respond to those changes. – Ken Thomases Jun 23 '15 at 00:40
  • Not at the NSOperationQueue level. I'm making an assumption that when an NSOperation is initialized, it also sets up KVO privately on itself. I make this assumption because of this call I'm seeing in a stack trace crash `[__NSOperationInternal _observeValueForKeyPath:ofObject:changeKind:oldValue:newValue:indexes:context:]`. – fyell Jun 23 '15 at 00:46
0

I've implemented NSOperation without following the pattern

[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:@"isFinished"];
[self didChangeValueForKey:@"isExecuting"];

instead by simply doing

self.executing = NO;
self.finished = YES;

.. without problems (never ending operations or the like) when using NSOperationQueues. It seems NSOperationQueue only listens for 'IsFinished' to determine if an NSOperation is truly finished. This other answer explains better.

NSOperation KVO isFinished

Community
  • 1
  • 1
user3754239
  • 31
  • 1
  • 3