views:

356

answers:

4

I am making a simple game project involving the use of Cocos2d. Now as defined by Ray Wenderlich's example, i have completed the whole tutorial but added an extra bit of code myself to check total number of melons, when they reach 3, i replace screen with "You Win" screen to notify the user that he has won using [[CCDirector sharedDirector] replaceScene:gameoverscreen];.

The problem is that i get EXC_BAD_ACCESS everytime i call this from ccTouchEnded coz my condition is checked here. But the same thing works if i use [[CCDirector sharedDirector] pushScene:gameoverscreen];

Cant understand what the problem is!!

the code for gameoverscreen screen is:

#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

and the Header file of GameoverScene contains the following!

#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

i call the scene from HelloWorld class using the following syntax!

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

EXEC_BAD_ACCESS means you are using data that has been released. Does the youwin scene use data from the current scene? If so, it needs to retain the data. When replaceScene: is called, the current scene is not held in memory but when pushScene: is called, both scenes remain in memory.

EDIT: Let's say you have two scenes, A and B. When you call pushScene:, A continues to exist in memory and B is added. When you call replaceScene:, A is removed and no longer exists, only the B scene. That is why A's data would disappear, but only when replacing.

Colin Gislason
nope, the youwin scene doesnt use any of the current scenes data... Its just a plane scene consisting of a layer which in turn consists of a CClabel... And suppose it did use any data, y would it give in to pushScene but not ReplaceScene???Secondly, you talked about retaining the data, how should i figure out which particular data needs to be retained??Thanx for your prompt reply!!
JaVadid
I added more to my explanation of how replace and push work. Can you post the code used to create the `youwin` scene and push it?
Colin Gislason
@Colin GislasonHave added the code! thanx 4 pondering over it... Thanx a lot! Do comment on the code if i have made any mistake!
JaVadid
For the `@property` declaration, are both `layer` and `label` set to retain?Also, you probably want to set a breakpoint at the beginning of that code block and try to find the line that causes the error.
Colin Gislason
as u must have observed in the code snippet i posted in the question itself(actually edited later!), both label and layer are retain! Secondly yes i had put a breakpoint and from there i realized that the replaceScene part of the code was creating problem! Thanx for sharing ur ideas dude! It helped me in resolving the problem!
JaVadid
A: 

The general rule when it comes to memory dealing is to release whatever you alloced or retained. In your case you are instantiating a CCLabel object with a convenience method (thus, not calling alloc) and you are not retaining it. So, that [label release] in your dealloc method must not be there in this case.

pabloruiz55
ohh...I guess i follow you to some extent... can u please define a way i can retain it?? and by the way i have released the label in the GameOverScreen itself, then also do i have to make n release it in the HelloWorld class too???
JaVadid
+1  A: 

I see several issues in your code.

One is the CCLabel object, you initialize it as autorelease object using cocos2d's static initializer:

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

But in the dealloc method you release it even though its an autorelease object:

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

You should not release the label since it is set to autorelease by cocos2d! This is a guaranteed crash!

Then you make things more complicated than needed:

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

The alloc/init/autorelease is completely superfluous because you can simply write [HelloWorld scene] if the HelloWorld class has a +(id) scene method (it normally should). If not, then use [HelloWorld node]. Always prefer cocos2d's static autorelease initializers before using alloc/release on cocos2d objects. The only time you ever need to alloc a cocos2d class is when you explicitly don't add it as a child to some other node, which is rare.

Finally, this is very bad style:

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

If the super implementation of initWithColor calls [self init] - which is often the case and even if not, could change with future releases of cocos2d - it would call your implementation of init, resulting in an endless loop (stack overflow). To fix this simply either rename your init method or call [super init] and provide the parameters some other way, usually there will be a property or setter method to do so.

And a minor issue: Apple advises against using leading underscores as member variable prefix. In fact, many other compiler vendors advice against that too since often system internal variables use one or two underscores as prefix. The cocos2d style with trailing underscores is preferred which would have you write label_ instead of _label.

GamingHorror
Wow! that was truly an eye-opener! Thanx a lot! But 1 thing i wanted to confirm was, as you might have seen i have added my header file to the question, I have declared the label variable in the header file, hence correct me if i'm wrong but i guess i'll have to release it! wont i??
JaVadid
A: 

I also account with such thing,the reason for it may be you release something that are autorelease,so you can try it again by not to release some object in the dealloc method!

McGrady