Cocos2d - removeSpriteFrames and removeAllTextures do not work straight away

て烟熏妆下的殇ゞ 提交于 2019-12-11 22:37:53

问题


EDIT: I added the following code in both the dealloc and cleanup method of the previous scene (the one from which I call replaceScene but it does not have any effect. Even after 1/2/5 seconds from when InstructionScene has being created the memory does still contain the assets from the previous scene. The only way to force the removal of those is to remove them from the new scene 0.1f seconds after the scene is being created (via a Callback). This is kind of weird.

Here is the code of the previous scene cleanup and daelloc methods:

-(void) cleanup
{
    CCLOG(@"");
    CCLOG(@"");
    CCLOG(@"PlanetSelection Menu cleanup");
    CCLOG(@"");
    [super cleanup];
    [planetLayer removeAllChildrenWithCleanup:YES];
    [self removeAllChildrenWithCleanup: YES];

    [[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFramesFromFile:textureFileName];
    [[CCTextureCache sharedTextureCache] removeUnusedTextures];
    [[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];
    [CCAnimationCache purgeSharedAnimationCache];
}


-(void) dealloc
{
    CCLOG(@"Dealloc gets caled");
    [CCAnimationCache purgeSharedAnimationCache];
    [[CCDirector sharedDirector] purgeCachedData];
    [[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];
    [[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFramesFromFile:textureFileName];
}

Original question:

I got several scenes in my game and so far I used the following bit of code at the beginning of each scene to remove previously stored textures. However there is a case in which this doesn't work: when I replace a scene (lets call it A) with a new scene (lets call it B) with no sprites and only some label created from font image sheet.

[[CCDirector sharedDirector] replaceScene: [InstructionsScene sceneWithLevelName:FIRST_LEVEL]];

The new object does get created too fast as the following call doesn't seem to have any effect:

-(id) initWithLevelName:(LevelName)name
{
    if ((self = [super init]))
    {
        //Remove stuff from previous scene
        [[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFrames];
        [[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames]; //Not really necessary


        //Use these
        [[CCTextureCache sharedTextureCache] removeUnusedTextures]; //Not really needed
        [[CCTextureCache sharedTextureCache] removeAllTextures];
        [[CCDirector sharedDirector] purgeCachedData];
        [CCAnimationCache purgeSharedAnimationCache];
        ....
     }
}

At the moment in which the replace scene method is being called the two CCLayer (CCScane) objects are alive at the same time. However texture from the previous scene do not get removed. The same code works perfectly if a sprite sheet is being added and used in the Instruction scene. A tweak to this is to use a callback to a selector removing all textures after 0.1f, however this is not very elegant and smooth:

 [self runAction:[CCSequence actionOne:[CCDelayTime actionWithDuration:0.1f] two:[CCCallFunc actionWithTarget:self selector:@selector(removeStuffFromPreviousScene)]]];

Is this a known issue? It could cause potential crashes.

I paste here the code to make sure you can try it out:

//
//  InstructionsScene.h
//
//  Created by mm24 on 09/09/13.
//  Copyright 2013 mm24. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "CommonEnumerations.h"

@interface InstructionsScene : CCLayer {

    LevelName levelName;
    float startTime;

    CCLabelBMFont * levelNameTitle;
    CCLabelBMFont * levelSubtitle;

    CCLabelBMFont * instructionsHeader;
    CCLabelBMFont * instructions;
}

+(id)sceneWithLevelName:(LevelName)name;
@end


//
//  InstructionsScene.m
//
//  Created by mm24 on 09/09/13.
//  Copyright 2013 mm24. All rights reserved.
//

#import "InstructionsScene.h"
#import "ShooterScene.h"
#import "AppDelegate.h"
#import "mach/mach.h"


@implementation InstructionsScene

+(id)sceneWithLevelName:(LevelName)name
{
    CCScene * scene = [CCScene node];
    InstructionsScene * layer = [[self alloc] initWithLevelName:name];

    [scene addChild:layer];
    return scene;
}


-(id) initWithLevelName:(LevelName)name
{
    if ((self = [super init]))
    {
        //Remove stuff from previous scene
        [[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFrames];
        [[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];


        //Use these
        [[CCTextureCache sharedTextureCache] removeUnusedTextures];
        [[CCTextureCache sharedTextureCache] removeAllTextures];
        [[CCDirector sharedDirector] purgeCachedData];
        [CCAnimationCache purgeSharedAnimationCache];

        //Try out and use it. Not compulsory
        [self removeAllChildrenWithCleanup: YES];

        CCLOG(@"init with level name");
        levelName = name;
        startTime = 10.0f;

        levelNameTitle = [CCLabelBMFont labelWithString:@"Title" fntFile:@"bitmapFontTest.fnt"];
        levelNameTitle.position = CGPointMake(160.0f, 420.0f);
        levelNameTitle.anchorPoint = CGPointMake(0.5f, 0.5f);
        levelNameTitle.scale = 1.3f;
        [self addChild:levelNameTitle z:1] ;

        levelSubtitle = [CCLabelBMFont labelWithString:@"Subtitle" fntFile:@"bitmapFontTest.fnt"];
        levelSubtitle.position = CGPointMake(160.0f, 400.0f);
        levelSubtitle.anchorPoint = CGPointMake(0.5f, 0.5f);
        levelSubtitle.scale = 0.7f;
        [self addChild:levelSubtitle z:1] ;

        instructionsHeader   = [CCLabelBMFont labelWithString:@" Instructions " fntFile:@"bitmapFontTest.fnt"];
        instructionsHeader.position = CGPointMake(160.0f, 240.0f);
        instructionsHeader.anchorPoint = CGPointMake(0.5f, 0.5f);
        instructionsHeader.scale = 0.7f;
        [self addChild:instructionsHeader z:1] ;

        instructions  = [CCLabelBMFont labelWithString:@"Press any key" fntFile:@"bitmapFontTest.fnt"];
        instructions.position = CGPointMake(160.0f, 200.0f);
        instructions.anchorPoint = CGPointMake(0.5f, 0.5f);
        instructions.scale = 0.7f;
        [self addChild:instructions z:1] ;

        [[CCDirector sharedDirector] resume];


     //   [self runAction:[CCSequence actionOne:[CCDelayTime actionWithDuration:0.1f] two:[CCCallFunc actionWithTarget:self selector:@selector(removeStuffFromPreviousScene)]]];
        [self runAction:[CCSequence actionOne:[CCDelayTime actionWithDuration:1.0f] two:[CCCallFunc actionWithTarget:self selector:@selector(report_memory)]]];
        [self runAction:[CCSequence actionOne:[CCDelayTime actionWithDuration:2.0f] two:[CCCallFunc actionWithTarget:self selector:@selector(report_memory)]]];
        [self runAction:[CCSequence actionOne:[CCDelayTime actionWithDuration:5.0f] two:[CCCallFunc actionWithTarget:self selector:@selector(report_memory)]]];
        [self callBackReplace];


    }
    return self;
}

-(void) removeStuffFromPreviousScene
{
    //Use these
    [[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFrames];
    [[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];


    //Use these
    [[CCTextureCache sharedTextureCache] removeUnusedTextures];
    [[CCTextureCache sharedTextureCache] removeAllTextures];
    [[CCDirector sharedDirector] purgeCachedData];
    [CCAnimationCache purgeSharedAnimationCache];

}

-(void) report_memory {
    CCLOG(@"");
    CCLOG(@"");
    CCLOG(@"InstructionScene info:");
    CCLOG(@"");

    [[CCTextureCache sharedTextureCache] dumpCachedTextureInfo];
    struct task_basic_info info;

    mach_msg_type_number_t size = sizeof(info);
    kern_return_t kerr = task_info(mach_task_self(),
                                   TASK_BASIC_INFO,
                                   (task_info_t)&info,
                                   &size);
    if( kerr == KERN_SUCCESS ) {
        NSLog(@"Memory in use (in bytes): %u", info.resident_size);
    } else {
        NSLog(@"Error with task_info(): %s", mach_error_string(kerr));
    }
}

-(void) nextSuggestionPressed
{
    [self stopAllActions];
    [self callBackReplace];
}

-(void) callBackReplace 
{
    [self runAction:[CCSequence actions:
                     [CCDelayTime actionWithDuration:startTime] ,
                     [CCCallFunc actionWithTarget:self selector:@selector(replaceWithShooterScene)],
                      nil]
     ];
}


-(void) replaceWithShooterScene
{
      [[CCDirector sharedDirector] replaceScene:[ShooterScene sceneWithLevelName:levelName]];
}


@end

回答1:


Since you initialize the new scene within an already running scene, both scenes are alive at the same time. If you try to remove "unused" resources during init of the new scene, nothing (or not much) will happen since the existing scene is still using these assets.

You can only do two things:

  • a loading scene in between the two scenes to allow the first scene to deallocate before the next scene is initialized
  • unloading all unused assets during dealloc of a scene, not when initializing a new one

The loading scene is the best approach if both scenes use a lot of unique assets, so having both in memory at the same time may cause memory warnings or even the app forced to terminate due to memory pressure.

The other approach is best used in all other cases. Just be careful to unload specific resources rather than using the "unused" methods because another scene may currently be using this resource and will be forced to reload it if the other scene removes it.

PS: as long as your app isn't under memory pressure even on devices with the least amount of memory you shouldn't remove resources simply to prevent them from being reloaded again and again. Switching scenes will be a lot faster with already cached textures.



来源:https://stackoverflow.com/questions/18706146/cocos2d-removespriteframes-and-removealltextures-do-not-work-straight-away

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!