Question

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!

Was it helpful?

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!

OTHER TIPS

The operations property of NSOperationQueue does not have a mutable type (it returns NSArray*). It therefore does not implement the indexed to-many compliance methods for mutable arrays so you'll never see the insert events, only the change event for the whole array.

Edit

Shadowmatter has brought up the fact that the actually returned object is an NSMutableArray. This does not, however, change anything. Firstly, Apple's documentation is clear on the issue. If a method is advertised to return an immutable object, you must respect the API. You must not use isKindOf: to find out if it is really mutable and you must definitely not change it.

The API says the operations return type is immutable and you must therefore treat it as such. More importantly for this question, as it's not a mutable collection property, it is not key value coding compliant for the mutable array KVC values. For mutable indexed collection compliance, the class has to

  • Implement one or both of the methods -insertObject:in<Key>AtIndex: or -insert<Key>:atIndexes:.
  • Implement one or both of the methods -removeObjectFrom<Key>AtIndex: or -remove<Key>AtIndexes:.

(taken directly from the Apple KVC guide)

The designer of the NSOperationQueue class designed the operations property as immutable and therefore deliberately ommitted the above methods.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top