iphone, использующий cocos2d, получает EXC_BAD_ACCESS - PullRequest
1 голос
/ 18 августа 2010

Я делаю простой игровой проект с использованием 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];

Ответы [ 4 ]

2 голосов
/ 23 августа 2010

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

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

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

Но в методе dealloc вы освобождаете его, даже если он является объектом автоматического выпуска:

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

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

Тогда вы усложняете ситуацию:

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

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

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

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

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

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

1 голос
/ 18 августа 2010

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

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

0 голосов
/ 26 сентября 2010

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

0 голосов
/ 23 августа 2010

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

...