正确重启/重新初始化半单例
Posted
技术标签:
【中文标题】正确重启/重新初始化半单例【英文标题】:Properly restarting/re-initializing a semi-singleton 【发布时间】:2013-06-30 23:36:54 【问题描述】:我在 cocos2d 游戏的主游戏层/场景中使用半单例方法,如后面的代码所示。
目标是通过调用 [[GameLayer sharedGameLayer] restart] 方法使用暂停或游戏结束层中的按钮正确重新启动/重新创建此单例场景。
问题是如果我为此使用 CCTransition 效果,并使用 sharedGameLayer = nil; 行覆盖 GameLayer 的 dealloc 方法(到确保静态变量的重置),sharedGameLayer 变量在第一次重启后(也就是第一次 dealloc 后)保持为零,所以调用 restart 方法什么都不做。
怀疑的方法根本不覆盖 dealloc 方法,而是在使用 replaceScene: 重新启动场景之前,我设置sharedGameLayer 为零。
问题:这是重新启动/重新创建这个半单例类的正确方法吗?
提前致谢。
代码:
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
【问题讨论】:
【参考方案1】:我敢打赌,您的新游戏层是在第一个释放之前创建的,因此它将静态 var 设置为 nil。检查 sharedGameLayer 在 dealloc 中是否等于 self。
【讨论】:
感谢您的回复。我在前面的问题中说过,在 GameLayer 场景的第一次重新启动/dealloc 之后,如果-dealloc
被 sharedGameLayer = nil;
行覆盖,则 sharedGameLayer 变量始终为零。还是您的意思是我应该在-dealloc
方法中将变量设置为self?这听起来很奇怪,因为您在书中的 -init
方法中将其设置为 self。【参考方案2】:
不要使用 dealloc 将静态变量设置为 nil。 Dealloc 发生在您的对象停止使用之后。切勿使用它来控制应用的行为。
就您所知,从您将sharedGameLayer
设置为nil
到调用dealloc
方法之间可能已经过了30 分钟。或者dealloc
可能根本不会被调用。
您的代码看起来不错,只需删除注释掉的“dealloc”代码即可。
另外,这段代码:
[[NSNotificationCenter defaultCenter] removeObserver:self];
除了您的自定义-cleanup
方法外,还应在-dealloc
方法中完成。移除观察者两次是完全没问题的,如果它已经被移除,则不会发生任何事情。
另外注意,+sharedGameLayer
应该返回一个instancetype
类型的变量,它应该负责设置sharedGameLayer
变量。如果你在-init
中使用sharedGameLayer
,就会遇到错误。
例如:
+ (instancetype)sharedGameLayer
if (sharedGameLayer)
return sharedGameLayer;
sharedGameLayer = [[[self class] alloc] init];
return sharedGameLayer;
- (id)init
if (!(self = [super init]))
return nil;
.
.
.
return self;
【讨论】:
感谢您的回复。我在问题中编辑了我发布的代码以包含完整的 -init 方法,并且您可以看到有一个 -createSpidersBatchNode 方法实际上调用了一个蜘蛛类来创建batchNode 中的蜘蛛实际上包含在 GameLayer 的类本身中,因此发生了一个循环 (-init -> -createSpidersBatchNode -> Spider 的类 -> [[GameLayer sharedGameLayer].spidersBatchNode addchild:] -> sharedGameLayer -> -init- ..etc)。那是因为 sharedGameLayer 从来没有机会被设置为自我思考。以上是关于正确重启/重新初始化半单例的主要内容,如果未能解决你的问题,请参考以下文章