开发者

Cocoa binding to single object from an array

I previously posted this question as a comment on a related thread thinking it was simple. That thread is here:

Cocoa binding to a particular item in an array controller

The questions relates to (and I'll more fully describe it here) a game I'm building to try and learn objective-c and cocoa. Its good enough to think of it like texas hold-em poker. One server holds the game information and manages input from a variable number of clients (always more than one). Through cocoa bindings, it displays to each player the public information of the game which is stored in an array on the server using an array controller in IB. Think of the five cards on the table being stored in an NSArray on the server and bound to the content field of an NSArrayController for each client.

This part works fine, like a charm. However, each player has two cards that he needs to keep private. Each client should display a different card depending on what was dealt to that particular player. (Because what is really happening is I'm binding to an array of player objects

NSArray * thePlayers, 

imagine all the cards being stored on the same array). So my question is, how do I set up bindings to a single object out of the array controller (or do I need some other controller)? That is, how to I bi开发者_Go百科nd to one player of thePlayers array?'


You set up a property in the controller or model to access that particular player and bind to that. There is no way to bind directly to an object at a particular index in an array.


If you do want to bind to specific array indices, you could could create a wrapper object. Something like this. It lets you bind to item0, item1 and so on. There is no range checking and it breaks if you change the size of the array, but you get the idea.

Interface

@interface MyArrayBinder : NSObject {
    NSMutableArray *array;
}
- (id)initWithMutableArray:(NSMutableArray *)theArray;
- (NSMutableArray *)array;
@end

Implementation

#include <objc/runtime.h>

static NSInteger _indexFromSelector(SEL sel) {
    return [[NSStringFromSelector(sel) stringByTrimmingCharactersInSet:[NSCharacterSet letterCharacterSet]] integerValue];
}

static void _dynamicSetItem(MyArrayBinder *self, SEL sel, id obj) {
    [self.array replaceObjectAtIndex:_indexFromSelector(sel) withObject:obj];
}

static id _dynamicItem(MyArrayBinder *self, SEL sel)  {
    return [self.array objectAtIndex:_indexFromSelector(sel)];
}

@implementation MyArrayBinder

- (id)initWithMutableArray:(NSMutableArray *)theArray {
    self=[super init];
    if (self) {
        array=theArray;
        for(NSUInteger i=0; i<[array count]; i++) {
            class_addMethod([self class], NSSelectorFromString([NSString stringWithFormat:@"item%lu", i]), (IMP) _dynamicItem, "@@:");  
            class_addMethod([self class], NSSelectorFromString([NSString stringWithFormat:@"setItem%lu:", i]), (IMP) _dynamicSetItem, "v@:@");  
        }       
    }
    return self;
}

- (NSMutableArray *)array {
    return array;
}

@end


However, each player has two cards that he needs to keep private. Each client should display a different card depending on what was dealt to that particular player. (Because what is really happening is I'm binding to an array of player objects …

The client knows which player it's representing, right? Not by index—it should have a direct reference to the Player object for the player sitting at its keyboard. Something like MyPlayer *userPlayer;. This is in addition to the dealer object holding an array of all the players, including that one.

Once you have it lain out that way, with the client controller having a property whose value is the user's Player object, the binding becomes simple: You'll bind the card views directly to card A and card B of the userPlayer property of the client controller. (This is essentially what Chuck already suggested in his answer, and what I suggested in my comment on your answer on that other question.)

imagine all the cards being stored on the same array).

Why would I want to imagine that? Why don't the players own their own cards separately?

OK, so the dealer should own all the cards (that is, the deck). It should co-own those also held by a player. The players don't access their cards through the dealer; each player should directly hold his or her cards.

It sounds like you made the same mistake with cards as with players: Thinking that one object can/should know another through an array by index. You can't—certainly not if you want to use that knowledge with Bindings—and shouldn't. The one object needs to know the other directly. This is not only the correct solution, it's the correct way for objects to know each other. Any array-index-based reference would be more complex for no benefit.


Very similar to Nick Moore's solution:

If you do want to bind to specific array indices, you could create a wrapper object. Something like this. It lets you bind to item0, item1 and so on. There is no range checking and it breaks if you change the size of the array, but you get the idea.

Interface

@interface MyArrayBinder : NSObject
@property NSMutableArray *array;
- (id)initWithMutableArray:(NSMutableArray *)theArray;
@end

Implementation

static NSInteger _indexFromString(NSString *key) {
    return [[key stringByTrimmingCharactersInSet:[NSCharacterSet letterCharacterSet]] integerValue];
}

@implementation MyArrayBinder

- (id)initWithMutableArray:(NSMutableArray *)theArray {
    if ( self=[super init] ) {
        _array=theArray;
    }
    return self;
}

- (id)valueForUndefinedKey:(NSString *)key {
    return _array[_indexFromString( key )];
}

- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    _array[_indexFromString( key )] = value;
}

@end
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜