正确地重新启动/重新初始化半单例 [英] Properly restarting/re-initializing a semi-singleton

查看:91
本文介绍了正确地重新启动/重新初始化半单例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用半单一方法为我的cocos2d游戏的主要游戏层/场景,如后面的代码所示。



是通过调用 [[GameLayer sharedGameLayer] restart] 方法,使用暂停或游戏层中的按钮正确重新启动/重新创建此单身场景。



问题是如果我使用CCTransition效果,并覆盖GameLayer的 dealloc 方法与 sharedGameLayer = nil; restart 方法不起作用。



可疑内容不是覆盖 dealloc 方法,而是在使用 replaceScene重新启动场景之前: / em>,我将sharedGameLayer设置为nil。



问题:这是重新启动/重新创建这个半singleton类的正确方法吗?



提前感谢。



代码: b

GameLayer.m:

  static GameLayer * sharedGameLayer; 

@implementation GameLayer

- (id)init
{
NSLog(@%s,__PRETTY_FUNCTION__);

//总是调用superinit
// Apple建议重新分配self与super的返回值
if(self = [super initWithColor: ccc4(255,255,255,255)width:[CCDirector sharedDirector] .winSize.width
height:[CCDirector sharedDirector] .winSize.height])
{

/ /将sharedGameLayer实例设置为self。
sharedGameLayer = self;

//设置初始游戏状态。
self.gameState = kGameStateRunning;

//注册通知中心,以便在游戏退出活动状态时暂停游戏。
NSNotificationCenter * center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(pauseGame)name:UIApplicationWillResignActiveNotification object:nil];

//启用触摸和多点触摸。
CCDirector * director = [CCDirector sharedDirector];
[director.touchDispatcher addTargetedDelegate:self priority:0 swallowsTouches:YES];
director.view.multipleTouchEnabled = YES;


//设置初始分数。
self.score = 5;

//创建spiders批处理节点。
[self createSpidersBatchNode];

//加载游戏资产。
[self loadAssets];


//播放背景音乐。
if(![SimpleAudioEngine sharedEngine] .isBackgroundMusicPlaying)
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@backgroundmusic.mp3loop:YES];

//预加载声音效果。
[[SimpleAudioEngine sharedEngine] preloadEffect:@inbucket.mp3];
[[SimpleAudioEngine sharedEngine] preloadEffect:@outbucket.mp3];
[[SimpleAudioEngine sharedEngine] preloadEffect:@gameover.mp3];

//安排更新。
[self scheduleUpdate];
[self schedule:@selector(releaseSpiders)interval:0.7];
}

return self;
}



- (void)createSpidersBatchNode
{
NSLog(@%s,__PRETTY_FUNCTION__);

// BatchNode。 (用于动画优化)
self.spidersBatchNode = [CCSpriteBatchNode batchNodeWithFile:@spiderAtlas.png];

//蜘蛛精灵+ BatchNode +动画动作。
for(int i = 0; i <50; i ++){
Spider * spider = [[Spider alloc] init];
spider.spiderSprite.visible = NO;
}

[self addChild:self.spidersBatchNode];
}



- (void)restartGame
{
//播放按钮按下的声音效果。
[[SimpleAudioEngine sharedEngine] playEffect:@button.mp3];

//取消静态变量。
sharedGameLayer = nil;

//重新启动游戏。
[[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:1.0 scene:[GameLayer scene]]];
}



+(CCScene *)场景
{
//'scene'是一个自动释放对象。
CCScene * scene = [CCScene node];

//游戏层。
//'gameLayer'是一个自动释放对象。
GameLayer * gameLayer = [GameLayer node];

//将gameLayer添加为场景
[scene addChild:gameLayer];

// HUD图层。
HUDLayer * hudLayer = [HUDLayer node];
[scene addChild:hudLayer];
gameLayer.hud = hudLayer;

//返回场景
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。



对于所有你知道,30分钟可能已经在你设置 sharedGameLayer nil 并且当 dealloc 方法被调用时。或者 dealloc 将永远不会被呼叫



您的代码看起来不错,



此外,此代码:

  [[NSNotificationCenter defaultCenter] removeObserver:self]; 

应该在 -dealloc 方法除了您的自定义 -cleanup 方法。



另一个注意事项, + sharedGameLayer 应返回类型 instancetype 的变量,它应负责设置 sharedGameLayer 变量。如果 sharedGameLayer -init 内,您将遇到错误。



例如:

  +(instancetype)sharedGameLayer 
{
if(sharedGameLayer)
return sharedGameLayer;

sharedGameLayer = [[[self class] alloc] init];

return sharedGameLayer;
}

- (id)init
{
if(!(self = [super init]))
return nil;





return self;
}


I'm using the semi-singleton approach for the main game layer/scene of my cocos2d game as shown in the later code.

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.

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.

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?

Thanks in advance.

Code:

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

解决方案

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.

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.

Your code looks good, just delete the commented out "dealloc" code.

Also, this code:

[[NSNotificationCenter defaultCenter] removeObserver:self];

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.

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.

For example:

+ (instancetype)sharedGameLayer
{
    if (sharedGameLayer)
        return sharedGameLayer;

    sharedGameLayer = [[[self class] alloc] init];

    return sharedGameLayer;
}

- (id)init
{
    if (!(self = [super init]))
        return nil;

    .
    .
    .

    return self;
}

这篇关于正确地重新启动/重新初始化半单例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆