0
votes

I'm following along with the Stanford ios 7 course (in the third lecture) where the instructor builds a card matching game. There are twelve cards on the screen and they are hooked upto this property in the ViewController

@property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *cardButtons;

and when any of the buttons are clicked, this method is triggered

- (IBAction)touchCardButton:(UIButton *)sender {
      NSLog(@"touchCardbutton");

    int cardIndex = [self.cardButtons indexOfObject:sender];
    [self.game chooseCardAtIndex:cardIndex];
    [self updateUI];

}

which triggers updateUI in the viewController

- (void)updateUI{
    NSLog(@"updateUI");
    for (UIButton *cardButton in self.cardButtons){
        int index = [self.cardButtons indexOfObject:cardButton];
         NSLog(@"index in UpdateUI %d", index);
        Card *card = [self.game cardAtIndex:index];
        NSLog(@"card in UpdateUI %@", card);
        [cardButton setTitle:[self titleForCard:card ]forState:UIControlStateNormal];
        [cardButton setBackgroundImage:[self backgroundImageForCard:card]   forState:UIControlStateNormal];
        cardButton.enabled = !card.isMatched;

    }
}

In this updateUi method, the second NSLog statement is showing that card is nil. The first NSLog statement is showing the index without a problem. So why is card nil? I'm assuming that there's some problem with the cardAtIndex method in the cardMatchGame class referred to by this property in the view Controller

from the viewController @property (strong, nonatomic) CardMatchingGame *game;

cardAtIndex

-(Card *)cardAtIndex:(NSInteger)index
{
    NSLog(@"cardAtIndex %d", index);
    return (index < [self.cards count]) ? self.cards[index] : nil;


}

This NSLog statement is not showing in the console, so it doesn't appear anything's happening when I call cardAtIndex in updateUI

Card *card = [self.game cardAtIndex:index];

Can you explain why the method cardAtIndex might not be getting called while there is also no error message when I build and run?

Update

In the view controller, the game property is lazily instantiated like this

-(CardMatchingGame *)game
{
     if (_game) _game = [[CardMatchingGame alloc] initWithCardCount:[self.cardButtons count] usingDeck:self.createDeck];

    return _game;
}
1
Log self.game. I guess you get nil and you forgot to assign it somewhere. - Wain
cardAtIndex will also return nil if you request a card index higher than the number of elements in the array - so your array may not be loaded correctly. Try setting a breakpoint at cardAtIndex and see what is happening in that method - Paulw11
@Wain logging self.game said it was NULL. At the top of the ViewController I set this property @property (strong, nonatomic) CardMatchingGame *game; which I thought would let me call self.game without need to assign anything else - BrainLikeADullPencil
The @property just gives you an instance variable to store something in, it doesn't assign it to anything... - Wain
@Paulw11 i set a breakpoint but it's not getting triggered, which isn't surprising because the nslog in it also wasn't logging - BrainLikeADullPencil

1 Answers

3
votes

You self.game reference is nil so no call is made. No warning / error is thrown as calling nil is defined to do nothing.

You issue appears to stem from a logic issue in your accessor method which should be:

- (CardMatchingGame *)game
{
    if (!_game)
        _game = [[CardMatchingGame alloc] initWithCardCount:[self.cardButtons count] usingDeck:self.createDeck];

    return _game;
}

Note the addition of the !

It's usually better not to shortcut and use if (!something) but to be explicit and use if (something == nil) because it's clearer and faster to understand what's going on.