iOS App crashes at end of for loop - ios

I'm pretty new to Objective C, and I've been having quite the roadblock in my code...
I am making a (very) simple card game to run on iOS. All you are supposed to do is draw cards until a Joker is drawn, then the game ends.
My app has been crashing at a curly bracket at the end of a for loop that is making card objects to put into the array
Here's my Deck.m:
#import "Deck.h"
#import "Card.h"
#implementation Deck
#synthesize deckArray;
- (void)removeCard {
[deckArray removeObjectAtIndex:deckArray.count];
}
- (void)generate {
NSLog(#"Generating deck...");
NSMutableArray *deckArray = [[NSMutableArray alloc]init];
for (int i = 0; i < 14; i++) {
//Make a card
Card *card = [[Card alloc]init];
//Give it some values
card.value = i + 1;
card.suit = #"Hearts";
//Put card in array
[deckArray addObject:card];
NSLog(#"Added object (%#) to array",card);
}
for (int i = 0; i < 14; i++) {
//Make a card
Card *card = [[Card alloc]init];
//Give it some values
card.value = i + 1;
card.suit = #"Spades";
//Put card in array
[deckArray addObject:card];
NSLog(#"Added object (%#) to array",card);
}
for (int i = 0; i < 14; i++) {
//Make a card
Card *card = [[Card alloc]init];
//Give it some values
card.value = i + 1;
card.suit = #"Diamonds";
//Put card in array
[deckArray addObject:card];
NSLog(#"Added object (%#) to array",card);
}
for (int i = 0; i < 14; i++) {
//Make a card
Card *card = [[Card alloc]init];
//Give it some values
card.value = i + 1;
card.suit = #"Clubs";
//Put card in array
[deckArray addObject:card];
NSLog(#"Added object (%#) to array",card);
} //Crashes on this line: Thread 1: Breakpoint 2.1
NSLog(#"Generated deck");
}
- (Card *)topCard {
return [deckArray objectAtIndex:deckArray.count];
}
#end
I'm pretty sure it is the most obvious thing in the world but, again, I am pretty new on programming in general
Thank you in advance!

I can see issue in tow methods: removeCard and topCard.
You try access object which is out of bounds:
[deckArray objectAtIndex:deckArray.count]
If you have 5 items in the array deckArray.count returns 5 but last index is 4 (arrays index are zero based) so you have to replace call to deckArray.count to deckArray.count-1.
- (Card *)topCard {
return [deckArray objectAtIndex:deckArray.count-1];
}
And do the same in removeCard method.

This line : NSMutableArray *deckArray = [[NSMutableArray alloc]init];
You already declared deckArray in your header file(.h).
You should do: deckArray = [[NSMutableArray alloc] init];

As told in the Crash log, I had my 'remove card' method take away an object that didn't exist because arrays are zero-based. Also, I put self. in front of deckArray seemed to help. I should've learned a bit more Obj-C before posting this question, so my apologies to you. .count - 1 also helped on the other methods.

Related

unsure of (id)init explanation

So this is fairly beginner, but I'm trying to really grasp this concept. So in my sample code I have an Init method with two Constants defined at the beginning of the .m :
#define NUM_TOP_ITEMS 5
#define NUM_SUBITEMS 6
- (id)init {
self = [super init];
if (self) {
topItems = [[NSArray alloc] initWithArray:[self topLevelItems]];
subItems = [NSMutableArray new];
currentExpandedIndex = -1;
for (int i = 0; i < [topItems count]; i++) {
[subItems addObject:[self subItems]];
}
}
return self;
}
What is the init method doing here? Could i initiate the two constants in their separate alloc/init methods? Any clarity here would be great! thanks
OK, line by line...
#define NUM_TOP_ITEMS 5 // defining a macro for the number 5
#define NUM_SUBITEMS 6 // and 6
- (id)init
{
self = [super init]; // here you are initialising the object by calling the super class method
// this is important as you still want the functionality/properties etc... from the super class.
if (self) { //check the object actually exists.
topItems = [[NSArray alloc] initWithArray:[self topLevelItems]]; //This looks like it's running a method called topLevelItems
// that returns an NSArray. You are then saving it in an iVar (I guess)
subItems = [NSMutableArray new]; // this creates a new mutable array in an iVar called subItems
currentExpandedIndex = -1; // this sets an iVar int value
for (int i = 0; i < [topItems count]; i++) { // set up a for loop for the topItems array
[subItems addObject:[self subItems]]; //...wat?
// not quite sure what's going on here.
// I don't think it's doing what you think it's doing
}
}
return self; // return the object whether it has been allocated successfully or not.
}
Constants defined using #define are like a search and replace for tokens which happens at compile time. This is the C pre-processor, it means that code like this,
#define NUM_TOP_ITEMS 5 + 1
int i = NUM_TOP_ITEMS + NUM_TOP_ITEMS;
gets pre-processed to something like this,
int i = 5 + 1 + 5 + 1;
before the next compilation step happens.

Travel through NSArray without using loop but cursors?

I have an NSArray with objects inside (CLLocation). I can have 50, 100, 300 or more objects inside. In fact, this array is used while the user walking and can follow a direction. But the user can start at the middle of my NSArray, you know what I mean ?
Well, I Have to loop all the time my array to know where my user is compare to the locations in my array.
My question is: Is it possible to use a thing like in Java with a "cursor" in a list, and simply call "Next object" to travel in my array instead of loop ?
Because I need that the user walk on all location of my array.
Example:
Count of my array: 100
User start at location at index 34 (the nearest location found)
The user must do 35, 36, 37... 100 AND 0,1,2,3 ... until 33.
Hope it's clear, I really don't know how to do this without using for loop...
Thank you for help and suggestions!
Regards,
Lapinou.
Here's one way:
NSArray * arr = ... yourArray
int index = [arr indexOfObject:currentLocation];
index ++;
if (index == arr.count) index = 0;
id nextLocation = arr[index];
Another might be to create a global counter variable that stores the current position. If these needs to last after the user closes the app, you could write it to user defaults
Is looks like you are want to use NSEnumerator
NSEnumerator Class Reference
NSArray *anArray = // ... ;
NSEnumerator *enumerator = [anArray objectEnumerator];
id object;
while ((object = [enumerator nextObject])) {
// do something with object...
}
try this:
#interface ArrayEnumerator : NSEnumerator
{
NSArray* array;
NSInteger index;
NSInteger startIndex;
BOOL over;
}
- (id)initWithArray:(NSArray*)anArray
atIndex:(NSInteger)anIndex;
#end
#implementation ArrayEnumerator
- (id)initWithArray:(NSArray*)anArray
atIndex:(NSInteger)anIndex
{
if (self=[super init]) {
array = anArray;
index = anIndex;
startIndex = anIndex;
over = NO;
}
return self;
}
- (id)nextObject
{
if (index == [array count]) {
index = 0;
over = YES;
}
if (over && index == startIndex)
return nil;
return array[index++];
}
- (NSArray*)allObjects
{
return array;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
NSArray* array = #[#0,#1,#2,#3,#4,#5,#6,#7,#8,#9];
id element;
ArrayEnumerator* enumerator = [[ArrayEnumerator alloc] initWithArray:array atIndex:4];
while (element = [enumerator nextObject])
NSLog(#"%#", element);
}
#end
What is wrong with a for loop?
An iterator is typically used on lists, because you can't access elements in a list by index. However you are working with an array, so you don't need an iterator, but rather some clever way of accessing the array in the desired order.
Maybe this code can provide you with some ideas. This will run from 34 to 100, then start with 0 and go up to 33.
for (int i = 34; i < 134; i++)
{
int ix = i % 100;
id whatever = arr[ix];
}
You can access array elements by index:
CLLocation * myLocation = myArray[34];
(or)
int i = 34;
CLLocation * myLocation = myArray[i];

Quiz application - How can I choose 3 wrong answers to show from an array of 6 wrong answers?

I am trying to make a quiz app and I am new to IOS.I want to get 3 wrong answers from an array of 6 wrong answers for a specific question and there will be 1 correct answer for each question.I want to randomize the wrong options every time the question is shown.So every time the question is shown the app will choose 3 wrong options from the array and 1 correct answer.How can I code this?I would be glad if you can help me on this.
Thank you..
{
int counted = [theOptions count];
int i;
NSMutableArray *indexes = [[NSMutableArray alloc] initWithCapacity:counted];
for (i=0; i<counted; i++) [indexes addObject:[NSNumber numberWithInt:i]];
NSMutableArray *shuffle = [[NSMutableArray alloc] initWithCapacity:counted];
while ([indexes count])
{
int index = rand()%[indexes count];
[shuffle addObject:[indexes objectAtIndex:index]];
[indexes removeObjectAtIndex:index];
}
NSMutableArray* shuffledOptions = [[NSMutableArray alloc] initWithCapacity:4];
for (int i=0; i<counted; i++)
{
int randomIndex = [[shuffle objectAtIndex:i] intValue];
[shuffledOptions addObject:[theOptions objectAtIndex:randomIndex]];
UIButton* optionButton = [_optionsButtonsArray objectAtIndex:i];
optionButton.tag = randomIndex;
}
return shuffledOptions;
}
return theOptions;}
If you have an array of 6 wrong answers and want to randomly select three of them
NSArray *answers = [NSArray arrayWithObjects:#"Wrong #1", #"Wrong #2", #"Wrong #3", #"Wrong #4", #"Wrong #5", #"Wrong #6"];
NSMutableArray *output = [[NSMutableArray alloc] init];
int r;
while (output.count < 3) {
r = arc4random_uniform(5);
if (![output containsObject:[answers objectAtIndex:r]]) {
[output addObject:[answers objectAtIndex:r]];
}}
The easiest way would be select a random number between 0-5 then select another random one. If the next one is a duplicate of the first one repeat the previous step. Do this one more time and you now have three unique random numbers from 0-5.
A more clever way would be to store numbers 1-5 in an array. Shuffle the array and then return the first three indexes.
Good luck.

My method won't work

I'm a total noob and need help with a method I'm writing.
The method creates a deck of cards (using NSMutableArray). I'm first experimenting with loading the array with numbers 1-13 randomly (each number appearing once).
When I run a simple test program to print the values in the array, I get a "Build Successful" but an error once the program starts. The error says "[__NSArrayM insertObject:atIndex:]: object cannot be nil".
Once I understand what I'm doing wrong, I can then expand on the method properly. Thanks!
NB: This is my first post. Is this type of question ok?
- (void) createDeck {
int r;
BOOL same;
deck = [[NSMutableArray alloc]init];
NSNumber *randNum;// = nil;
randNum = [[NSNumber alloc]init];
[randNum initWithInt: (arc4random()%13)+1];
[deck addObject: randNum]; // First card added to deck
same = FALSE;
while (!same) {
for (int i=1; i<13; i++) {
same = FALSE;
for (r=0; r<=i; r++) {
[randNum initWithInt: (arc4random()%13)+1];
if ([deck objectAtIndex:r] == [deck objectAtIndex:i]) {
same = TRUE;
}
[deck addObject: randNum]; // Next card added to deck
}
}
}
}
you cannot re-init randNum:
NSNumber *randNum;// = nil;
randNum = [[NSNumber alloc]init];
[randNum initWithInt: (arc4random()%13)+1];
and the third line is missing the assignment anyway. just do:
NSNumber *randNum = [[NSNumber alloc] initWithInt:(arc4random()%13)+1];
and put that inside the inner for loop, like this:
BOOL same = FALSE;
NSMutableArray *deck = [[NSMutableArray alloc] initWithCapacity:13];
[deck addObject:[[NSNumber alloc] initWithInt:(arc4random()%13)+1]]; // First card added to deck
while (!same) {
for (int i = 1; i < 13; i++) {
same = FALSE;
for (int r = 0; r <= i; r++) {
NSNumber *randNum = [randNum initWithInt:(arc4random()%13)+1]; // modern ObjC will assign a register for this outside the while loop, but restrict the variable's scope to the inner-most loop
if ([deck objectAtIndex:r] == [deck objectAtIndex:i])
same = TRUE;
[deck addObject: randNum]; // Next card added to deck
}
}
Note that I haven't thought through the logic of what you are trying to do here, I've only attempted to resolve the NULL object reference. The error was referring to the first line [deck addObject: randNum] outside of the loop.
Try to use this line of code where you using NSNumber
NSNumber * randNum = [NSNumber numberWithInt: (arc4random%13)+1];
Instead of
[[NSNumber alloc] initWithInt: ]

Method does not return what it should when called

I have a piece of code that returns an object of type Card like so:
-(Card*) drawRandomCard {
if ([self.cards count]) {
unsigned index = arc4random() % self.cards.count;
Card *randomCard = self.cards[index];
[self.cards removeObjectAtIndex:index];
NSLog(#"%#", randomCard.description); **//returns a description and not null**
return randomCard;
} else {
NSLog(#"nil");
return nil;
}
}
This actually works fine when I use it in other functions, but there is a problem with this one below:
- (IBAction)addCards:(UIButton *)sender {
int numberOfCardsToAdd = EXTRA_CARDS_NUMBER;
if ([[self.collectionView visibleCells] count] == 0) {
numberOfCardsToAdd = self.startingCardCount;
}
for (int i = 0; i < numberOfCardsToAdd; i++) {
if (!self.game.deck.isEmpty){
Card *cardToAdd = [self.game.deck drawRandomCard];
NSLog(#"%#", cardToAdd.description); **// gives (null)**
if (cardToAdd) { **// this does not get called**
// do stuff with cardToAdd
}
}
}
[self.collectionView reloadData];
}
So for some reason it seems my drawRandomCard, when called with the method above, does not work. Does anyone know how to fix this?
Thanks very much!
Edit: my method for initializing the game object that has the deck property:
- (id) initWithNumberOfCards: (int) numberOfCards withDeck: (SetsPlayingDeck *) deck {
self = [self init];
self.score = 0;
_deck = deck;
_cards = [[NSMutableArray alloc] initWithCapacity:numberOfCards];
for (int i = 0; i < numberOfCards; i++) {
[_cards addObject: [_deck drawRandomCard]];
}
return self;
}
This method is called right at the start of the program.
Edit #2:
Here is the code that initializes the game object, along with a deck object that is its property:
- (SetsGame *) game {
if (!_game) {
SetsPlayingDeck *deck = [[SetsPlayingDeck alloc] init];
_game = [[SetsGame alloc] initWithCardCount:self.startingCardCount usingDeck:deck];
}
NSLog(#"%#", _game.deck.description); **// this returns null!!**
return _game;
}
As far as I can see, the issue should lie with the method initWithCardCount:usingDeck:, where you probably forgot to assign the deck argument to the deck property (or for some reason the assignment is not executed):
_game = [[SetsGame alloc] initWithCardCount:self.startingCardCount usingDeck:deck];
So, please review this or post that method definition.
Maybe the problem is that you're running out of cards. Inside your init method you have the following line inside a for-loop:
[_cards addObject: [_deck drawRandomCard]];
And inside your drawRandomCard method you have the following line:
[self.cards removeObjectAtIndex:index];
I'm assuming that _cards(1st line) and self.cards(2nd line) are different objects.
Probably, by the time you call Card *cardToAdd = [self.game.deck drawRandomCard]; inside your addCards: method you don't have any cards left.
Try to set a breakpoint and step in the for-loop and let me know,
Hope this helps!

Resources