Re: bindings keypaths and collections
Re: bindings keypaths and collections
- Subject: Re: bindings keypaths and collections
- From: Quincey Morris <email@hidden>
- Date: Sun, 29 Jul 2012 12:03:34 -0700
On Jul 29, 2012, at 11:15 , William Squires <email@hidden> wrote:
> Ex: my GameEngine class has an @property (nonatomic, strong) Player *player;
>
> The Player class, in turn, has an @property (nonatomic, strong) NSMutableArray *inventory;
>
> and the following method
>
> -(void)addThing:(Thing *)theThing;
>
> If I want to refer to a 'Thing' in the player's inventory using a key path, how would I do it?
>
> i.e. [[GameEngine sharedEngine] valueForKeyPath:@"player.inventory???"] to return an id pointer to some Thing object in the player's inventory (let's say I want the first one - [inventory objectAtIndex:0])?
>
> Can I do @"[player.inventory objectAtIndex:0]" as the path?
There's no magic path here, just think it through step by step:
'[[GameEngine sharedEngine] valueForKeyPath:@"player.inventory"]' is a NSMutableArray. To get its first element, you'd write '[[[GameEngine sharedEngine] valueForKeyPath:@"player.inventory"] objectAtIndex: 0]'.
But don't do it this way anyway. By declaring "inventory" to be a mutable array, your Player class has given up control of its data. Any client of the class can reach in from the outside and change the inventory without the class having a say in the matter. This is equivalent to declaring a NSMutableArray instance variable @public.
The basic pattern I'd suggest is as follows:
@property (nonatomic,readonly) NSArray *inventory;
Note that this permits read-only, non-mutable access, thus hiding the internal class implementation from prying public eyes. If clients should be allowed to mutate the property, then implement the KVC mutation accessors in your class ('insertObject:inInventoryAtIndex:' etc), and clients can mutate the property like this:
[[player mutableArrayValueForKey: @"inventory"] addObject: thing];
and so on. Alternatively, provide a convenience property:
@property (nonatomic,readonly) NSMutableArray *mutableInventory;
that returns the 'mutableArrayValueForKey:' proxy, so that clients can write '[player.mutableInventory addObject: thing]'. Why bother mucking around with the proxy? Because it makes your "inventory" property KVO-compliant. Even if it happens not to matter right now, lack of KVO compliance will likely jump up and bite you later, and it's so easy to get it right now.
Note that both of the above property definitions are 'readonly'. Normally it isn't necessary to have a setter for a collection property, because if a client of your class wants to replace the entire contents of the property it can do so this way:
[player.mutableInventory removeAllObjects];
[player.mutableInventory addObjectsFromArray: otherThings];
or use one of the array 'replace' methods. If, for convenience, you really do want to provide setter-like behavior, then do this in place of the original definition:
@property (nonatomic,copy) NSArray *inventory;
and implement your setter to really copy the supplied array -- or, more likely, just do a simple removeAllObjects/addObjectsFromArray internally. Why do a copy, rather than having a '(nonatomic,strong)' property that just keeps the array it's given? Because in that case, something outside your class has a reference to the storage structure (the array) that your class is using, and can mutate it (destroying KVO-compliance, apart from the general havoc it could wreak on your class) at any time.
P.S. Bindings and NSArrayControllers always mutate values using the 'mutableArrayValueForKey:' proxy anyway, so you don't need or want to specify your property as "mutableInventory" in IB. "inventory" works just fine.
_______________________________________________
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