My method won't work - ios

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: ]

Related

Comparing objects in arrays to find event clashes in a timetable

I have 4 Event objects that would be displayed on a school timetable. I need to retrieve an array of the Events that clash with each other i.e. the start time of one Event is in between the start and end time of another. For the sake of getting the algorithm right first i have used int for the times instead of NSDate.
Event *event = [[Event alloc] init];
event.courseName = #"Maths";
event.room = #"405";
event.startTime = [NSNumber numberWithInt:8];
event.endTime = [NSNumber numberWithInt:10];
[eventsStore addObject:event];
Event *event2 = [[Event alloc] init];
event2.courseName = #"English";
event2.room = #"510";
event2.startTime = [NSNumber numberWithInt:10];
event2.endTime = [NSNumber numberWithInt:12];
[eventsStore addObject:event2];
Event *event3 = [[Event alloc] init];
event3.courseName = #"Computing";
event3.room = #"220";
event3.startTime = [NSNumber numberWithInt:11];
event3.endTime = [NSNumber numberWithInt:14];
[eventsStore addObject:event3];
Event *event4 = [[Event alloc] init];
event4.courseName = #"Sports";
event4.room = #"000";
event4.startTime = [NSNumber numberWithInt:13];
event4.endTime = [NSNumber numberWithInt:15];
[eventsStore addObject:event4];
Here is what i have at the moment to find the clashes:
int clashCounter = 0;
Event *curEv = nil, *otherEv = nil;
for(int i = 0; i < [eventsStore count]; i++)
{
curEv = [eventsStore objectAtIndex:i];
for (int j = 0; j < [eventsStore count]; j++)
{
if (j!=i && ![curEv.clashList containsObject:otherEv])
{
otherEv = [eventsStore objectAtIndex:j];
if (curEv.startTime < otherEv.endTime && otherEv.startTime < curEv.endTime)
{
clashCounter++;
NSLog(#"Clash: %# clashes with %#", curEv.courseName, otherEv.courseName);
[curEv.clashList addObject:otherEv];
}
}
}
}
Starts by creating two empty Event objects. Will loop through each Event in the eventsStore array, sets curEv to the objectAtIndex. Then loops through each of the eventsStore array objects that are not the curEv and does not have otherEv in its array. (each Event object contains an array called clashList for the Events it clashes with). If theres a clash then add otherEv to curEv.
When i run it i get:
ClashTest[3393:134117] Clash: English clashes with Computing
ClashTest[3393:134117] Clash: Computing clashes with English
ClashTest[3393:134117] Clash: Sports clashes with Computing
This is showing that the Event clashes are picked up but its storing a duplicate in the first instance(the English clashing with Computing goes both ways). But works as it is supposed to when it comes to the Sports and Computing clash because it only shows this once.
How can i get this working properly?
Apologies if this is deemed to specific, i've been messing around with this for a couple of weeks and haven't had much like no matter what ever way i try.
You could change the second loop to start with j = i+1 (and the first loop to stop at i < [eventsStore count] - 1). You would then go only over elements that are after the one checked.
I believe you could use a hash map to store the clashes, define this before your first for loop
NSMutableDictionary* clashes = [[NSMutableDictionary alloc] init];
Now when you find the clash (where you are logging it), check if the clash already exists both ways and if not, add it to the clashes dictionary like this (but you should have some unique id for the event, I'll assume it's an int e.g.)
NSString* firstHashKey = [NSString stringWithFormat:#"%d", curEv.Id];
NSString* secondHashKey = [NSString stringWithFormat:#"%d", otherEv.Id];
if (([clashes valueForKey:firstHashKey] == nil) && ([clashes valueForKey:secondHashKey] == nil)) {
clashes[firstHashKey] = #[ curEv, otherEv ];
}
else {
// It is already added to the clashes dictionary so no need to do it
}
Now when you want to print all the clashes you can simply do the following
for (NSString* key in clashes.allKeys) {
NSArray* value = clashes[key];
Event* firstEvent = value[0];
Event* secondEvent = value[1];
NSLog(#"Clash: %# clashes with %#", firstEvent.name, secondEvent.name);
}
The problem in the code is the initialization of otherEvent inside the inner loop...
for (int j = 0; j < [eventsStore count]; j++)
{
if (j!=i && ![curEv.clashList containsObject:otherEv]) // <-- look
{
// this happens too late to properly check the condition above
otherEv = [eventsStore objectAtIndex:j];
Change it like this...
for (int j = 0; j < [eventsStore count]; j++)
{
otherEv = [eventsStore objectAtIndex:j];
if (j!=i && ![curEv.clashList containsObject:otherEv])
{
and your logic will work. (Though there are quite a few opportunities for improvement, including shortening this inner loop as suggested above).

iOS App crashes at end of for loop

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.

NSMutableArray with int values from 1 to 100

This should be dead easy, but somehow it doesn't want to work for me. Using iOS 7 and XCode 5.
All I'm trying to do is create an array with values from 1 to 100.
NSMutableArray *array;
for (int i = 0; i < 100; i++)
{
[array addObject:i];
}
This doesn't work. I get a "Implicit conversion of 'int' to 'id' is disallowed with ARC.
I get it, I can't add primitive types to an NSMutableArray.
[array addObject:#i];
This doesn't work either. I get a "unexpected '#' in program"
[array addObject:[NSNumber numberWithInt:i]];
[array addObject:[NSNumber numberWithInteger:i]];
(either case) This "works" (compiles) but it really doesn't "work". The problem with this is that the value from NSNumber is really not a 1-100. What I get for each row is "147212864", 147212832", "147212840"...not what I want.
Lastly:
for (NSNumber *i = 0; i < [NSNumber numberWithInteger:100]; i++)
{
[array addObject:i];
}
This also doesn't compile. I get an error on the i++. "Arithmetic on pointer to interface 'NSNumber', which is not a constant size for this architecture and platform"
Any suggestions on how to do this extremely simple thing on obj-c?
Either one of these should work:
NSMutableArray *array = [NSMutableArray array];
for (int i = 0; i < 100; i++) {
[array addObject:#(i)];
}
or
NSMutableArray *array = [NSMutableArray array];
for (int i = 0; i < 100; i++) {
[array addObject:[NSNumber numberWithInt:i]];
}
Here are the reasons why your code snippets did not work:
[array addObject:i] - You cannot add primitives to Cocoa collections
[array addObject:#i] - You forgot to enclose the expression i in parentheses
NSNumber *i = 0; i < [NSNumber numberWithInteger:100]; i++ - You cannot increment NSNumber without "unwrapping" its value first.
If memory serves, I think you're simply missing parenthesis around the NSNumber shorthand expression.
NSMutableArray *array = [[NSMutableArray alloc] init];
for (NSUInteger i = 0; i < 100; i++)
{
[array addObject:#(i)];
}
Minimally, #i should be #(i) as described here. You are also forgetting to allocate and initialise your array
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0; i < 100; i++) {
[array addObject:#(i)];
}
And since you are getting: "147212864", 147212832", "147212840"...not what I want., I think you are probably printing out your information wrongly or because the array is unallocated, that's simply garbage. Can you show us how you are outputting?
NSMutableArray *array = [[NSMutableArray alloc] init];
NSNumber *myNum;
for (int i = 0; i < 100; i++) {
myNum = [[NSNumber alloc]initWithInt:i];
[array addObject:myNum];
}
NSLog(#"%#", array); // 1 - 99 as expected
Worked for me :)
Just saying: Turn on all reasonable warnings in your Xcode project. Then read what the warnings are saying and do something about them. When you write something like
for (NSNumber *i = 0; i < [NSNumber numberWithInteger:100]; i++)
What does a for loop do? An object is in the end a pointer. So you initalise i to nil. Then you compare a pointer with a random pointer: [NSNumber numberWithInteger:100] returns a pointer to an object which could be anywhere in memory, and you compare pointers. Next the i++: No, you can't increment a pointer to an NSNumber. It doesn't make sense.

UITextfield text in array will not change a label's text in another array

Please help. What am I doing wrong? textfield.text goes into one array and is suppose to change a label that belongs to another array.
I have multiple textfields. Im trying to save the text of each field to an array and then setAlpha to 0. Then I have an equal amount of labels that I want to change to the textfield's text. I have tried using mutable and immutable arrays. I keep getting errors.
I've tried multiple ways and its got to be something simply dumb I'm missing. I've greatly reduced these arrays just to post here.
_namesText = [NSMutableArray arrayWithObjects:_nameLabel1.text, _nameLabel2.text, nil];
_names = [NSMutableArray arrayWithObjects:_nameLabel1, _nameLabel2, nil];
_nameInputs = [NSMutableArray arrayWithObjects:_p1NameTextField, _p2NameTextField, nil];
_playerNameText = [NSArray arrayWithObjects:_p1NameTextField.text, _p2NameTextField.text, nil];
enter code here
- (IBAction)enterNamesButton:(id)sender {
//These don't work.
for (int i = 0; i < _numberOfPlayers; i++) {
[_names[i] setAlpha:1];
NSString *tempString = [[NSString alloc]initWithString:[_nameInputs[i] text]];
[_lastTry addObject:tempString];
}
//Then tried this. This is after trying for 2.5 hours and different coding.
for (int i = 0; i < _numberOfPlayers; i++) {
_namesText[i] = _lastTry[i];
UILabel *cell = [[UILabel alloc] init];
cell.text = _lastTry[i];
//[[_namesText[i] textLabel] text] = #"Idiot";
_namesText[i] = cell;
NSLog(#"%#", _namesText[i]);
// This works (but bad practice) and I want to loop through arrays that each UI element belongs to instead of typing it all out.
// _nameLabel1.text = _p1NameTextField.text;
// _nameLabel2.text = _p2NameTextField.text;
I expect this to work but NOPE!!!!
Assuming the following:
Array _fromTextField is the textfield you want to copy from and set Alpha to 0
Array _toLabelField is the label you want the text to appear in
Array _toStoreStrings is where you keep all the strings, for later maybe ?
NSMutableArray *_fromTextField = [[NSMutableArray alloc] init]; // Put the Text Fields in Here
NSMutableArray *_toLabelField = [[NSMutableArray alloc] init]; // Put the Labels in Here
NSMutableArray *_toStoreStrings = [[NSMutableArray alloc] init];
int count = 0;
int overRun = [_toLabelField count];
for (UITextField *tf in _fromTextField)
{
tf.alpha = 0;
NSString *tempString = [[NSString alloc]initWithString:tf.text];
[_toStoreStrings addObject:tempString];
if (count < overRun) // Check just in case Array sizes not same
{
UILabel *lb = [_toLabelField ObjectAtIndex:count];
lb.text = tempString;
}
else
break;
count++;
}
The issue your code faces is you have an array of NSObject. The compiler has no idea they are UITextField or UILabel unless you cast them with (type_cast *) or point a new typed variable at them. Hence the crashes you were seeing.
I would do it this way:
for (int i = 0; i < _numberOfPlayers; i++) {
_namesText[i] = _lastTry[i];
}
I cannot see why this won't work... does it?

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