【问题标题】: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

【问题讨论】:

    标签: ios cocos2d-iphone


    【解决方案1】:

    我敢打赌,您的新游戏层是在第一个释放之前创建的,因此它将静态 var 设置为 nil。检查 sharedGameLayer 在 dealloc 中是否等于 self。

    【讨论】:

    • 感谢您的回复。我在前面的问题中说过,在 GameLayer 场景的第一次重新启动/dealloc 之后,如果 -deallocsharedGameLayer = 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 从来没有机会被设置为自我思考。
    猜你喜欢
    • 1970-01-01
    • 2017-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-25
    • 1970-01-01
    • 2011-06-04
    相关资源
    最近更新 更多