views:

152

answers:

3

I have a class called "CardSet", containing an NSMutableArray* cardSet to hold "cards", which I extend to make "DeckCards". I'd like "CardSet" to have a method called "(void)addCard:(Card*)" (and similarly a method "removeCard"). I'd like "addCard" to some how have access to and set cardSet. Even better I'd like to use the "addCard" method to initialise cardSet. The class file "CardSet.h" reads:

 #import < Cocoa/Cocoa.h >

 #import < Card.h >

@interface CardSet : NSObject {

    NSMutableArray* cardSet;

}

-(id)init;

-(NSMutableArray*)getCardSet;

-(void)setCardSet:(NSMutableArray *)new_cardset;

-(Card*)getCard:(NSInteger) index;

**-(void)addCard:(Card*) new_card;**

-(void)removeCard:(Card*) old_card;

-(void)dealloc;

@property (readwrite, retain, getter=getCardSet, setter=setCardSet) NSMutableArray* cardSet;

@end

and the method file reads:

 #import "CardSet.h"

@implementation CardSet

-(id)init{
    if( self = [super init] ){}   //will add initialisations here later
    return self;
}

-(NSMutableArray*)getCardSet{
    return cardSet;
}

-(void)setCardSet:(NSMutableArray *)new_cardSet{
    cardSet = new_cardSet;
}

-(Card*)getCard:(NSInteger)index{
    return [cardSet objectAtIndex:index];
}

**-(void)addCard:(Card *)new_card{
    [cardSet addObject:new_card];
}**

-(void)removeCard:(Card *)old_card{
    [cardSet removeObject:old_card];
}

-(void)dealloc{
    [cardSet release];
    [super dealloc];
}

@synthesize cardSet;

@end

This compiles just fine. I'd like to initialise a "DeckCards" instance using its "addCard" method 52 times. When I call addCard 52 times in a DeckCards setter method, and ask for the size of its "cardSet", I'm returned 0.

This appears to be a scope or privileges problem? Can the "addCard" method have any setter privileges? Must a setter argument be the same as the return and respective member type?

[I can work around the above by creating an NSMutableArray object "deck_cards_temp" outside of "DeckCard", add 52 cards to this, and pass it to set the member of my "DeckCards" instance via the setter inherited from "CardSet". This is not very satisfactory!]

What do you advise? Many thanks in advance for your help and patience.

+3  A: 

You are never actually creating the cardSet object. You should be creating it in your -init method:

-(id)init
{
    if( self = [super init] )
    {
        cardSet = [[NSMutableArray alloc] init];
    }
    return self;
}

Because you never actually create the array, all the calls to -addCard: are being sent to a nil object.

When you pass in an array to -setCardSet:, you are passing in an initialized array so the array is no longer nil and the -addCard: calls work fine.

Rob Keniger
Thanks for the reply. This is actually commented on in the script to some extent -- I don't think you need to instantiate the NSMutableArray in init, XCode just tells me the array is unused. I've also remarked that passing an array to the setter works just fine but it's unattractive.
SpecialK
Actually what you're saying is a little different... I'll check it out, you're probably right!
SpecialK
You definitely need to instantiate the array if you want to use it. Until you assign a valid array to the `cardSets` instance variable, its value is `nil` and all messages to `nil` are ignored. Some of the other answers have provided information on improving your implementation, but the lack of initialization is the core of your problem.
Rob Keniger
Don't assume that -init will only be invoked once. Code defensively.
NSResponder
A: 

CardSet.h

#import <Cocoa/Cocoa.h>

// For know we just need to know there is a class named "Card" being used but implemented later
@class Card;

@interface CardSet : NSObject {
    NSMutableArray *cardSet;
}

// Here are the methods according to "correct" naming conventions
- (Card *)cardAtIndex:(NSInteger)index;
- (void)addCard:(Card *)card;
- (void)removeCard:(Card *)card;

// This will help us and forget about writing the setter/getter
@property (nonatomic, retain) NSMutableArray *cardSet;

@end

CardSet.m

#import "CardSet.h"
// Now we tell the compiler what "Card" is and what methods etc. it has
#import "Card.h"

@implementation CardSet

@synthesize cardSet;

- (id)init {
    if (self = [super init]) {
        // If we don't create the cardSet, how are we able to work with it!?
        NSMutableArray *anArray = [[NSMutableArray alloc] init];
        self.cardSet = anArray;
        [anArray release];
    }
    return self;
}

- (Card *)cardAtIndex:(NSInteger)index {
    return [cardSet objectAtIndex:index];
}

- (void)addCard:(Card *)card {
    [cardSet addObject:card];
}

- (void)removeCard:(Card *)card {
    [cardSet removeObject:card];
}

- (void)dealloc {
    [cardSet release];
    [super dealloc];
}

@end

As Abizern already noted: Naming the array the same as your class is a bad thing.

bddckr
As noted in my comment to NSResponder's answer, you should not use accessors when setting the value of an instance variable in an initializer method. Just set it directly, as in my answer.
Rob Keniger
Oh you're right. Thanks for pointing that out. So does this mean the accessors aren't properly set up in the init method? When exactly it is safe to use them?
bddckr
A: 

I would shorten that init method:

- (id)init {
    if (self = [super init]) {
        // If we don't create the cardSet, how are we able to work with it!?
        self.cardSet = [NSMutableArray array];
    }
    return self;
}
NSResponder
You generally should avoid the use of accessor methods in initializers. Just set the ivar directly.
Rob Keniger