5

I have been struggling with this for days, for some reason my SKScenes are not deallocating correctly, this results in bounded memory growth as each time i exit and enter a scene the memory jumps up. This means after say 10 rounds of the game the App crashes. As far as i'm aware after much checking i do not have any retain cycles or strong references to the scenes themselves and whilst i know textures are cached and held in memory surely once preloaded the memory shouldn't be going up each time.

This is how i setup the skview and first scene in the viewcontroller:

-(void)loadStartScreen{

    SKView *theView = (SKView *) self.view;
    theView.showsFPS = YES;
    theView.showsNodeCount = YES;
    //Sprite Kit applies additional optimizations to improve rendering performance
    theView.ignoresSiblingOrder = YES;

    // Create and configure the scene.
    MainMenuScene *theScene = [MainMenuScene sceneWithSize:theView.bounds.size];
    theScene.scaleMode = SKSceneScaleModeAspectFill;
    theScene.backgroundColor = [UIColor grayColor];

    // Present the scene
    [theView presentScene:theScene];

This is the code from within my MainMenuScene to create and move to the next scene:

 SKScene *theScene;
    SKTransition *theTransition;
    switch (theTag.intValue) {
        case 0: // start game
            // stop music
            [[appDelegate musicPlayer]stop];
            theScene  = [[GameLevelScene alloc] initWithSize:self.view.bounds.size];
            // transition type
            theTransition = [SKTransition fadeWithDuration:1.0];
            break;
        case 1: // settings
            theScene  = [[SettingsScene alloc] initWithSize:self.view.bounds.size];
            // transition type
            theTransition = [SKTransition flipHorizontalWithDuration:1.0];
            break;
        case 2: // iap
            theScene  = [[IAPScene alloc] initWithSize:self.view.bounds.size];
            // transition type
            theTransition = [SKTransition flipHorizontalWithDuration:1.0];
            break;
        case 3: // unlocks screen
            NSLog(@"scene is %@",self.view.scene);
             theScene  = [[deletmet alloc] initWithSize:self.view.bounds.size];
            // transition type
            theTransition = [SKTransition flipHorizontalWithDuration:1.0];
            NSLog(@"scene is after %@",self.view.scene);
            break;
        case 4: // level complete
            theScene  = [[LevelCompleteScene alloc] initWithSize:self.view.bounds.size];
            // transition type
            theTransition = [SKTransition flipHorizontalWithDuration:1.0];
            break;
        case 5: // cheats
            theScene  = [[CheatsScene alloc] initWithSize:self.view.bounds.size];
            // transition type
            theTransition = [SKTransition flipHorizontalWithDuration:1.0];
            break;

        default:
            break;
    }

Now i know that you should use "[self.view presentScene:]" as the SkView holds the scene but because nothing is working i've been experimenting to see why the scene does not seem to deallocate. When i try pressing the button for case 2 following i get the resulting NSlogs:

[self presentScene:theScene];

NSLOG result = "IAP SCENE DEALLOC" but the user is not taken to the next scene.

[self presentScene:nil];

NSLOG result = "IAP SCENE DEALLOC" but the user is not taken to the next scene.

[self.view presentScene:theScene];

No nslog results even though the current MainMenuScene has an NSLOG for dealloc

[self.view presentScene:nil];

NSLOG result = IAP SCENE DEALLOC and the screen becomes grey

Now this is all very odd as surely when i do get a DEALLOC NSLOG message it should be giving me the NSLOG for the dealloc of the current scene aka MainMenuScene rather than the scene it should be loading.

Am i doing something wrong? Am i working with the SKView incorrectly, any help would be greatly appreciated as i have read a variety of posts all of which have not helped.

  • Update: This is the code for each scene that i am moving back and forth between, notice the lack of actions or any strong references to self:

Main Menu:

#import "MainMenuScene.h"
#import "SKScene+SceneUtils.h"
#import "GameLevelScene.h"
#import "SettingsScene.h"
#import "CreditsScene.h"
#import "IAPScene.h"
#import "UnlocksScene.h"
#import "LevelCompleteScene.h"
#import "CheatsScene.h"
#import "AppDelegate.h"
#import "UserDetails.h"
#import "ItemBannerLabel.h"
#import "ItemWindow.h"
#import "TextureList.h"
#import "TextureLoader.h"

@interface MainMenuScene(){

    SKSpriteNode *backgroundImage;
    SKSpriteNode *topBar;
    SKSpriteNode *bottomBar;
    SKSpriteNode *ladybirds;
    SKLabelNode *title;
    SKLabelNode *subtitle;
    SKLabelNode *coffee;
    ItemWindow *settingsWin;
    ItemWindow *iapWin;
    ItemWindow *unlocksWin;
    AGSpriteButton *startButton;
    AGSpriteButton *continueButton;
    AGSpriteButton *settingsButton;
    AGSpriteButton *iapButton;
    AGSpriteButton *unlocksButton;
    SKEmitterNode *theParticles;
    //SKAction *delay;
    //AppDelegate *appDelegate;
}

@end

@implementation MainMenuScene


#pragma mark - Scene Appears
-(void)didMoveToView:(SKView *)view {

    // setup UI
    [self createUI];
    // setup view
    [self setupView];
}
-(void)willMoveFromView:(SKView *)view{

}

#pragma mark - CreateUI
-(void)createUI{

    // scene size
    self.scene.size = [[TextureList sharedManager]returnTextureSize:@"kMMBg"];
    // background
    self.scene.backgroundColor = [SKColor colorWithRed:0/255.0 green:0/255.0 blue:0/255.0 alpha:1.0];

    // masked background
    backgroundImage = [SKSpriteNode spriteNodeWithTexture:[SKTexture textureWithImageNamed:kMMBg] size:[[TextureList sharedManager]returnTextureSize:@"kMMBg"]];
    backgroundImage.position = screenCenter;
    backgroundImage.zPosition = self.zPosition+1;
    [self addChild:backgroundImage];

    ladybirds = [SKSpriteNode spriteNodeWithTexture:[SKTexture textureWithImageNamed:kMMLadybirds] size:[[TextureList sharedManager]returnTextureSize:kMMLadybirds]];
    ladybirds.position = CGPointMake(CGRectGetMaxX(self.frame)-ladybirds.frame.size.width/2, screenCenter.y);
    ladybirds.zPosition = bottomBar.zPosition+5;
    [self addChild:ladybirds];

    // buttons
    startButton = [[AGSpriteButton alloc]initWithTexture:[SKTexture textureWithImageNamed:kMMStartBtn] color:[UIColor clearColor] size:[[TextureList sharedManager]returnTextureSize:kMMStartBtn]];
    [startButton setLabelWithText:@"" andFont:[UIFont fontWithName:kFontName size:kFontSizeButton] withColor:nil];
    startButton.position = CGPointMake(ladybirds.position.x-ladybirds.frame.size.width/4, ladybirds.position.y-ladybirds.frame.size.height/16);
    startButton.zPosition = ladybirds.zPosition+1;
    [startButton addTarget:self selector:@selector(buttonPressed:) withObject:[NSNumber numberWithInt:0] forControlEvent:AGButtonControlEventTouchUpInside];
    [self addChild:startButton];

    // emitter
    theParticles = [NSKeyedUnarchiver unarchiveObjectWithFile:[[NSBundle mainBundle] pathForResource:@"FlowerPetalParticle" ofType:@"sks"]];
    theParticles.zPosition = backgroundImage.zPosition+1;
    theParticles.position = CGPointMake((CGRectGetMinX(self.scene.frame)),startButton.position.y);
    theParticles.particlePositionRange = CGVectorMake(0.0, CGRectGetMaxY(self.frame)-topBar.frame.size.height-bottomBar.frame.size.height);
    if ([[[UserDetails sharedManager]userDevice]isEqualToString:@"ipad"]) {
        theParticles.particleLifetime = 8.0;
        theParticles.particleScale = 0.5;
        theParticles.particleScaleRange = 0.2;
    }
    [self addChild:theParticles];

    topBar = [SKSpriteNode spriteNodeWithColor:[UIColor whiteColor] size:[[TextureList sharedManager]returnTextureSize:kMMTopBar]];
    topBar.position = CGPointMake(screenCenter.x, CGRectGetMaxY(self.frame)-topBar.frame.size.height/2);
    topBar.zPosition = theParticles.zPosition+1;
    [self addChild:topBar];

    bottomBar = [SKSpriteNode spriteNodeWithColor:[UIColor whiteColor] size:[[TextureList sharedManager]returnTextureSize:kMMBottomBar]];
    bottomBar.position = CGPointMake(screenCenter.x, CGRectGetMinY(self.frame)+bottomBar.frame.size.height/2);
    bottomBar.zPosition = theParticles.zPosition+1;
    [self addChild:bottomBar];

    settingsWin = [[ItemWindow alloc]initWithImageNamed:kMMCreditsBtn withLabel:@"SETTINGS" setLabelTop:NO];
    settingsWin.theLabel.fontColor = [UIColor blackColor];
    settingsWin.theLabel.fontSize = 15;
    settingsWin.position = CGPointMake(CGRectGetMinX(self.frame)+settingsWin.frame.size.width/2+20, CGRectGetMinY(self.frame)+settingsWin.frame.size.height/2+25);
    settingsWin.zPosition = bottomBar.zPosition+1;
    [self addChild:settingsWin];

    settingsButton = [[AGSpriteButton alloc]initWithTexture:nil color:nil size:CGSizeMake(settingsWin.size.width*2, settingsWin.size.height*2)];
    [settingsButton setLabelWithText:@"" andFont:[UIFont fontWithName:kFontName size:kFontSizeButton] withColor:nil];
    settingsButton.position = settingsWin.position;
    settingsButton.zPosition = settingsWin.zPosition+1;
    [settingsButton addTarget:self selector:@selector(buttonPressed:) withObject:[NSNumber numberWithInt:1] forControlEvent:AGButtonControlEventTouchUpInside];
    [self addChild:settingsButton];

    iapWin = [[ItemWindow alloc]initWithImageNamed:kMMIapBtn withLabel:@"SHOP" setLabelTop:NO];
    iapWin.theLabel.fontColor = [UIColor blackColor];
    iapWin.theLabel.fontSize = 15;
    iapWin.position = CGPointMake(settingsWin.position.x+iapWin.frame.size.width+30, settingsWin.position.y);
    iapWin.zPosition = bottomBar.zPosition+1;
    [self addChild:iapWin];

    iapButton = [[AGSpriteButton alloc]initWithTexture:nil color:nil size:CGSizeMake(iapWin.size.width*2, iapWin.size.height*2)];
    [iapButton setLabelWithText:@"" andFont:[UIFont fontWithName:kFontName size:kFontSizeButton] withColor:nil];
    iapButton.position = iapWin.position;
    iapButton.zPosition = iapWin.zPosition+1;
    [iapButton addTarget:self selector:@selector(buttonPressed:) withObject:[NSNumber numberWithInt:2] forControlEvent:AGButtonControlEventTouchUpInside];
    [self addChild:iapButton];

    unlocksWin = [[ItemWindow alloc]initWithImageNamed:kMMUnlockBtn withLabel:@"UNLOCKS" setLabelTop:NO];
    unlocksWin.theLabel.fontColor = [UIColor blackColor];
    unlocksWin.theLabel.fontSize = 15;
    unlocksWin.position = CGPointMake(iapWin.position.x+unlocksWin.frame.size.width+30, iapWin.position.y);
    unlocksWin.zPosition = bottomBar.zPosition+1;
    [self addChild:unlocksWin];

    unlocksButton = [[AGSpriteButton alloc]initWithTexture:nil color:nil size:CGSizeMake(unlocksWin.size.width*2, unlocksWin.size.height*2)];
    [unlocksButton setLabelWithText:@"" andFont:[UIFont fontWithName:kFontName size:kFontSizeButton] withColor:nil];
    unlocksButton.position = unlocksWin.position;
    unlocksButton.zPosition = unlocksWin.zPosition+1;
    [unlocksButton addTarget:self selector:@selector(buttonPressed:) withObject:[NSNumber numberWithInt:3] forControlEvent:AGButtonControlEventTouchUpInside];
    [self addChild:unlocksButton];

    // Labels
    title = [[SKLabelNode alloc]initWithFontNamed:kFontName];
    if ([[[UserDetails sharedManager]userDevice]isEqualToString:@"iphone4"]) {
        title.fontSize = 60;
    }
    else{
    title.fontSize = 75;
    }
    title.fontColor = [UIColor blackColor];
    title.position = CGPointMake(topBar.frame.size.width/4, topBar.position.y-10);
    title.zPosition = topBar.zPosition+1;
    title.text = @"FLOWERS";
    [self addChild:title];

    subtitle = [[SKLabelNode alloc]initWithFontNamed:kFontName];

    if ([[[UserDetails sharedManager]userDevice]isEqualToString:@"iphone4"]) {
        subtitle.fontSize = 24;
    }
    else{
        subtitle.fontSize = 30;
    }
    subtitle.fontColor = [UIColor grayColor];
    subtitle.position = CGPointMake(title.position.x, title.position.y-title.frame.size.height/2-10);
    subtitle.zPosition = topBar.zPosition+1;
    subtitle.text = @"THE BEAUTIFUL MEADOW";
    [self addChild:subtitle];

    AGSpriteButton *testButton3 = [[AGSpriteButton alloc]initWithTexture:[SKTexture textureWithImageNamed:kMMIapBtn] color:[UIColor clearColor] size:[[TextureList sharedManager]returnTextureSize:kMMIapBtn]];
    [testButton3 setLabelWithText:@"" andFont:[UIFont fontWithName:kFontName size:kFontSizeButton] withColor:nil];
    testButton3.position = CGPointMake(bottomRight.x-testButton3.frame.size.width/1.5, bottomRight.y+settingsButton.frame.size.height/1.5);
    testButton3.zPosition = interfaceLayer;
    [testButton3 addTarget:self selector:@selector(buttonPressed:) withObject:[NSNumber numberWithInt:4] forControlEvent:AGButtonControlEventTouchUpInside];
    [self addChild:testButton3];

    AGSpriteButton *testButton4 = [[AGSpriteButton alloc]initWithTexture:[SKTexture textureWithImageNamed:kMMIapBtn] color:[UIColor clearColor] size:[[TextureList sharedManager]returnTextureSize:kMMIapBtn]];
    [testButton4 setLabelWithText:@"" andFont:[UIFont fontWithName:kFontName size:kFontSizeButton] withColor:nil];
    testButton4.position = CGPointMake(testButton3.position.x-testButton4.frame.size.width, bottomCenter.y+settingsButton.size.height/1.5);
    testButton4.zPosition = interfaceLayer;
    [testButton4 addTarget:self selector:@selector(buttonPressed:) withObject:[NSNumber numberWithInt:5] forControlEvent:AGButtonControlEventTouchUpInside];
    [self addChild:testButton4];

    if ([[[UserDetails sharedManager]userDevice] isEqualToString:@"ipad"]) {

        title.fontSize = 105;
        title.position = CGPointMake(topBar.frame.size.width/6+20, topBar.position.y+10);
        subtitle.position = CGPointMake(title.position.x, title.position.y-title.frame.size.height/2-10);
        subtitle.fontSize = 41;

        settingsWin.theLabel.fontSize = 25;
        iapWin.theLabel.fontSize = 25;
        unlocksWin.theLabel.fontSize = 25;

        settingsWin.position = CGPointMake(CGRectGetMinX(self.frame)+settingsWin.frame.size.width/2+20, CGRectGetMinY(self.frame)+settingsWin.frame.size.height+25);
        iapWin.position = CGPointMake(settingsWin.position.x+iapWin.frame.size.width+30, settingsWin.position.y);
        unlocksWin.position = CGPointMake(iapWin.position.x+unlocksWin.frame.size.width+30, settingsWin.position.y);
    }
}

#pragma mark - Setup View
-(void)setupView{
    // setup music players slightly quieter music now
    /*appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    [self fadeVolumeIn:[appDelegate musicPlayer] toVolume:0.45];
    [[appDelegate soundFxPlayer]setVolume:0.25];
    [[appDelegate soundFxPlayer]play];

    // no continue if the user has not progressed past level 1
    if ([[UserDetails sharedManager]userCurrentLevel] <= 1) {
        continueButton.userInteractionEnabled = NO;
        continueButton.hidden = YES;
    }
    else{
        continueButton.userInteractionEnabled = YES;
        continueButton.hidden = NO;
    }*/
}

#pragma mark - Interaction
-(void)buttonPressed:(NSNumber*)theTag{

    SKScene *theScene;
    SKTransition *theTransition;
    switch (theTag.intValue) {
        case 0: // start game
            // stop music
            //[[appDelegate musicPlayer]stop];
            theScene  = [[GameLevelScene alloc] initWithSize:self.view.bounds.size];
            // transition type
            theTransition = [SKTransition fadeWithDuration:1.0];
            break;
        case 1: // settings
            theScene  = [[SettingsScene alloc] initWithSize:self.view.bounds.size];
            // transition type
            theTransition = [SKTransition flipHorizontalWithDuration:1.0];
            break;
        case 2: // iap
            theScene  = [[IAPScene alloc] initWithSize:self.view.bounds.size];
            // transition type
            theTransition = [SKTransition flipHorizontalWithDuration:1.0];
            break;
        case 3: // unlocks screen
            theScene  = [[UnlocksScene alloc] initWithSize:self.view.bounds.size];
            // transition type
            theTransition = [SKTransition flipHorizontalWithDuration:1.0];
            break;
        case 4: // level complete
            theScene  = [[LevelCompleteScene alloc] initWithSize:self.view.bounds.size];
            // transition type
            theTransition = [SKTransition flipHorizontalWithDuration:1.0];
            break;
        case 5: // cheats
            theScene  = [[CheatsScene alloc] initWithSize:self.view.bounds.size];
            // transition type
            theTransition = [SKTransition flipHorizontalWithDuration:1.0];
            break;

        default:
            break;
    }

    // play sound
    [self menuButtonPressed];
    [self.view presentScene:theScene transition:theTransition];

}

@end

Settings Screen:

#import "SettingsScene.h"
#import "SKScene+SceneUtils.h"
#import "AGSpriteButton.h"
#import "ItemBannerLabel.h"
#import "TextureList.h"
#import "UnlockController.h"
#import "UserDetails.h"
#import "TutorialFlowerTarget.h"
#import "CreditsScene.h"

@interface SettingsScene(){

    AGSpriteButton *backButton;
    AGSpriteButton *resetButton;
    AGSpriteButton *resetTutorialsButton;
    AGSpriteButton *creditsButton;
    ItemBannerLabel *titleLabel;
    SKLabelNode *copyrightLabel;
    UIAlertView *resetGameAlert;
    UIAlertView *resetTutAlert;
    SKScene *theScene;
    SKTransition *theTransition;
    SKSpriteNode *menuBg;
}

@end

@implementation SettingsScene

#pragma mark - SCENE APPEARS
-(void)didMoveToView:(SKView *)view {

    // setup UI
    [self createUI];
}

#pragma mark - CREATE UI
-(void)createUI{

    // background
    self.scene.backgroundColor = [UIColor whiteColor];

    menuBg = [[SKSpriteNode alloc]initWithTexture:[SKTexture textureWithImageNamed:kMDBg] color:nil size:[[TextureList sharedManager]returnTextureSize:@"kMDBg"]];
    menuBg.position = screenCenter;
    menuBg.zPosition = self.zPosition+1;
    [self addChild:menuBg];

    // labels
    titleLabel = [[ItemBannerLabel alloc]initWithBgImageNamed:kMDTitle withLabel:@"GAME SETTINGS" withfont:kFontName withSize:kFontSizeMDTitle];
    titleLabel.position = CGPointMake(topLeft.x+titleLabel.frame.size.width/2+10,topLeft.y-titleLabel.frame.size.height/2-10);
    titleLabel.zPosition = interfaceLayer;
    [self addChild:titleLabel];

    copyrightLabel = [SKLabelNode labelNodeWithFontNamed:kFontName];
    copyrightLabel.text = [NSString stringWithFormat:@"COPYRIGHT © 2015 RICHARD ACHERKI"];
    copyrightLabel.fontSize = kFontSizeMDSmall;
    copyrightLabel.fontColor = [UIColor blackColor];
    copyrightLabel.verticalAlignmentMode = SKLabelVerticalAlignmentModeCenter;
    copyrightLabel.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeCenter;
    copyrightLabel.position = CGPointMake(bottomCenter.x, bottomCenter.y+copyrightLabel.frame.size.height);
    copyrightLabel.zPosition = interfaceLayer;
    [self addChild:copyrightLabel];

    // buttons
    if ([[[UserDetails sharedManager]userDevice]isEqualToString:@"ipad"]) {
        resetTutorialsButton = [[AGSpriteButton alloc]initWithTexture:[SKTexture textureWithImageNamed:kMDButton] color:nil size:CGSizeMake([[TextureList sharedManager]returnTextureSize:kMDButton].width*4, [[TextureList sharedManager]returnTextureSize:kMDButton].height*2)];
        [resetTutorialsButton setLabelWithText:@"RESET TUTORIALS" andFont:[UIFont fontWithName:kFontName size:kFontSizeButton] withColor:[UIColor whiteColor]];
        resetTutorialsButton.position = CGPointMake(screenCenter.x, titleLabel.position.y-titleLabel.frame.size.height/2-resetTutorialsButton.frame.size.height/2-30);
        resetTutorialsButton.zPosition = menuBg.zPosition+1;
        [resetTutorialsButton addTarget:self selector:@selector(buttonPressed:) withObject:[NSNumber numberWithInt:0] forControlEvent:AGButtonControlEventTouchUpInside];
    }
    else{
        resetTutorialsButton = [[AGSpriteButton alloc]initWithTexture:[SKTexture textureWithImageNamed:kMDButton] color:nil size:CGSizeMake([[TextureList sharedManager]returnTextureSize:kMDButton].width*2, [[TextureList sharedManager]returnTextureSize:kMDButton].height)];
        [resetTutorialsButton setLabelWithText:@"RESET TUTORIALS" andFont:[UIFont fontWithName:kFontName size:kFontSizeButton] withColor:[UIColor whiteColor]];
        resetTutorialsButton.position = CGPointMake(screenCenter.x, titleLabel.position.y-titleLabel.frame.size.height/2-resetTutorialsButton.frame.size.height/2-30);
        resetTutorialsButton.zPosition = menuBg.zPosition+1;
        [resetTutorialsButton addTarget:self selector:@selector(buttonPressed:) withObject:[NSNumber numberWithInt:0] forControlEvent:AGButtonControlEventTouchUpInside];
    }
    [self addChild:resetTutorialsButton];

    if ([[[UserDetails sharedManager]userDevice]isEqualToString:@"ipad"]) {
        resetButton = [[AGSpriteButton alloc]initWithTexture:[SKTexture textureWithImageNamed:kMDButton] color:nil size:CGSizeMake([[TextureList sharedManager]returnTextureSize:kMDButton].width*4, [[TextureList sharedManager]returnTextureSize:kMDButton].height*2)];
        [resetButton setLabelWithText:@"RESET GAME" andFont:[UIFont fontWithName:kFontName size:kFontSizeButton] withColor:[UIColor whiteColor]];
        resetButton.position = CGPointMake(resetTutorialsButton.position.x,resetTutorialsButton.position.y-resetTutorialsButton.frame.size.height-20);
        resetButton.zPosition = menuBg.zPosition+1;
        [resetButton addTarget:self selector:@selector(buttonPressed:) withObject:[NSNumber numberWithInt:1] forControlEvent:AGButtonControlEventTouchUpInside];
    }
    else{
        resetButton = [[AGSpriteButton alloc]initWithTexture:[SKTexture textureWithImageNamed:kMDButton] color:nil size:CGSizeMake([[TextureList sharedManager]returnTextureSize:kMDButton].width*2, [[TextureList sharedManager]returnTextureSize:kMDButton].height)];
        [resetButton setLabelWithText:@"RESET GAME" andFont:[UIFont fontWithName:kFontName size:kFontSizeButton] withColor:[UIColor whiteColor]];
        resetButton.position = CGPointMake(resetTutorialsButton.position.x,resetTutorialsButton.position.y-resetTutorialsButton.frame.size.height-20);
        resetButton.zPosition = menuBg.zPosition+1;
        [resetButton addTarget:self selector:@selector(buttonPressed:) withObject:[NSNumber numberWithInt:1] forControlEvent:AGButtonControlEventTouchUpInside];
    }
    [self addChild:resetButton];

    if ([[[UserDetails sharedManager]userDevice]isEqualToString:@"ipad"]) {
        creditsButton = [[AGSpriteButton alloc]initWithTexture:[SKTexture textureWithImageNamed:kMDButton] color:nil size:CGSizeMake([[TextureList sharedManager]returnTextureSize:kMDButton].width*4, [[TextureList sharedManager]returnTextureSize:kMDButton].height*2)];
        [creditsButton setLabelWithText:@"CREDITS" andFont:[UIFont fontWithName:kFontName size:kFontSizeButton] withColor:[UIColor whiteColor]];
        creditsButton.position = CGPointMake(resetButton.position.x,resetButton.position.y-resetButton.frame.size.height-20);
        creditsButton.zPosition = menuBg.zPosition+1;
        [creditsButton addTarget:self selector:@selector(buttonPressed:) withObject:[NSNumber numberWithInt:2] forControlEvent:AGButtonControlEventTouchUpInside];
    }
    else{
        creditsButton = [[AGSpriteButton alloc]initWithTexture:[SKTexture textureWithImageNamed:kMDButton] color:nil size:CGSizeMake([[TextureList sharedManager]returnTextureSize:kMDButton].width*2, [[TextureList sharedManager]returnTextureSize:kMDButton].height)];
        [creditsButton setLabelWithText:@"CREDITS" andFont:[UIFont fontWithName:kFontName size:kFontSizeButton] withColor:[UIColor whiteColor]];
        creditsButton.position = CGPointMake(resetButton.position.x,resetButton.position.y-resetButton.frame.size.height-20);
        creditsButton.zPosition = menuBg.zPosition+1;
        [creditsButton addTarget:self selector:@selector(buttonPressed:) withObject:[NSNumber numberWithInt:2] forControlEvent:AGButtonControlEventTouchUpInside];
    }
    [self addChild:creditsButton];    

    backButton = [[AGSpriteButton alloc]initWithTexture:[SKTexture textureWithImageNamed:kMDButton] color:nil size:[[TextureList sharedManager]returnTextureSize:kMDButton]];
    [backButton setLabelWithText:@"BACK" andFont:[UIFont fontWithName:kFontName size:kFontSizeButton] withColor:[UIColor whiteColor]];
    backButton.position = CGPointMake(bottomRight.x-backButton.frame.size.width/2-10, bottomRight.y+backButton.frame.size.height/2+10);
    backButton.zPosition = menuBg.zPosition+1;
    [backButton addTarget:self selector:@selector(buttonPressed:) withObject:[NSNumber numberWithInt:3] forControlEvent:AGButtonControlEventTouchUpInside];
    [self addChild:backButton];

}

#pragma mark - BUTTON PRESSED
-(void)buttonPressed:(NSNumber*)theTag{

    if (theTag.intValue == 0) { // reset tutorials
        resetTutAlert = [[UIAlertView alloc]initWithTitle:@"RESET TUTORIALS" message:@"Are you sure you want to reset tutorials?\nThis will cause all tutorials to show again when playing the game." delegate:self cancelButtonTitle:@"No" otherButtonTitles:@"Yes", nil];
        [resetTutAlert show];
    }
    else if (theTag.intValue == 1) { // reset game
        resetGameAlert = [[UIAlertView alloc]initWithTitle:@"RESET THE GAME" message:@"Are you sure you want to wipe your game progression?\nThis will remove all unlocks, scores and level progression." delegate:self cancelButtonTitle:@"No" otherButtonTitles:@"Yes", nil];
        [resetGameAlert show];
    }
    else if (theTag.intValue == 2) { // credits menu
        // play sound
        [self menuButtonPressed];
        theScene = [[CreditsScene alloc] initWithSize:self.view.bounds.size];
        theTransition = [SKTransition flipHorizontalWithDuration:1.0];
        [self.view presentScene:theScene transition:theTransition];
    }
    else if (theTag.intValue == 3) { // main menu
        // play sound
        [self menuButtonPressed];
        // scene to move to
        theScene  = [[MainMenuScene alloc] initWithSize:self.view.bounds.size];
        // transition type
        theTransition = [SKTransition flipHorizontalWithDuration:1.0];
        [self.view presentScene:theScene transition:theTransition];
    }

}

#pragma mark - RESET TUTORIALS
-(void)resetTutorials{

    for (id theObject in [[UserDetails sharedManager]userTutorials]) {
        TutorialFlowerTarget *theTut= [[[UserDetails sharedManager]userTutorials]objectForKey:theObject];
        [theTut setTriggered:NO];
    }
    [[UserDetails sharedManager]saveData];
}

#pragma mark - RESET UNLOCKS
-(void)resetUnlocks{

    [[UnlockController sharedManager]resetGame];
}
SmokersCough
  • 957
  • 7
  • 22
  • Are there any animations running when you present the next scene? I have seen nodes with animations running cause scenes to not release. Or if your scene is running an SKAction that could cause it too. I think you will have to show some more code so we can see what would cause the retain cycle. – Ben Kane May 15 '15 at 16:43
  • Hi Ben, I've added the code for each scene in their entirety, hopefully this should help. – SmokersCough May 16 '15 at 13:04
  • I am not seeing anything that would cause this, but can you post the code for `menuButtonPressed` – Skyler Lauren May 17 '15 at 19:18
  • Hi Skyler, perhaps this could be the issue: -(void)menuButtonPressed{ SKAction *playSound = [SKAction playSoundFileNamed:kButtonTappedSound waitForCompletion:NO]; [self runAction:playSound]; } As it may be creating a retain cycle on the [self runAction:playsound]; – SmokersCough May 17 '15 at 20:27
  • Could be but I think it the reference your buttons have to your screen. I posted an answer based in that. – Skyler Lauren May 17 '15 at 23:19

3 Answers3

4

I could be wrong but I suspect your button class is your offender. When you call...

[testButton4 addTarget:self selector:@selector(buttonPressed:) withObject:[NSNumber numberWithInt:5] forControlEvent:AGButtonControlEventTouchUpInside];

You pass self being that scene. In the button class the method..

-(void)addTarget:(id)target selector:(SEL)selector withObject:(id)object forControlEvent:(AGButtonControlEvent)controlEvent
{
    //check whether selector is already saved, otherwise it will get called twice

    if (marrSelectors == nil)
    {
        marrSelectors = [NSMutableArray new];
    }

    NSMutableDictionary *mdicSelector = [[NSMutableDictionary alloc]init];

    [mdicSelector setObject:target forKey:@"target"];
    [mdicSelector setObject:[NSValue valueWithPointer:selector] forKey:@"selector"];

    if (object)
    {
        [mdicSelector setObject:object forKey:@"object"];
    }

    [mdicSelector setObject:[NSNumber numberWithInt:controlEvent] forKey:@"controlEvent"];

    [marrSelectors addObject:mdicSelector];
}

Note this..

[mdicSelector setObject:target forKey:@"target"];

target is your scene and it is being tossed into a dictionary in that is tossed into an array. So in theory that button now has a strong reference to your scene and your scene has a reference to that button.

To test this theory set all of your buttons to nil before calling

[self.view presentScene:theScene transition:theTransition];

And if I am correct that should break the strong reference to each other. Hopefully that helps and is the issue.

Skyler Lauren
  • 3,700
  • 3
  • 15
  • 30
  • 2
    Hi Guys, Thanks to all of you for taking the time to look into this issue with me. A SUPER BIG thanks to Skyler Lauren for pointing me in the right direction! He was indeed correct, it turns out the culprit is the AGSpriteButton class! It appears it keeps a dictionary of the selector actions and this was causing retain cycles, however setting the AGSpriteButtons to nil did not seem to work so instead try using this: [yourAgsButton removeAllTargets]; After using this my dealloc methods starting being called when scenes were being exited and BOOM my memory began dropping – SmokersCough May 18 '15 at 13:20
  • 1
    Glad I was able to help. Ran into a similar problem myself by not setting a custom delegate to weak. Days and days spent trying to figure it out. :) best of luck with your app. – Skyler Lauren May 18 '15 at 13:40
0

Based on your posted code, I suggest you add this to your MainMenu:

-(void) willMoveFromView:(SKView *)view {
    [self removeAllChildren];

    backgroundImage = nil;
    topBar = nil;
    bottomBar = nil;
    ladybirds = nil;
    title = nil;
    subtitle = nil;
    coffee = nil;
    settingsWin = nil;
    iapWin = nil;
    unlocksWin = nil;
    startButton = nil;
    continueButton = nil;
    settingsButton = nil;
    iapButton = nil;
    unlocksButton = nil;
    theParticles = nil;
}

Add this to your SettingsScene:

-(void) willMoveFromView:(SKView *)view {
    [self removeAllChildren];

    backButton = nil;
    resetButton = nil;
    resetTutorialsButton = nil;
    creditsButton = nil;
    titleLabel = nil;
    copyrightLabel = nil;
    resetGameAlert = nil;
    resetTutAlert = nil;
    theScene = nil;
    theTransition = nil;
    menuBg = nil;
}

Keep in mind that SK uses its own caching which you have no control over. This means you will see an increase in memory the first couple of times you switch scenes but it will level out at some point. I suggest you move back and forth 15 to 20 times and see what happens.

sangony
  • 11,426
  • 4
  • 34
  • 51
  • Yes but i'm not sure this will help, as the issue appears to be that the scene is not being released, doing so would automatically remove all those items as long as they aren't referenced elsewhere. But thanks for the suggestion. – SmokersCough May 13 '15 at 17:58
  • @SmokersCough - "the strong reference to the old scene is removed. If you need to keep the scene around after the transition occurs, your app needs to keep its own strong reference to the old scene." This is according to the docs. So unless you are deliberately keeping a strong reference to your old scene, it and everything else in it will get deallocated. – sangony May 13 '15 at 18:48
  • Sorry i'm a little confused, i don't keep a strong reference to the scene neither do i want to, the scene is also not registered for any notifications. But the scene is being kept in memory as i'm not seeing a dealloc nslog. Surely i shouldn't have to manually nil every object for this to work? – SmokersCough May 13 '15 at 18:53
  • @SmokersCough - Look at this http://stackoverflow.com/questions/7292119/custom-dealloc-and-arc-objective-c – sangony May 13 '15 at 18:55
  • Just to clarify, i am using ARC, the only reason why i'm talking about it calling the dealloc nslog is so i can see its actually being removed from memory which it isn't. – SmokersCough May 13 '15 at 19:07
  • @SmokersCough - are you running any SKAction methods in your scene? – sangony May 16 '15 at 12:09
  • Not in scenes that i am jumping between, i've added the scene code for both scenes above – SmokersCough May 16 '15 at 17:33
  • @SmokersCough - is that the entire code for your SKScene? – sangony May 16 '15 at 22:37
  • Its the entire code from both scenes that i am moving between, the main menu scene and the settings scene. Any ideas? – SmokersCough May 17 '15 at 18:48
  • @SmokersCough - I updated my answer earlier. Try it out and see what happens. – sangony May 17 '15 at 19:58
0

In a game I'm working on I have a method for going on to the next scene. It looks like this:

func proceedToNextLevel() {
    // Go to next level
    self.physicsWorld.removeAllJoints()
    self.enumerateChildNodesWithName("cube", usingBlock: {
        (node: SKNode!, stop: UnsafeMutablePointer <ObjCBool>) -> Void in
        // do something with node or stop
        node.removeFromParent()
    })
    player.removeFromParent()
    hud.removeFromParent()
}

Each level is a scene that inherits this method. (I have a GameScene.swift, and every level is a subclass of it)

This method in each level looks like this:

override func proceedToNextLevel() {
    super.proceedToNextLevel()
    let transition = SKTransition.revealWithDirection(SKTransitionDirection.Right, duration: 3)
    let newScene: LevelFourScene = LevelFourScene.unarchiveFromFile("LevelFourScene") as! LevelFourScene
    newScene.scaleMode = SKSceneScaleMode.AspectFit
    self.view?.presentScene(newScene)
}

Obviously this is swift and your game is in Objective C but hopefully you get the idea.

CodyMace
  • 638
  • 1
  • 8
  • 15
  • Thanks but looking at this the you're replacing the scene that is loaded as i do by using the presentScene, however my previous scene doesn't seems to be released meaning every time i return to the scene its like adding a whole new scene. – SmokersCough May 13 '15 at 17:59