Re: KVC and KVO for arrays
Re: KVC and KVO for arrays
- Subject: Re: KVC and KVO for arrays
- From: Ken Thomases <email@hidden>
- Date: Wed, 13 Feb 2008 16:05:06 -0600
On Feb 12, 2008, at 2:32 PM, Hans van der Meer wrote:
I came along this problem because I understand the binding mechanism
requires kvc calls in order to get the observing going. It is
unclear to me how to do this for changes in array. (I did read the
KVC and KVO Programming Guides).
Setting an array works, for example:
NSMutableArray *myArray;
@property(copy) NSMutableArray *myArray;
@synthesize myArray;
....
myArray = [NSMutableArray arrayWithCapacity:10];
// fill myArray with objects for ex. with [myArray addObject:..] etc.
[self setMyArray:myArray]
In the above code the NSArrayController bound to myArray a Model Key
Path gets informed of the change.
In the documentation there is mention of "indexed accessor methods"
that will directly inform the array of changes. For changes these
are given as insertObject:in<Key>AtIndex: and
removeObjectFrom<Key>AtIndex:
The <key> in this case being MyArray.
Generally, for the array KVC/KVO messages, <key> is a plural noun.
For example, the <key> might be "widgets", so the template method name
above becomes:
insertObject:inWidgetsAtIndex:
That sounds fine for implementing array accessing in a custom class
of my own. But what for the standard NSArrayMutable array? When
coding:
[myArray insertObject:anObject inMyArrayAtIndex:0]
the compiler warns me that NSMutableArray does not know this method
and on executing I get the more or less expected error: [NSCFArray
insertObject:inSolutionsAtIndex:]: unrecognized selector sent to
instance.
The fundamental confusion is that you don't want to (and can't) make a
_property_ KVC/KVO compliant. You want to make your _class_ KVC/KVO
compliant _for_ a property. It doesn't make any sense to think about
a mutable array being KVO compliant for, uh, it's contents. You need
to make a class, which has a mutable array property, KVO-compliant for
that property.
So, the method insertObject:in<Key>AtIndex: is not something you'd
invoke on the mutable array. It's something you'd invoke on the
object of the class with the property. The object, on receiving that
message, would do whatever it wants to keep track of the change
internally, and the KVO mechanism would automatically send out the
proper notifications.
So, you'd invoke:
[self insertObject:anObject inWidgetsAtIndex:0];
Honestly, I don't know if Objective-C 2.0's new property stuff is
smart enough to synthesize array mutation accessors. If not, you'd
manually implement the following on your class (not on NSMutableArray
via a cateogory!):
- (void)insertObject:(MyElementClass*)anObject inWidgetsAtIndex:
(unsigned int)index
{
[widgets insertObject:anObject atIndex:index];
}
Note where I said "would do whatever it wants to keep track of the
change internally", above. In my code snippet above, I assumed that
the class has an ivar "widgets" which is an NSMutableArray*. This is
not required by KVC, KVO, or anything. A class may back a property
with whatever implementation it wants. It's even possible for a class
to have a property with no backing storage, at all -- the class could
synthesize the value for the property dynamically.
This is an important concept to grasp, because if you understood it
you wouldn't have been tempted to make the ivar KVO-compliant. KVO is
concerned with the properties (a.k.a. keys) of the class, not with the
implementation of their backing storage. (Yes, KVC and KVO will fall
back to direct access to ivars if necessary, but that's not their
purpose.)
do I have to add all the "indexed accessor methods" to NSArray and
NSMutableArray in a category?
Definitely no.
That generates another question: in that case, must I program for
manual observing by bracketing the implementation statements with
[self willChangeValueForKey ..etc] messages?
You can do that, instead, if you prefer. However, you should use the
will/didChange:valuesAtIndexes:forKey: form when mutating arrays, for
efficiency. Just using willChangeValueForKey: tells observers that
you've replaced the property's value wholesale. The other form allows
KVO to inform observers of just the specific changes you've made.
Another alternative is to obtain a proxy object for your mutable array
property using [someObject mutableArrayValueForKey:@"widgets"], where
someObject is an object of a class with a "widgets" property. When
invoked from within that class, you can use self. Any mutation
operations you perform on the proxy are performed in a KVO-compliant
manner. If your class has existing KVO-compliant mutating accessors
as described above, the proxy will use those. If it doesn't, the
proxy may call set<Key>:, which is inefficient. Otherwise, it will
directly manipulate your ivar, and perform the will/didChange:...
calls itself.
Finally I tried:
myArray = [[NSMutableArray arrayWithCapacity:10]
mutableArrayValueForKey:@"myArray"];
[myArray addObject:anObject];
But that did not work either, the NSArrayController not being
updated. Though no NSUndefinedKeyException was raised, so
mutableArrayValueForKey must have had some effect.
The above is very confused. First, you're creating a new empty
mutable array which has no relation to anything. Even if the rest of
the code were conceptually sound, you'd be accessing an object having
nothing to do with anything else. For example, who could possibly be
observing properties of that array?
Second, you ask this new array for one of its properties, named
"myArray". Now, NSMutableArray doesn't have any such property.
Again, what you mean to be doing is asking the object of your own
class for its "myArray" property (or, rather, a KVO proxy for that
property as described above). When coding that class, you would
obtain it with:
[self mutableArrayValueForKey:@"myArray"]
However, using the proxy in this case is just lazy. Since you're
coding the class, you should add the proper array mutation primitives
and invoke those.
Could someone possibly enlighten me? Thanks in advance.
I hope that helps.
Cheers,
Ken
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden