But shouldn't the synthesized accessors automatically return such a proxy object?
No.
What's the proper way to work around this--should I write a custom accessor that just invokes [super mutableArrayValueForKey...]
?
No. Implement the array accessors. When you call these, KVO will post the appropriate notifications automatically. So all you have to do is:
[myObject insertObject:newObject inTheArrayAtIndex:[myObject countOfTheArray]];
and the Right Thing will happen automatically.
For convenience, you can write an addTheArrayObject:
accessor. This accessor would call one of the real array accessors described above:
- (void) addTheArrayObject:(NSObject *) newObject {
[self insertObject:newObject inTheArrayAtIndex:[self countOfTheArray]];
}
(You can and should fill in the proper class for the objects in the array, in place of NSObject
.)
Then, instead of [myObject insertObject:…]
, you write [myObject addTheArrayObject:newObject]
.
Sadly, add<Key>Object:
and its counterpart remove<Key>Object:
are, last I checked, only recognized by KVO for set (as in NSSet) properties, not array properties, so you don't get free KVO notifications with them unless you implement them on top of accessors it does recognize. I filed a bug about this: x-radar://problem/6407437
I have a list of all the accessor selector formats on my blog.
Objective-C's message dispatch and other features are tuned and pretty fast for what they provide, but they still don't approach the potential of tuned C for computational tasks:
NSNumber *a = [NSNumber numberWithIntegerValue:(b.integerValue + c.integerValue)];
is way slower than:
NSInteger a = b + c;
and nobody actually does math on objects in Objective-C for that reason (well that and the syntax is awful).
The power of Objective-C is that you have a nice expressive message based object system where you can throw away the expensive bits and use pure C when you need to. KVO is one of the expensive bits. I love KVO, I use it all the time. It is computationally expensive, especially when you have lots of observed objects.
An inner loop is that small bit of code you run over and over, anything thing there will be done over and over. It is the place where you should be eliminating OOP features if need be, where you should not be allocating memory, where you should be considering replacing method calls with static inline functions. Even if you somehow manage to get acceptable performance in your rendering loop, it will be much lower performance than if you got all that expensive notification and dispatch logic out of there.
If you really want to try to keep it going with KVO here are a few things you can try to make things go faster:
- Switch from automatic to manual KVO in your objects. This may allow you to reduce spurious notifications
- Aggregate updates: If your intermediate values over some time interval are not relevant, and you can defer for some amount of time (like the next animation frame) don't post the change, mark that the change needs to posted and wait for a the relevent timer to go off, you might get to avoid a bunch of short lived intermediary updates. You might also use some sort of proxy to aggregate related changes between multiple objects.
- Merge observable properties: If you have a large number of properties in one type of object that might change you may be better off making a single "hasChanges" property observe and having the the observer query the properties.
Best Answer
The only way to be sure is to measure. None of us have enough knowledge about how NSMutableDictionary's and NSMutableArray's implementations work, so there's little point asking.
Granted, you could probably expect some hit since the dictionary has to do additional hashing that a simple array would not. Whether or not that is "significant" is hard to say.
Again, measure.