问题描述
我在 cocos2d 游戏的主游戏层/场景中使用半单例方法,如后面的代码所示.
I'm using the semi-singleton approach for the main game layer/scene of my cocos2d game as shown in the later code.
目标是通过调用 [[GameLayer sharedGameLayer] restart] 方法使用暂停或游戏结束层中的按钮正确重新启动/重新创建此单例场景.
The objective is to properly restart/recreate this singleton scene using a button in a pause or gameover layers by calling: [[GameLayer sharedGameLayer] restart] method.
问题是如果我为此使用 CCTransition 效果,并使用 sharedGameLayer = nil; 行覆盖 GameLayer 的 dealloc 方法(到确保静态变量的重置),sharedGameLayer 变量在第一次重启后(也就是第一次 dealloc 后)保持为零,所以调用 restart 方法什么都不做.
The problem is if I use a CCTransition effect for that, and override the GameLayer's dealloc method with sharedGameLayer = nil; line (to ensure the resetting of the static variable), the sharedGameLayer variable stays nil after the first restart (aka. after the first dealloc), so calling the restart method does nothing.
怀疑的方法根本不覆盖 dealloc 方法,而是在使用 replaceScene: 重新启动场景之前,我设置sharedGameLayer 为零.
What works with suspicion is not to override the dealloc method at all, but before restarting the scene using replaceScene:, I set the sharedGameLayer to nil.
问题:这是重新启动/重新创建这个半单例类的正确方法吗?
Question: Is this the right approach for restarting/recreating this semi-singleton class?
提前致谢.
代码:
GameLayer.m:
GameLayer.m:
static GameLayer *sharedGameLayer;
@implementation GameLayer
- (id)init
{
NSLog(@"%s", __PRETTY_FUNCTION__);
// always call "super" init
// Apple recommends to re-assign "self" with the "super's" return value
if (self = [super initWithColor:ccc4(255, 255, 255, 255) width:[CCDirector sharedDirector].winSize.width
height:[CCDirector sharedDirector].winSize.height])
{
// Set the sharedGameLayer instance to self.
sharedGameLayer = self;
// Set the initial game state.
self.gameState = kGameStateRunning;
// Register with the notification center in order to pause the game when game resigns the active state.
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(pauseGame) name:UIApplicationWillResignActiveNotification object:nil];
// Enable touches and multi-touch.
CCDirector *director = [CCDirector sharedDirector];
[director.touchDispatcher addTargetedDelegate:self priority:0 swallowsTouches:YES];
director.view.multipleTouchEnabled = YES;
// Set the initial score.
self.score = 5;
// Create the spiders batch node.
[self createSpidersBatchNode];
// Load the game assets.
[self loadAssets];
// Play Background music.
if (![SimpleAudioEngine sharedEngine].isBackgroundMusicPlaying)
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"backgroundmusic.mp3" loop:YES];
// Preload sound effects.
[[SimpleAudioEngine sharedEngine] preloadEffect:@"inbucket.mp3"];
[[SimpleAudioEngine sharedEngine] preloadEffect:@"outbucket.mp3"];
[[SimpleAudioEngine sharedEngine] preloadEffect:@"gameover.mp3"];
// Schdule updates.
[self scheduleUpdate];
[self schedule:@selector(releaseSpiders) interval:0.7];
}
return self;
}
- (void)createSpidersBatchNode
{
NSLog(@"%s", __PRETTY_FUNCTION__);
// BatchNode. (For Animation Optimization)
self.spidersBatchNode = [CCSpriteBatchNode batchNodeWithFile:@"spiderAtlas.png"];
// Spider sprite + BatchNode + animation action.
for (int i = 0; i < 50; i++) {
Spider *spider = [[Spider alloc] init];
spider.spiderSprite.visible = NO;
}
[self addChild:self.spidersBatchNode];
}
- (void)restartGame
{
// Play button pressed sound effect.
[[SimpleAudioEngine sharedEngine] playEffect:@"button.mp3"];
// Nil'ing the static variable.
sharedGameLayer = nil;
// Restart game.
[[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:1.0 scene:[GameLayer scene]]];
}
+ (CCScene *)scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// Game Layer.
// 'gameLayer' is an autorelease object.
GameLayer *gameLayer = [GameLayer node];
// add gameLayer as a child to scene
[scene addChild: gameLayer];
// HUD Layer.
HUDLayer *hudLayer = [HUDLayer node];
[scene addChild:hudLayer];
gameLayer.hud = hudLayer;
// return the scene
return scene;
}
+ (GameLayer *)sharedGameLayer
{
return sharedGameLayer;
}
- (void)cleanup
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[CCDirector sharedDirector].touchDispatcher removeDelegate:self];
[self stopAllActions];
[self unscheduleAllSelectors];
[self unscheduleUpdate];
[self removeAllChildrenWithCleanup:YES];
self.hud = nil;
[super cleanup];
}
//- (void)dealloc
//{
// sharedGameLayer = nil;
//}
@end
推荐答案
不要使用dealloc将静态变量设置为nil.Dealloc 发生在您的对象停止使用之后.切勿使用它来控制应用的行为.
Do not use dealloc to set the static variable to nil. Dealloc happens after your object has stopped being used. Never use it for controlling the behaviour of your app.
就您所知,从您将 sharedGameLayer
设置为 nil
到调用 dealloc
方法之间可能已经过了 30 分钟.或者也许 dealloc
将根本不会被调用.
For all you know, 30 minutes might have passed between when you set sharedGameLayer
to nil
and when the dealloc
method gets called. Or perhaps dealloc
will never be called at all.
您的代码看起来不错,只需删除注释掉的dealloc"代码即可.
Your code looks good, just delete the commented out "dealloc" code.
另外,这段代码:
[[NSNotificationCenter defaultCenter] removeObserver:self];
除了您的自定义 -cleanup
方法外,还应在 -dealloc
方法中完成.移除观察者两次是完全没问题的,如果它已经被移除,则不会发生任何事情.
Should be done in the -dealloc
method in addition to your custom -cleanup
method. It is perfectly fine to remove an observer twice, if it's already been removed nothing will happen.
另外注意,+sharedGameLayer
应该返回一个 instancetype
类型的变量,它应该负责设置 sharedGameLayer
变量.如果你在-init
中sharedGameLayer
,你会遇到bug.
Another note, +sharedGameLayer
should return a variable of type instancetype
and it should be responsible for setting the sharedGameLayer
variable. If you sharedGameLayer
inside -init
, you will run into bugs.
例如:
+ (instancetype)sharedGameLayer
{
if (sharedGameLayer)
return sharedGameLayer;
sharedGameLayer = [[[self class] alloc] init];
return sharedGameLayer;
}
- (id)init
{
if (!(self = [super init]))
return nil;
.
.
.
return self;
}
这篇关于正确重启/重新初始化半单例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!