0
votes

Please bear with me I am about to post a long code. I am subclassing CCSprite:

@interface Character : CCSprite {
    }
    -(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect;
    @end

    @implementation Character
    -(id) initWithTexture:(CCTexture2D*)texture rect:(CGRect)rect
    {
        NSLog(@"in initWithTexture");
        if( (self=[super initWithTexture:texture rect:rect]))
        {
        }
        return self;
    }
    -(void) dealloc
    {
        NSLog(@"character dealloced");
        [super dealloc];
    }
    @end

Question #1: is it correct?

I have a layer containing this sprite as class variable and also another CCSprite.

Character *characterSprite;
CCSprite *burpSprite;

This layer also has two CCAnimation objects:

CCAnimation *burpAnim;
CCAnimation *burpParticlesAnim;

In this class I have two methods: -(void) loadBurpAnimation; -(void) unloadBurpAnimation;

-(void) loadBurpAnimation {
        NSString* burpFile;
        NSString* burpFilePVR;

        NSString* burpParticlesFile;
        NSString* burpParticlesFilePVR;

        burpFile = @"latestBurp1-1.plist";
        burpFilePVR = @"latestBurp1-1.pvr.ccz";
        burpParticlesFile = @"burpParticles1-1.plist";
        burpParticlesFilePVR = @"burpParticles1-1.png";

        [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:burpFile texture:[[CCTextureCache sharedTextureCache] addImage:burpFilePVR]];
        [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:burpParticlesFile texture:[[CCTextureCache sharedTextureCache] addImage:burpParticlesFilePVR]];

        NSMutableArray *burpFrames = [NSMutableArray array];
        for(int i = 231; i <= 268; ++i) {
            [burpFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:@"burp.%d.png", i]]];
        }
        burpAnim = [[CCAnimation alloc] initWithFrames:burpFrames delay:0.04f];
        [burpFrames removeAllObjects];

        //Burp Particles
        [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:burpParticlesFile];
        NSMutableArray *burpParticlesFrames = [NSMutableArray array];
        for(int i = 3; i <= 37; ++i) {
            [burpParticlesFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:@"Burp_%05d.png", i]]];
        }

        burpParticlesAnim = [[CCAnimation alloc] initWithFrames:burpParticlesFrames delay:0.04f];
        [burpParticlesFrames removeAllObjects];
}

-(void) unloadBurpAnimation {

    [burpAnim release];
    [burpParticlesAnim release];
}

I have to load the burp animation in thread and call a method on main thread to play it, which goes like this:

-(void) loadAnimation {
    NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];

    [dictLock lock];

    if( [EAGLContext setCurrentContext:auxEAGLcontext] ) {
        [self loadBurpAnimation];
                [self performSelectorOnMainThread:@selector(burpAnimation) withObject:nil waitUntilDone:NO];
        } else {
        CCLOG(@"cocos2d: TetureCache: EAGLContext error");
    }
    [dictLock unlock];
    [autoreleasepool drain];
}

I know there is a method to load animation separately "asynchAddImage", but this is not the concern here.

-(void) burpAnimation {

    CCAction *burpAction = [CCSequence actions:
                            [CCAnimate actionWithAnimation:burpAnim restoreOriginalFrame:NO],
                            [CCCallFunc actionWithTarget:self selector:@selector(animationDone)],nil];
    CGSize winSize = [CCDirector sharedDirector].winSize;
    characterSprite = [[Character alloc] initWithSpriteFrameName:@"burp.231.png"];
    characterSprite.position = ccp(winSize.width/2, winSize.height/2);
    [characterSprite setScale:1.5];

    CCAction *particlesAction = [CCSequence actions:
                            [CCAnimate actionWithAnimation:burpParticlesAnim restoreOriginalFrame:NO],
                            nil];
    burpSprite = [[CCSprite alloc] initWithSpriteFrameName:@"Burp_00003.png"];
    burpSprite.position = ccp(winSize.width/2-50, winSize.height/2-80);

    [self addChild:characterSprite];
    [characterSprite release];

    [self addChild:burpSprite];
    [burpSprite release];

    [characterSprite runAction:burpAction];
    [burpSprite runAction:particlesAction];
}

The real problem I am facing is when the CCCallFunc selector is called after the animation:

- (void)animationDone {
    //Here characterSprite retain count is 2
        NSLog(@"B4 stop char rc %d", [characterSprite retainCount]);

        [characterSprite stopAllActions];

        //Here characterSprite retain count is still 2
        NSLog(@"B4 stop char rc %d", [characterSprite retainCount]);

    [self removeChild:characterSprite cleanup:YES];

>>>     //Here characterSprite retain count is 1 WHY?
>>>     NSLog(@"char rc %d", [characterSprite retainCount]);

        //Here burpSprite retain count is 2
        NSLog(@"particles rc %d", [burpSprite retainCount]);

    [burpSprite stopAllActions];

        //Here burp retain count is 1
        NSLog(@"particles rc %d", [burpSprite retainCount]);
    [self removeChild:burpSprite cleanup:YES];

        // releases the animation objects
    [self unloadBurpAnimation];

        //Here burpAnim retainCount = 1
        NSLog(@"BURP ANIM rc = %d", [burpAnim retainCount]);

        //Here burpParticlesAnim retainCount = 0 , crashes if print here obviously.
        //NSLog(@"Particles ANIM rc = %d", [burpParticlesAnim retainCount]);

        [[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];
    [[CCTextureCache sharedTextureCache] removeUnusedTextures];
}

Two Problems: Though I have removed the characterSprite I get its retain count equal to one. Though I have released the burpAnim I get its retain count equal to one, due to this CCSpriteFrameCache is unable to remove the spriteFrames and hence texture is not removed too.

Are both of these problems interlinked, somehow? Thankyou guys for reading this, any help is much appreciated.

1

1 Answers

0
votes

Two Problems: Though I have removed the characterSprite I get its retain count equal to one. Though I have released the burpAnim I get its retain count equal to one, due to this CCSpriteFrameCache is unable to remove the spriteFrames and hence texture is not removed too.

Your first problem is that you are calling retainCount and thinking that the number returned is meaningful.

The retain count of an object should always be treated as a delta. If you increase it by retaining something, you must decrease it by releasing or autoreleasing somewhere else. Where is entirely dependent on how long you "own" a reference to the object.

There are some other questionable bits:

    burpAnim = [[CCAnimation alloc] initWithFrames:burpFrames delay:0.04f];
    [burpFrames removeAllObjects];

Why do you set up the contents of burpFrames in a for() loop just before, then pass it to the CCAnimation, then promptly removeAllObjects? If the CCAnimation doesn't copy the array, you've just emptied it out from under the animation. Since the array is autoreleased, no need to empty it.

Same goes for burpParticleFrames.

Taking a lock (dictLock) on a thread and then calling the main thread seems dubious; like a great way to generate a dead lock. If the main thread isn't supposed to take a lock on that thread, do you have more than one background thread running around? What is your concurrency model?

Note that [self removeChild:characterSprite cleanup:YES]; likely releases characterSprite as, hopefully, the method on self would follow normal Cocoa conventions.

This all sounds like you have some basic retain/release problems. Read the guide and then use Build and Analyze to fix any issues. If your app still crashes, use Zombie detection to figure out what is being messaged after deallocation.