iphone с помощью cocos2d получает EXC_BAD_ACCESS

Я делаю простой игровой проект с использованием Cocos2d. Теперь, как определено пример Рэя Вендерлиха, я завершил весь урок, но сам добавил дополнительный бит кода, чтобы проверить общее количество дынь, когда их становится 3, я заменяю экран на экран «Вы выиграли», чтобы уведомить пользователя о том, что он выиграл, используя [[CCDirector sharedDirector] replaceScene:gameoverscreen];.

Проблема в том, что я получаю EXC_BAD_ACCESS каждый раз, когда я вызываю это из ccTouchEnded, потому что здесь проверяется мое условие. Но то же самое работает, если я использую [[CCDirector sharedDirector] pushScene:gameoverscreen];

Не могу понять в чем проблема!!

код для экрана gameoverscreen:

#import "GameOverScene.h"
#import "HelloWorldScene.h"

@implementation GameOverScene
@synthesize _layer = layer;

- (id)init {

    if ((self = [super init])) {
        self._layer = [GameOverLayer node];
        [self addChild:layer];
    }
    return self;
}

- (void)dealloc {
    [layer release];
    layer = nil;
    [super dealloc];
}

@end

@implementation GameOverLayer
@synthesize _label = label;

-(id) init
{
    if( (self=[super initWithColor:ccc4(255,255,255,255)] )) {

        CGSize winSize = [[CCDirector sharedDirector] winSize];
        self._label = [CCLabel labelWithString:@"" fontName:@"Arial" fontSize:32];
        label.color = ccc3(0,0,0);
        label.position = ccp(winSize.width/2, winSize.height/2);
        [self addChild:label];

        [self runAction:[CCSequence actions:
                         [CCDelayTime actionWithDuration:3],
                         [CCCallFunc actionWithTarget:self selector:@selector(gameOverDone)],
                         nil]];

    }   
    return self;
}

- (void)gameOverDone {

    [[CCDirector sharedDirector] replaceScene:[[[HelloWorld alloc] init] autorelease]];

}

- (void)dealloc {
    [label release];
    label = nil;
    [super dealloc];
}

@end

а файл заголовка GameoverScene содержит следующее!

#import "cocos2d.h"

@interface GameOverLayer : CCColorLayer {
    CCLabel *label;
}

@property (nonatomic, retain) CCLabel *_label;

@end

@interface GameOverScene : CCScene {
    GameOverLayer *layer;
}

@property (nonatomic, retain) GameOverLayer *_layer;

@end

я вызываю сцену из класса HelloWorld, используя следующий синтаксис!

GameOverScene *gameoverscene = [GameOverScene node];
[gameoverscene._layer._label setString:@"You WON!"];
[[CCDirector sharedDirector] pushScene:gameoverscene];

person JaVadid    schedule 18.08.2010    source источник


Ответы (4)


Я вижу несколько проблем в вашем коде.

Одним из них является объект CCLabel, вы инициализируете его как объект autorelease с помощью статического инициализатора cocos2d:

 self._label = [CCLabel labelWithString:@"" fontName:@"Arial" fontSize:32];

Но в методе Dealloc вы освобождаете его, даже если это объект autorelease:

- (void)dealloc {
    [label release];
    label = nil;
    [super dealloc];
}

Вы не должны освобождать метку, так как cocos2d устанавливает для нее автоматическое освобождение! Это гарантированный сбой!

Затем вы делаете вещи более сложными, чем необходимо:

 [[CCDirector sharedDirector] replaceScene:[[[HelloWorld alloc] init] autorelease]];

Alloc/init/autorelease совершенно излишен, потому что вы можете просто написать [сцена HelloWorld], если класс HelloWorld имеет метод сцены +(id) (обычно он должен). Если нет, то используйте [узел HelloWorld]. Всегда отдавайте предпочтение статическим инициализаторам autorelease cocos2d перед использованием alloc/release для объектов cocos2d. Единственный раз, когда вам нужно выделить класс cocos2d, это когда вы явно не добавляете его в качестве дочернего элемента к какому-либо другому узлу, что бывает редко.

Наконец, это очень плохой стиль:

-(id) init
{
    if( (self=[super initWithColor:ccc4(255,255,255,255)] )) {

Если супер-реализация initWithColor вызывает [self init] — что часто бывает, и даже если нет, это может измениться в будущих выпусках cocos2d — она вызовет вашу реализацию init, что приведет к бесконечному циклу (переполнение стека). Чтобы исправить это, просто либо переименуйте свой метод инициализации, либо вызовите [super init] и укажите параметры другим способом, обычно для этого будет метод свойства или установки.

И небольшая проблема: Apple не рекомендует использовать ведущие символы подчеркивания в качестве префикса переменной-члена. На самом деле, многие другие поставщики компиляторов также советуют против этого, поскольку часто внутренние системные переменные используют один или два символа подчеркивания в качестве префикса. Предпочтителен стиль cocos2d с завершающими символами подчеркивания, в котором вы должны писать label_ вместо _label.

person LearnCocos2D    schedule 23.08.2010
comment
Вау! это было действительно откровением! Большое спасибо! Но 1 вещь, которую я хотел подтвердить, заключалась в том, что, как вы могли видеть, я добавил свой заголовочный файл к вопросу, я объявил переменную метки в заголовочном файле, поэтому поправьте меня, если я ошибаюсь, но я думаю, у меня будет чтобы освободить его! я не буду?? - person JaVadid; 25.08.2010

EXEC_BAD_ACCESS означает, что вы используете опубликованные данные. Использует ли сцена youwin данные из текущей сцены? Если это так, ему необходимо retain данные. При вызове replaceScene: текущая сцена не сохраняется в памяти, но при вызове pushScene: обе сцены остаются в памяти.

РЕДАКТИРОВАТЬ: Допустим, у вас есть две сцены, A и B. Когда вы вызываете pushScene:, A продолжает существовать в памяти, а B добавляется. Когда вы вызываете replaceScene:, A удаляется и больше не существует, только сцена B. Именно поэтому данные A пропадали, но только при замене.

person Colin Gislason    schedule 18.08.2010
comment
нет, сцена youwin не использует какие-либо данные текущей сцены... Это просто плоская сцена, состоящая из слоя, который, в свою очередь, состоит из CClabel... И предположим, что она действительно использует какие-либо данные, она уступит pushScene а не ReplaceScene??? Во-вторых, вы говорили о сохранении данных, как мне выяснить, какие именно данные необходимо сохранить? Спасибо за быстрый ответ!! - person JaVadid; 19.08.2010
comment
Я добавил больше к своему объяснению того, как работают замена и отправка. Можете ли вы опубликовать код, используемый для создания сцены youwin, и отправить ее? - person Colin Gislason; 19.08.2010
comment
Добавил код! спасибо, 4 размышлял над этим... Большое спасибо! Прокомментируйте код, если я сделал какую-либо ошибку! - person JaVadid; 23.08.2010
comment
Для объявления @property установлены ли layer и label для сохранения? Кроме того, вы, вероятно, захотите установить точку останова в начале этого блока кода и попытаться найти строку, которая вызывает ошибку. - person Colin Gislason; 24.08.2010
comment
как вы, должно быть, заметили во фрагменте кода, который я разместил в самом вопросе (фактически отредактированный позже!), И метка, и слой сохраняются! Во-вторых, да, я поставил точку останова, и оттуда я понял, что часть кода replaceScene создает проблему! Спасибо, что поделился своими идеями, чувак! Мне помогло в решении проблемы! - person JaVadid; 26.08.2010

Общее правило, когда дело доходит до работы с памятью, — высвобождать все, что вы выделили или сохранили. В вашем случае вы создаете экземпляр объекта CCLabel с помощью удобного метода (таким образом, не вызывая alloc) и не сохраняете его. Таким образом, в этом случае [label release] в вашем методе Dealloc не должно быть.

person pabloruiz55    schedule 23.08.2010
comment
ооо... Думаю, я в какой-то степени понимаю вас... не могли бы вы определить, как я могу сохранить это?? и, кстати, я выпустил метку в самом GameOverScreen, тогда мне также нужно сделать так, чтобы она была выпущена и в классе HelloWorld??? - person JaVadid; 23.08.2010

Я также учусь с такой вещью, причиной этого может быть то, что вы выпускаете что-то, что является автоматическим выпуском, поэтому вы можете попробовать еще раз, не выпуская какой-либо объект в методе Dealloc!

person McGrady    schedule 26.09.2010