Cocos2d - removeSpriteFrames和removeAllTextures不会立即生效

时间:2013-09-09 20:06:18

标签: memory-management cocos2d-iphone crash textures

修改 我在前一个场景的dealloc和cleanup方法中添加了以下代码(我称之为replaceScene的方法,但它没有任何效果。即使在创建InstructionScene后的1/2/5秒内,内存仍然存在包含前一场景中的资源。强制删除它们的唯一方法是在创建场景后0.1秒(通过回调)将它们从新场景中删除。这有点奇怪。 / strong>

以下是场景清理和daelloc方法的代码:

-(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];
}

原始问题:

我在游戏中有几个场景,到目前为止,我在每个场景的开头使用了下面的代码来删除以前存储的纹理。然而,有一种情况,这不起作用:当我用一个新的场景(让我们称之为B)替换场景(让我们称之为A)没有精灵,只有一些标签从字体图像表创建。

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

新对象确实创建得太快,因为以下调用似乎没有任何效果:

-(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];
        ....
     }
}

在调用替换场景方法的那一刻,两个CCLayer(CCScane)对象同时处于活动状态。但是,前一场景中的纹理不会被删除。 如果在指令场景中添加和使用精灵表,则相同的代码可以正常工作。对此的一个调整是使用回调选择器去除0.1f 之后的所有纹理,但这不是非常优雅和平滑:

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

这是一个已知问题吗?它可能会导致潜在的崩溃。

我在这里粘贴代码以确保您可以尝试:

//
//  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 个答案:

答案 0 :(得分:1)

由于您在已经运行的场景中初始化新场景,因此两个场景同时处于活动状态。如果您尝试在新场景的初始化期间删除“未使用”资源,则由于现有场景仍在使用这些资源,因此不会发生任何(或不会太多)。

你只能做两件事:

  • 两个场景之间的加载场景,允许第一个场景在下一个场景初始化之前解除分配
  • 在场景dealloc期间卸载所有未使用的资产,而不是在初始化新场景时

如果两个场景都使用了很多独特的资产,那么加载场景是最好的方法,因此同时在内存中同时使用可能会导致内存警告,甚至由于内存压力而导致应用程序被迫终止。

另一种方法最适用于所有其他情况。请注意卸载特定资源而不是使用“未使用”方法,因为另一个场景当前可能正在使用此资源,并且如果另一个场景将其删除,则会强制重新加载它。

PS:只要您的应用程序即使在内存容量最小的设备上也不会受到内存压力,您也不应该只是为了防止它们被反复重新加载而移除资源。使用已缓存的纹理可以更快地切换场景。