On Jul 29, 2012, at 11:15 , William Squires <wsqui...@satx.rr.com> 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 (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to