0

編集: 前のシーンの dealloc メソッドと cleanup メソッドの両方に次のコードを追加しました (replaceScene を呼び出す元のシーンですが、効果はありません。InstructionScene がメモリを作成してから 1/2/5 秒後でも、にはまだ前のシーンのアセットが含まれています.それらを強制的に削除する唯一の方法は, シーンが作成されてから 0.1f 秒後に新しいシーンからそれらを削除することです (コールバックを介して). これはちょっと変です.

前のシーンのクリーンアップと 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];
}

元の質問:

ゲームにいくつかのシーンがあり、これまでのところ、各シーンの最初に次のコードを使用して、以前に保存したテクスチャを削除しました。ただし、これが機能しない場合があります。シーン (A と呼びましょう) を、スプライトがなく、フォント イメージ シートから作成されたラベルのみの新しいシーン (B と呼びましょう) に置き換える場合です。

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

シーン置換メソッドが呼び出されている瞬間に、2 つの CCLayer (CCScane) オブジェクトが同時にアクティブになります。ただし、前のシーンのテクスチャは削除されません。スプライト シートが追加され、Instruction シーンで使用されている場合、同じコードが完全に機能します。これを微調整するには、セレクターへのコールバックを使用して 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
4

1 に答える 1

1

すでに実行中のシーン内で新しいシーンを初期化するため、両方のシーンが同時に有効になります。新しいシーンの初期化中に「未使用」のリソースを削除しようとしても、既存のシーンがまだこれらのアセットを使用しているため、何も (またはほとんど) 起こりません。

次の 2 つのことしかできません。

  • 次のシーンが初期化される前に最初のシーンの割り当てを解除できるようにするための、2 つのシーンの間の読み込みシーン
  • 新しいアセットを初期化するときではなく、シーンの割り当て解除中にすべての未使用のアセットをアンロードする

両方のシーンが多くの固有のアセットを使用している場合は、ロード シーンが最適な方法です。そのため、両方を同時にメモリに保持すると、メモリ警告が発生したり、メモリ不足のためにアプリが強制終了されたりする可能性があります。

他のすべてのケースでは、他のアプローチを使用するのが最適です。「未使用」メソッドを使用するのではなく、特定のリソースをアンロードするように注意してください。別のシーンが現在このリソースを使用している可能性があり、他のシーンがそれを削除すると強制的に再ロードされるからです。

PS: メモリの量が最も少ないデバイスでも、アプリがメモリ不足に陥っていない限り、リソースが何度も再ロードされるのを防ぐためだけにリソースを削除するべきではありません。シーンの切り替えは、既にキャッシュされたテクスチャを使用するとはるかに高速になります。

于 2013-09-09T20:14:22.253 に答える