我正在为孩子们在iOS中创建一个基本的猜谜游戏,我认为在理解我应该如何在应用程序的整个生命周期中创建和释放对象方面存在一些根本性的差距。我一直在阅读保留和发布周期,但我认为我的问题更多的是与应用程序的基本架构有关,以及我如何努力实例化然后杀死应用程序的一些关键对象。
问题集中在两个特定的类。
我有一个游戏类,我设计用来保存游戏运行所需的所有信息。当它被初始化时,它包含指向包含各种线索等字符串的数组的所有实例变量。它基本上是游戏所需的所有数据的容器。
我有一个游戏视图控制器,它创建游戏类的一个实例并查询它,以便在屏幕上显示游戏对象包含的各种元素。
这完美无缺。当用户启动一个新游戏时,会分配一个新的游戏类实例并启动它们。
当我开始制作新游戏时会出现问题。这种情况有很多种。用户完成游戏并启动另一个游戏或用户退出当前游戏然后开始新游戏。
在我的想法中,我只是释放游戏对象并分配并初始化一个新对象。但是,我注意到在设备上运行并查看分析器,游戏对象根本没有被释放。它仍然存在,游戏的每个实例都创建了一个新的游戏对象,旧游戏对象仍然坐在那里没有指针它。
摆弄代码,我注意到我没有在Game类中实现dealloc方法......但是当我尝试这样做时,应用程序崩溃,我怀疑是因为我试图释放以前发布的对象。
理想情况下,我想要做的是摆脱旧的Game对象,或者每次启动新游戏时用新的替换旧的(覆盖)。
但是,这种做法是错误的吗?我应该采用完全不同的方式吗?比如只创建游戏类的单个实例并重写该类中的方法,以便每当新游戏开始并且GameViewController告诉它时生成一组新的线索等等?
是否有“最佳实践”方法?
所以你已经知道我在做什么,下面是GameViewController的代码,其中创建了一个Game类的实例:
#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类的指定初始化程序:
#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;
}
第一个问题是[super dealloc]
必须是你在-dealloc
做的最后一件事。这是因为它是NSObject中的dealloc
方法实际上释放了内存,所以当你回到它时,你的实例变量指针可能已经是垃圾了。
其他问题:
在init中,执行self = [super init];
允许超级对象在init上返回不同的自指针。
startNewGameOfLevel:
和quitTheCurrentGameAndStartNewGame
应该使用属性,而不是裸实例变量。
-(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];
}
代码正文中可能存在其他问题 - 确保使用静态分析构建 - 它将捕获许多代码。