2
votes

Ok so I've recently decided to try to teach myself Objective-C (I'm interested in iPhone development), however I've never used C or any of its derivatives before, and as such am running into some problems. I decided to start out by writing a very basic card application that creates a deck of cards, shuffles the deck, and then displays the cards on the screen using UIButtons, however I'm having a problem with my shuffling algorithm. Every time it gets called I get an EXC_BAD_ACCESS error, which I know means there's something desperately wrong with my code, but I just can't figure out what it is.

- (void) randomize {
    NSMutableArray *tmpDeck = [[NSMutableArray alloc] init];
    for(Card *tmp in _cards) {
        BOOL didInsert = NO;
        while(!didInsert) {
            NSUInteger random = arc4random_uniform(54);
            if([[tmpDeck objectAtIndex:random] isEqual:nil]) {
                [tmpDeck insertObject:tmp atIndex:random];
                didInsert = YES;
            }
        }
    }
    _cards = tmpDeck;
    _hasBeenRandomized = YES;
}

_cards is a pointer to an NSMutableArray containing the unshuffled deck of card objects, and _hasBeenRandomized is a boolean (obviously) that keeps track of whether or not the deck has been randomized.

I've tried to use the debugger to work out what exactly is going on here, but I can't even step into the method without my program crashing. This leads me to believe that the problem has to come from the very first line, but it's just a straightforward creation of an NSMutableArray, so I don't know how it could be that. This method is being called from within viewDidLoad. This is the entirety of the viewDidLoad method currently.

- (void)viewDidLoad
{
    [super viewDidLoad];
    _deck = [[Deck alloc] init];
    [_deck randomize];
}

Any and all help will be appreciated. Sorry if the answer is dead obvious.

1

1 Answers

1
votes

This is because you are trying to insert into an index that doesn't exist yet. You need to initialize the array with as many places in the array as you need for your cards. Either that or use a NSMutableDictionary and just insert the object with the index being the key.

To add another note, calling initWithCapacity on the array wouldn't solve this for you either since this just gives a "hint" at the size. You need the count property of the array to actually be at least as large as the index you are trying to insert. If you wanted to do an array, then you would first need to populate something in each index first. You could define this in the new array literal format or use a for loop that loops the number of times you need (your max index) and insert a dummy object in it's place.

for (int i=0; i< _cards.count; ++i) 
{
    [tmpDeck insertObject:@"dummy" atIndex:i];
}

Then instead of checking for 'nil' before you replace, you check if it is equal to the dummy object you inserted. This would give you an array that you can insert into any of these indexes. I personally would still probably store them in an NSMutableDictionary. But if you need it in an array for some other purpose then this is a way to do it.

You also will need to be sure to replace the object instead of inserting, otherwise you will just keep adding indexes.

[tmpDeck replaceObjectAtIndex:random withObject:tmp];

If you still get the same error, set a breakpoint in your debugger and check what the random number is and what the count of your array is. If your random number is ever greater than your array count, then you will get this error.