Can't Add Object to NSMutableArray - objective-c

I have a class called KCBlackjack. The class contains this code:
playerHand = [[NSMutableArray alloc] init];
dealerHand = [[NSMutableArray alloc] init];
blackjack = [[KCBlackjack alloc] initWithNumberOfDecks:6];
[self deal];
[blackjack dealTo:playerHand dealer:dealerHand];
- (void)dealTo:(NSMutableArray *)player dealer:(NSMutableArray *)dealer {
// How many cards are left
NSLog(#"Cards in Deck: %d", [_decks count]);
// Deal to player then dealer
for (int i = 0; i <= 1; i++) {
[player addObject:[_decks lastObject]];
[_decks removeLastObject];
NSLog(#"%#", player);
if(_delegate && [_delegate respondsToSelector:#selector(didDistributeCard:withValue:)]) {
KCCard *aCard = (KCCard *)[player objectAtIndex:player.count-1];
[_delegate didDistributeCard:aCard to:player withValue:[aCard value]];
}
[dealer addObject:[_decks lastObject]];
[_decks removeLastObject];
NSLog(#"%#", dealer);
if(_delegate && [_delegate respondsToSelector:#selector(didDistributeCard:withValue:)]) {
KCCard *aCard = (KCCard *)[dealer objectAtIndex:dealer.count-1];
[_delegate didDistributeCard:aCard to:dealer withValue:[aCard value]];
}
}
NSLog(#"Done Dealing");
NSLog(#"Cards Remaining in Deck: %d", [_decks count]);
NSLog(#"Player: %#\n\n", player);
NSLog(#"Dealer: %#\n\n", dealer);
}
Inside of my game controller, I set player to my player array as well as the dealer his. When this is run however, it doesn't work. No objects are added to the player array that player or dealer is assigned.
When the code is in the game controller, it works, but not in this class. I figure something is not initializing, but in the game controller, the player and dealer are both initialized.

If you think of it from a OO perspective, dealer and player are really objects that should receive a message such as addCardToHand:(Card )aCard. The way you are doing it, Lucas has exactly right, you get a copy of his array which is not mutable. I'd much rather see some dot notation such as player.hand addCard:(Card)aCard if you don't want the player to handle the card himself (watch for card sharps! :-) ).
I think if you refactor with objects you can have clean code that works, and probably get rid of that delegation stuff (which is a bit confusing just reading).
Good luck - blackjack is fun!
Damien

Related

Storing a string from a text field as a Player name Objective C

I am trying to have multiple players for a card matching game. I have made a class that is a child of NSObject called Player. A Player object will have a name, high score, last score, and boolean is currently playing instance. I am trying to create a new player every time a new name is entered into a text field. I will work on storing the players later. Right now I just wish to make a new Player, have a user enter their name in the text field. And store the user's response in the text field. The following is my Player.m file
#import "Player.h"
#implementation Player
-(NSString *) nameOfPlayer:(Player *)playerName{
return self.name;
}
-(void) setPlayerName:(NSString *) nameOf{
self.name = nameOf;
self.lastScore = 0;
self.highestScore = 0;
self.playing = YES;
return;
}
-(id)init{
self = [super init];
self.name = #"";
self.lastScore = 0;
self.highestScore = 0;
self.playing = YES;
return self;
}
#end
I didn't originally have that init until I tried looking for a solution online. I am a bit new to iOS coding so I wasn't sure how to set up a constructor (or if they even have those). Below is how I tried to instantiate a Player object:
- (IBAction)handleButtonClick:(UIButton *)sender {
Player * friend = nil;
nameText = self.nameEntryField.text;
[friend setPlayerName:nameText];
NSLog(#"%#", [friend nameOfPlayer:friend]);
}
My app breaks its thread as soon as I try to setPlayerName. I am a bit stuck on this so any help would be appreciated.
- (IBAction)handleButtonClick:(UIButton *)sender {
Player * friend = [[Player alloc] init]; // Initialize this
nameText = self.nameEntryField.text;
[friend setPlayerName:nameText];
NSLog(#"%#", [friend nameOfPlayer:friend]);
}
You have done like Player * friend = nil; mean this is the nil object. And then you are trying to use method setPlayerName:on nil object. Because of this app breaks.
So you need to initialize object using [[Player alloc] init].
This is embarassing. I was unfamiliar with the xCode IDE and accidentally had selected that line of code for a breakpoint. The code is fine after previous answer.

Basic IOS OOP - structure and implementation

I am creating a basic guessing game in iOS for my kids, and I think there are some fundamental gaps in my understanding of how I should be creating and releasing objects throughout the lifecycle of the app. I have been reading up on retain and release cycles but I think my issue is more to do with the fundamental architecture of the app and how I may be poorly trying to instantiate and then kill a few key objects of the app.
The problem centers around two specific classes.
I have a game class, which I have designed to hold all the information that the game requires to run. When it is init-ed, it holds all instance variables that point to arrays that hold strings such as the various clues, etc. It's basically a container for all the data that the game requires.
I have a game view controller, that creates and an instance of the game class and queries it so as to present on screen the various elements contained with the game object.
This works perfectly the fine. When the user starts a new game, a new instance of the game class is allocated and init-ed and away they go.
The issue comes in when I come to generate a new game. This happens a number of ways. Either The user finishes the game and starts another one or the user quits the current game and then starts a new one.
In my thinking, I would just release the game object and alloc and init a new one. However, I notice running on the device and looking through the profiler, that the game object isn't released at all.It's still, there and each instantiation of the game creates a new game object with the old one still sitting there with no pointers to it.
Fiddling around with the code, I noticed that I did not implement a dealloc method in the Game class...but when I try to do that, the app crashes, I suspect because I am trying to release a previously released object.
Ideally what I am trying to do is get rid of the old Game object, or replace the old one (overwrite) with a new one each time a new game is started.
However, is this approach wrong? Should I be doing it a completely different way? Such as only ever creating a single instance of the game class and rewriting a method inside that class so as to generate a new set of clues, etc everytime a new game starts and the GameViewController tells it to?
Is there a 'best practice' way to do this?
So you've got an idea of what I am doing, code is below for the GameViewController, where an instance of the Game class is created:
#import "GameViewController.h"
#implementation GameViewController
#synthesize game = _game;
-(void)startNewGameOfLevel:(NSInteger)level
{
if(!_game)
{
Game *g = [[Game alloc]initGamewithLevel:level];
[self setGame:g];
[g release]; g = nil;
}
[self set_currentlevel:[_game _currentLevel]];
// set up popover to show the rounds goal letter
[self setUpPopOver];
}
-(void)quitTheCurrentGameAndStartNewGame
{
[_game release]; _game = nil;
[self clearGamePlayingField];
animationStepIndex = 0;
[self startNewGameOfLevel: _currentlevel];
}
Game class (abridged) with the designated initializer of the Game class:
#import "Game.h"
#implementation Game
#synthesize arrayOfLowerCaseLetters = _arrayOfLowerCaseLetters;
#synthesize arrayOfPhrases= _arrayOfPhrases;
#synthesize goalLetter = _goalLetter;
#synthesize goalPhrase = _goalPhrase;
#synthesize gameLetterPool = _gameLetterPool;
#synthesize _indexForGoalLetter, _numberOfLevelsInGame, _currentLevel, _numberOfWhackHoles, _numberOfLettersInGameLetterPool;
-(id)initGamewithLevel:(NSInteger)level
{
[super init];
//create an array of lower case letters. These will
//contain the full alphabet of all possible letters
NSArray *arrayOfLCLetters = [[NSArray alloc] initWithObjects:#"a", #"b", #"c", #"d",#"e", #"f", #"g", #"h", #"i", #"j", #"k", #"l", #"m", #"n", #"o", #"p", #"qu", #"r", #"s", #"t", #"u", #"v", #"w", #"x",#"y", #"z",#"ch", #"sh", #"th", nil];
[self setArrayOfLowerCaseLetters: arrayOfLCLetters];
[arrayOfLCLetters release];arrayOfLCLetters = nil;
//create an array of phrases.
// These must correspond with each of the letters. e.g. a = apple.
NSArray *phrases= [[NSArray alloc ] initWithObjects:
#"apple",
#"butterfly",
#"cat",
#"dog",
#"egg",
#"frog",
#"ghost",
#"horse",
#"igloo",
#"jam",
#"kite",
#"leaf",
#"moon",
#"nut",
#"orange",
#"pig",
#"queen",
#"rabbit",
#"snake",
#"tree",
#"umbrella",
#"van",
#"water",
#"x-ray",
#"yak",
#"Zebra",
#"chair",
#"shoes",
#"thumb",
nil];
[self setArrayOfPhrases:phrases];
[phrases release]; phrases = nil;
//choose a random number to be the index reference for
// each goal letter and goal phrase.
[self set_indexForGoalLetter:(arc4random()%[_arrayOfLowerCaseLetters count])];
NSLog(#"index for goal letter is:, %i", _indexForGoalLetter);
//set Goal letter and goal phrase
[self setGoalLetter: [_arrayOfLowerCaseLetters objectAtIndex: _indexForGoalLetter]];
[self setGoalPhrase: [_arrayOfPhrases objectAtIndex:_indexForGoalLetter ]];
//set current level
[self set_currentLevel: level];
//[self set_currentLevel: 2];
//set number of whackholes by level
[self set_numberOfWhackHoles: [self numberOfWhackHolesByLevel:_currentLevel]];
//generate size of Letter pool by level
[self set_numberOfLettersInGameLetterPool:[self numberOfLettersInLetterPoolbyLevel:_currentLevel]];
////////////////////////////
/// Game letter pool
///////////////////////////
//set up array ton hold the pool of letters
NSMutableArray *gp = [[NSMutableArray alloc] initWithCapacity:_numberOfLettersInGameLetterPool];
[self setGameLetterPool: gp];
[gp release];gp = nil;
//add the goal letter to this pool
[_gameLetterPool addObject:_goalLetter];
int i = 1;
while (i < _numberOfLettersInGameLetterPool) {
NSString *letter = [_arrayOfLowerCaseLetters objectAtIndex:(arc4random()%[_arrayOfLowerCaseLetters count])];
if ([_gameLetterPool containsObject:letter] == false)
{
[_gameLetterPool addObject:letter];
i++;
}
}
NSLog(#"********** Game created ***************");
NSLog(#"pool of letters is: %#", [_gameLetterPool description]);
NSLog(#"****************************************");
NSLog(#"current goal letter is: %#", _goalLetter);
NSLog(#"****************************************");
NSLog(#"current goal phrase is: %#", _goalPhrase);
NSLog(#"****************************************");
return self;
}
-(void)dealloc
{
[super dealloc];
[_arrayOfLowerCaseLetters release]; _arrayOfLowerCaseLetters = nil;
[_arrayOfPhrases release]; _arrayOfPhrases = nil;
[_goalLetter release];_goalLetter = nil;
[_goalPhrase release]; _goalPhrase = nil;
[_gameLetterPool release];_gameLetterPool = nil;
}
The number one problem is that [super dealloc] must be the absolute last thing you do in -dealloc. This is because it is the dealloc method in NSObject that actually frees the memory, so by the time you get back to it, your instance variable pointers may already be garbage.
Other issues:
In init, do self = [super init]; The super object is allowed to return a different self pointer on init.
startNewGameOfLevel: and quitTheCurrentGameAndStartNewGame should use the property, not the bare instance variable.
-(void)startNewGameOfLevel:(NSInteger)level
{
if(![self game])
{
Game *g = [[Game alloc]initGamewithLevel:level];
[self setGame:g];
[g release]; g = nil;// g = nil, not necessary when it's about to go out of scope
}
[self set_currentlevel:[[self game] _currentLevel]]; // don't use _ to start methods - Apple reserves this convention
// set up popover to show the rounds goal letter
[self setUpPopOver];
}
-(void)quitTheCurrentGameAndStartNewGame
{
[self setGame: nil];
[self clearGamePlayingField];
animationStepIndex = 0;
[self startNewGameOfLevel: _currentlevel];
}
There are probably other issues in the body of your code - make sure you build with static analysis enables - it will catch many of them.

If Statement not Running

I am fairly new to objective c and cocoa, however i have spent a lot of time with c++, and I have never run across this issue before.
My application needs to identify a mounted disk by name. Here is the code:
//this code will run whenever a new disk is mounted to the computer
-(void) scanForKindle:(NSNotification *) notification
{
NSMutableArray *mountedDisks = [[NSMutableArray alloc] init];
mountedDisks = [workspace mountedRemovableMedia];
NSMutableArray *ebookDevices = [[NSMutableArray alloc] init];
NSDictionary *fileAttributes = [[NSDictionary alloc] init];
int currentDisk;
for (currentDisk = 0; currentDisk < [mountedDisks count]; currentDisk++)
{
NSLog(#"Name: %#", [mountedDisks objectAtIndex:currentDisk]);
if ([mountedDisks objectAtIndex:currentDisk] == #"/Volumes/Kindle")
{
NSLog(#"Kindle has been identified");
}
}
}
I have gotten everything to work perfectly up to the if statement in the for loop. It simply wont run. any ideas why? I am sure this is a simple fix, but I cannot figure this out for the life of me.
Any help would be greatly appreciated!
That's because you are making pointer comparison between two different instances of NSStrings.
Do this instead -
if ([[mountedDisks objectAtIndex:currentDisk] isEqualToString:#"/Volumes/Kindle"])
{
NSLog(#"Kindle has been identified");
}
Use NSString's -isEqualToString: method to compare strings. == just compares the addresses of the strings.

Objective-c Memory Question

I have a question regarding memory management in objective-c. I have read Apple's various documents regarding this but I still don't seem to be grasping it. I have created a small sample app to demonstrate my confusion. When I launch the app Activity Monitor states that it's using about 7MB. When I execute the main loop the memory usage goes up to 44MB which is expected. However, when I release the array I only get about 14MB back in Activity Monitor. The app continues to use about 30MB. Shouldn't I get all my memory back return the "Real Memory" to 7MB in Activity Monitor?
What am I doing wrong?
Thanks in advance.
Here is the AppDelegate:
- (IBAction) buildArray:(id)sender {
values = [[NSMutableArray alloc] init]; // values is an instance variable of the appDelegate
for(int i = 0; i < 500000; ++i) {
NSString *tempString = [NSString stringWithFormat:#"New Object %i", i];
[values addObject:tempString];
}
[valuesTable reloadData]; // valuesTable is a NSTableView to diplay the array.
}
- (IBAction) clearMemory:(id)sender {
[values release];
[valuesTable reloadData];
}
- (int) numberOfRowsInTableView: (NSTableView *) tableView {
if (values) {
return [values count];
} else {
return 0;
}
}
- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) tableColumn row: (int) row {
return [values objectAtIndex:row];
}
This has been answered a multitude of times before; but the act of releasing an object does not immediately return memory to the system. The application holds on to parts of the arena that lie unused, waiting for them to be reallocated.
If they are actually requested by the system, it gives up these resources, but as long as no other process wants the memory, your application will hang on to it, "just in case".

Memory Leak when using for(object in array) with iPhone SDK

I am running into some serious memory leaks in one of my applications I am building. I have a UINavigatonController that is inside a UITabBarview. Inside the NavView is a MKMap view. When you click an accessory button on a callout a detail view is loaded. In that detail view I am trying to populate a table from a plist using a for(object in array) loop. The plist is an array of dictionaries. I am running though the dictionaries to find one with a key that is the title of the callout and then get an array from inside that dictionary. It all works fine in the simulaor but I am getting massive memory leaks doing it the way I am. Any Idea whats going on?
- (void)viewDidLoad {
self.title = #"Route Details";
NSString *path = [[NSBundle mainBundle] pathForResource:#"stopLocation" ofType:#"plist"];
holderArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
[self getRouteArray];
routeDetails.delegate = self;
routeDetails.dataSource = self;
}
-(void)getRouteArray{
for (NSMutableDictionary *dictionary in holderArray) {
//NSString *stopName = [dictionary objectForKey:#"StopName"];
//NSString *stopName = [[NSString alloc] initWithString:[dictionary objectForKey:#"StopName"]];
BOOL testString = [currentRoute isEqualToString:[dictionary objectForKey:#"StopName"]];
if (testString) {
routeArray = [[NSMutableArray alloc] initWithArray:[dictionary objectForKey:#"RouteService"]];
}
}
}
- (void)dealloc {
[routeArray release];
[routeDetails release];
[super dealloc];
}
holderArray is an ivar and so is route array. As you can see I have tried a few ways of allocating the nstrings and arrays but all seem to yield the same leaks. According to the performance tool I am leaking from NSCFString, NSCFDictionary, and the NSCFArry. I released the routeArray in the dealloc and it works fine, but if I release holderArray it crashes whenever I go back to my map from the detail view. I guess I am just really unsure as to how to deal with the strings and dictionary used in the for loop.
Just to add the detail view is being created like so:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{
NSString *selectedRouteName = [NSString stringWithFormat:#"%#",view.annotation.title];
RouteDetailView *rdc = [[RouteDetailView alloc] initWithNibName:#"RouteDetailView" bundle:nil];
[rdc setCurrentRoute:selectedRouteName];
[self.navigationController pushViewController:rdc animated:YES];
[rdc release];
}
Sorry if any of the above is unclear. Let me know and I can try to rephrase it.
Will testString be true for at most one key in holderArray? If so, you should probably break out of the loop after setting routeArray. If not, then you may be setting routeArray multiple times, and all but the last array you assigned to it would be leaked.
Also, I don't see you releasing holderArray.