• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: KVC and KVO for arrays
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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


References: 
 >KVC and KVO for arrays (From: Hans van der Meer <email@hidden>)

  • Prev by Date: Re: Do i need custom views created programatically
  • Next by Date: Re: how to simulate passing parameter with selector
  • Previous by thread: Re: KVC and KVO for arrays
  • Next by thread: Re: KVC and KVO for arrays
  • Index(es):
    • Date
    • Thread