Search code examples
iphonemultithreadingkey-value-observingfoundationnsoperationqueue

Change dictionary values wrong when using KVO with NSOperationQueue?


I was working through an example in the concurrency chapter of "More iPhone 3 Development," and can't get KVO on an NSOperationQueue working as expected. I create an NSOperationQueue and observe its operations array using:

NSOperationQueue *newQueue = [[NSOperationQueue alloc] init];
self.queue = newQueue;
[newQueue release];
[queue addObserver:self
        forKeyPath:@"operations"
           options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
           context:NULL];

When the first NSOperation is added to the queue, I expect it to be added to its underlying operations array (which the iOS documentation says is KVO-compliant) and hence, in the change dictionary, to find a mapping from NSKeyValueChangeKindKey to NSKeyValueChangeInsertion, along with a mapping from NSKeyValueChangeNewKey to the added NSOperation. But I wasn't seeing any kind of value NSKeyValueChangeInsertion.

I know the debugger is pro and all, but in the interest of having something useful to copy here, I started my observer method with:

- (void) observeValueForKeyPath:(NSString *)keyPath
                       ofObject:(id)object
                         change:(NSDictionary *)change
                        context:(void *)context {
  NSNumber *kind = [change objectForKey:NSKeyValueChangeKindKey];
  NSObject *newValue = [change objectForKey:NSKeyValueChangeNewKey];
  NSObject *oldValue = [change objectForKey:NSKeyValueChangeOldKey];
  NSIndexSet *indexes = [change objectForKey:NSKeyValueChangeIndexesKey];
  NSLog(@"kind=%d, newValue=%@, oldValue=%@, indexes=%@",
       [kind integerValue], newValue, oldValue, indexes);

And that prints:

2010-11-18 20:01:56.249 Stalled[2692:6f07] kind=1, newValue=(
    "<SquareRootOperation: 0x5f51b40>"
), oldValue=(
), indexes=(null)

2010-11-18 20:01:56.250 Stalled[2692:6f07] kind=1, newValue=(
    "<SquareRootOperation: 0x5f51b40>"
), oldValue=(
    "<SquareRootOperation: 0x5f51b40>"
), indexes=(null)

(SquareRootOperation is simply my subclass of NSOperation that overrides main appropriately, and Stalled is simply the project name.) But note that the method is called twice upon inserting a single operation, and both times with a kind value of 1, which is NSKeyValueChangeSetting, not NSKeyValueChangeInsertion. Additionally, newValue and oldValue seem to be the array itself, not the item added.

Any ideas? Thanks!


Solution

  • The docs say -operations is KVO-compliant, but don't specify to what detail the notifications will be. In practice, it seems you are only told that a change has occurred, so would have to compare the old and new values to find out what was inserted.

    Don't forget that these notifications can be sent to you on any thread!