Desasignar SKScene después de la transición a otro SKScene en SpriteKit

Tengo un controller de vista que tiene tres escenas como niños.

Cuando hago la transición de una a otra, la antigua escena no se desasigna.

Quiero que se desasigne como si nunca estuviera allí.

Ejemplo:

La primera vez que carga la aplicación, solo se ve 1 skscene (digamos que ocupa 100 MB de memory), luego paso a otro (100 MB más) y luego a la tercera (300 MB de memory).

Terminaría con memory de 300 MB y quiero tener 100 en todo momento.

¿Cómo puedo conseguir esto?

My view controller: // // ViewController.m // Paddle Jumper // // Created by Chance Daniel on 1/18/14. // Copyright (c) 2014 Max Hudson. All rights reserved. // #import "Flurry.h" #import "ViewController.h" #import "startViewController.h" @implementation ViewController{ BOOL sceneSetUp; } - (void)viewWillLayoutSubviews { if(!sceneSetUp){ [super viewWillLayoutSubviews]; // Configure the view. SKView * skView = (SKView *)self.view; //skView.showsFPS = YES; //skView.showsNodeCount = YES; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; if([[defaults objectForKey:@"firstTime"] intValue] != 1){ [defaults setObject:[NSNumber numberWithInt:1] forKey:@"firstTime"]; [defaults setObject:@"ggr" forKey:@"skinSelected"]; [defaults setObject:[NSNumber numberWithInt:2] forKey:@"ggrOwned"]; [defaults setObject:[NSNumber numberWithInt:5000] forKey:@"gona"]; [defaults setObject:[NSNumber numberWithInt:1500] forKey:@"points"]; [defaults setObject:[NSNumber numberWithInt:7] forKey:@"livesLeftValue"]; [defaults setObject:[NSNumber numberWithInt:3] forKey:@"shieldsLeftValue"]; [defaults setObject:[NSNumber numberWithInt:2] forKey:@"lvlTwoLeftValue"]; [defaults setObject:[NSNumber numberWithInt:0] forKey:@"lvlThreeLeftValue"]; } if(![defaults objectForKey:@"tut_game1"]){ [defaults setObject:[NSNumber numberWithInt:1] forKey:@"tut_game1"]; [defaults setObject:[NSNumber numberWithInt:1] forKey:@"tut_store"]; [defaults setObject:[NSNumber numberWithInt:1] forKey:@"tut_daily"]; } [defaults synchronize]; // Create and configure the scene. SKScene * startScene = [StartViewController sceneWithSize:skView.bounds.size]; startScene.scaleMode = SKSceneScaleModeAspectFill; // Present the scene. [skView presentScene:startScene]; //[skView presentScene:scene]; sceneSetUp = YES; } } -(void) switchScene{ } - (BOOL)shouldAutorotate { return YES; } - (NSUInteger)supportedInterfaceOrientations { if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { return UIInterfaceOrientationMaskAllButUpsideDown; } else { return UIInterfaceOrientationMaskAll; } } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use. } @end An SKScene that won't release: // // SettingsSKScene.m // Paddle Jumper // // Created by Max Hudson on 3/15/14. // Copyright (c) 2014 Max Hudson. All rights reserved. // #import "SettingsSKScene.h" #import "gameViewController.h" #import "StoreScene.h" @interface SettingsSKScene (){ SKSpriteNode *bg; SKSpriteNode *masterOverlay; SKSpriteNode *wbox; SKSpriteNode *gamecenter; SKSpriteNode *max; SKSpriteNode *chance; SKSpriteNode *bryce; SKSpriteNode *home; SKSpriteNode *play; SKSpriteNode *chance_link; SKSpriteNode *max_link; SKSpriteNode *bryce_link; SKSpriteNode *fbButton; SKSpriteNode *fbToggleYes; SKSpriteNode *fbToggleNo; SKLabelNode *story1; SKLabelNode *story2; SKLabelNode *story3; SKLabelNode *chance_name; SKLabelNode *max_name; SKLabelNode *bryce_name; SKLabelNode *chance_role; SKLabelNode *max_role; SKLabelNode *bryce_role; SKLabelNode *chance_handle; SKLabelNode *max_handle; SKLabelNode *bryce_handle; SKTexture *bg_texture; SKTexture *gamecenter_texture; SKTexture *wbox_texture; SKTexture *max_texture; SKTexture *chance_texture; SKTexture *bryce_texture; SKTexture *home_texture; SKTexture *play_texture; SKTexture *fb_texture; SKTexture *toggle_yes_texture; SKTexture *toggle_no_texture; } @end @implementation SettingsSKScene -(id) initWithSize:(CGSize)size{ if(self = [super initWithSize:size]){ masterOverlay = [SKSpriteNode spriteNodeWithColor:[SKColor blackColor] size:self.frame.size]; masterOverlay.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2); [self addChild:masterOverlay]; bg_texture = [SKTexture textureWithImageNamed:@"settings_bg"]; wbox_texture = [SKTexture textureWithImageNamed:@"white_rect_bg"]; gamecenter_texture = [SKTexture textureWithImageNamed:@"gc"]; max_texture = [SKTexture textureWithImageNamed:@"max"]; chance_texture = [SKTexture textureWithImageNamed:@"chance"]; bryce_texture = [SKTexture textureWithImageNamed:@"bryce"]; home_texture = [SKTexture textureWithImageNamed:@"home_light"]; play_texture = [SKTexture textureWithImageNamed:@"play_light"]; fb_texture = [SKTexture textureWithImageNamed:@"fb_light"]; toggle_yes_texture = [SKTexture textureWithImageNamed:@"toggle_yes"]; toggle_no_texture = [SKTexture textureWithImageNamed:@"toggle_no"]; NSArray *to_preload = @[bg_texture, wbox_texture, gamecenter_texture, max_texture, chance_texture, bryce_texture, home_texture, play_texture, fb_texture, toggle_yes_texture, toggle_no_texture]; [SKTexture preloadTextures:to_preload withCompletionHandler:^{ [self fadeRemove:masterOverlay]; [self initialize]; }]; } return self; } -(void) fadeRemove: (SKNode *) node{ double duration = arc4random() % 10; duration *= 0.05; SKAction *fadeOut = [SKAction fadeOutWithDuration:0.1+duration]; SKAction *remove = [SKAction runBlock:^{ [node removeFromParent]; }]; [node runAction:[SKAction sequence:@[fadeOut, remove]]]; } -(void) initialize{ bg = [SKSpriteNode spriteNodeWithTexture:bg_texture]; bg.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2); bg.zPosition = 0; wbox = [SKSpriteNode spriteNodeWithTexture:wbox_texture]; wbox.position = CGPointMake(self.frame.size.width/2, 70); wbox.alpha = 0.8; wbox.zPosition = 2; gamecenter = [SKSpriteNode spriteNodeWithTexture:gamecenter_texture]; gamecenter.position = CGPointMake(self.frame.size.width/2 + 100, self.frame.size.height/2-2); gamecenter.name = @"gc"; gamecenter.zPosition = 2; fbButton = [SKSpriteNode spriteNodeWithTexture:fb_texture]; fbButton.size = CGSizeMake(36, 36); fbButton.position = CGPointMake(self.frame.size.width/2 - 115, self.frame.size.height/2-2); fbButton.name = @"fb"; fbButton.zPosition = 2; fbToggleNo = [SKSpriteNode spriteNodeWithTexture:toggle_no_texture]; fbToggleNo.position = CGPointMake(self.frame.size.width/2 - 50, self.frame.size.height/2-2); fbToggleNo.size = CGSizeMake(fbToggleNo.size.width/3, fbToggleNo.size.height/3); fbToggleNo.name = @"fbno"; fbToggleNo.zPosition = 2; fbToggleYes = [SKSpriteNode spriteNodeWithTexture:toggle_yes_texture]; fbToggleYes.position = CGPointMake(self.frame.size.width/2 - 70, self.frame.size.height/2-2); fbToggleYes.name = @"fbyes"; fbToggleYes.zPosition = 2; int hpBuffer = 40; int hpZ = 2; home = [SKSpriteNode spriteNodeWithTexture:home_texture]; home.position = CGPointMake(hpBuffer, self.frame.size.height - hpBuffer); home.zPosition = hpZ; home.name = @"home"; play = [SKSpriteNode spriteNodeWithTexture:play_texture]; play.position = CGPointMake(self.frame.size.width - hpBuffer, self.frame.size.height - hpBuffer); play.zPosition = hpZ; play.name = @"play"; [self addChild:bg]; [self addChild:wbox]; [self addChild:gamecenter]; [self addChild:home]; [self addChild:play]; [self addChild:fbButton]; [self addChild:fbToggleNo]; [self addCnetworkingits]; [self addStory]; } -(void) addCnetworkingits{ /* images */ int cmbZ = wbox.zPosition + 1; int cmbY = wbox.position.y; int cmbXUnit = 132; int cmbX = self.frame.size.width/2 - (3*cmbXUnit)/2 + 20; int cmbWidth = 40; chance = [SKSpriteNode spriteNodeWithTexture:chance_texture]; max = [SKSpriteNode spriteNodeWithTexture:max_texture]; bryce = [SKSpriteNode spriteNodeWithTexture:bryce_texture]; chance.zPosition = cmbZ; max.zPosition = cmbZ; bryce.zPosition = cmbZ; chance.position = CGPointMake(cmbX+cmbXUnit*0, cmbY+3); max.position = CGPointMake(cmbX+cmbXUnit*1 + 10, cmbY); bryce.position = CGPointMake(cmbX+cmbXUnit*2 + 10, cmbY+5); chance.size = CGSizeMake(cmbWidth, (chance.size.height/chance.size.width)*cmbWidth); max.size = CGSizeMake(cmbWidth, (max.size.height/max.size.width)*cmbWidth); bryce.size = CGSizeMake(cmbWidth, (bryce.size.height/bryce.size.width)*cmbWidth); [self addChild:chance]; [self addChild:max]; [self addChild:bryce]; /* names */ int cmb_nameXUnit = 30; int cmb_nameY = wbox.position.y - 10; int cmb_nameFontSize = 18; chance_name = [SKLabelNode labelNodeWithFontNamed:@"BebasNeue"]; max_name = [SKLabelNode labelNodeWithFontNamed:@"BebasNeue"]; bryce_name = [SKLabelNode labelNodeWithFontNamed:@"BebasNeue"]; chance_name.text = @"Chance Daniel"; max_name.text = @"Max Hudson"; bryce_name.text = @"Bryce Daniel"; chance_name.fontColor = [SKColor blackColor]; max_name.fontColor = [SKColor blackColor]; bryce_name.fontColor = [SKColor blackColor]; chance_name.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeLeft; max_name.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeLeft; bryce_name.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeLeft; chance_name.position = CGPointMake(chance.position.x + cmb_nameXUnit, cmb_nameY); max_name.position = CGPointMake(max.position.x + cmb_nameXUnit, cmb_nameY); bryce_name.position = CGPointMake(bryce.position.x + cmb_nameXUnit, cmb_nameY); chance_name.fontSize = cmb_nameFontSize; max_name.fontSize = cmb_nameFontSize; bryce_name.fontSize = cmb_nameFontSize; chance_name.zPosition = cmbZ; max_name.zPosition = cmbZ; bryce_name.zPosition = cmbZ; [self addChild:chance_name]; [self addChild:max_name]; [self addChild:bryce_name]; /* roles */ int cmb_roleY = wbox.position.y - 25; int cmb_roleFontSize = 11; chance_role = [SKLabelNode labelNodeWithFontNamed:@"BebasNeue"]; max_role = [SKLabelNode labelNodeWithFontNamed:@"BebasNeue"]; bryce_role = [SKLabelNode labelNodeWithFontNamed:@"BebasNeue"]; chance_role.text = @"Programmer"; max_role.text = @"Programmer"; bryce_role.text = @"Graphic Designer"; chance_role.fontColor = [SKColor darkGrayColor]; max_role.fontColor = [SKColor darkGrayColor]; bryce_role.fontColor = [SKColor darkGrayColor]; chance_role.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeLeft; max_role.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeLeft; bryce_role.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeLeft; chance_role.position = CGPointMake(chance.position.x + cmb_nameXUnit + 19, cmb_roleY); max_role.position = CGPointMake(max.position.x + cmb_nameXUnit + 12, cmb_roleY); bryce_role.position = CGPointMake(bryce.position.x + cmb_nameXUnit + 6, cmb_roleY); chance_role.fontSize = cmb_roleFontSize; max_role.fontSize = cmb_roleFontSize; bryce_role.fontSize = cmb_roleFontSize; chance_role.zPosition = cmbZ; max_role.zPosition = cmbZ; bryce_role.zPosition = cmbZ; [self addChild:chance_role]; [self addChild:max_role]; [self addChild:bryce_role]; /* twitter handles */ int cmb_handY = wbox.position.y - 40; int cmb_handFontSize = 10; chance_handle = [SKLabelNode labelNodeWithFontNamed:@"BebasNeue"]; max_handle = [SKLabelNode labelNodeWithFontNamed:@"BebasNeue"]; bryce_handle = [SKLabelNode labelNodeWithFontNamed:@"BebasNeue"]; chance_handle.text = @"@ChanceOfThat"; max_handle.text = @"@max_hud"; bryce_handle.text = @"@BryceOfLife"; chance_handle.fontColor = [SKColor darkGrayColor]; max_handle.fontColor = [SKColor darkGrayColor]; bryce_handle.fontColor = [SKColor darkGrayColor]; chance_handle.position = CGPointMake(chance.position.x, cmb_handY); max_handle.position = CGPointMake(max.position.x, cmb_handY); bryce_handle.position = CGPointMake(bryce.position.x, cmb_handY); chance_handle.fontSize = cmb_handFontSize; max_handle.fontSize = cmb_handFontSize; bryce_handle.fontSize = cmb_handFontSize; chance_handle.zPosition = cmbZ; max_handle.zPosition = cmbZ; bryce_handle.zPosition = cmbZ; [self addChild:chance_handle]; [self addChild:max_handle]; [self addChild:bryce_handle]; /* touchzones */ CGSize cmdL_size = CGSizeMake(120, 70); SKColor *cmdL_color = [SKColor clearColor]; int cmdLZ = 3; int cmdLX = cmbX + 40; chance_link = [SKSpriteNode spriteNodeWithColor:cmdL_color size:cmdL_size]; max_link = [SKSpriteNode spriteNodeWithColor:cmdL_color size:cmdL_size]; bryce_link = [SKSpriteNode spriteNodeWithColor:cmdL_color size:cmdL_size]; chance_link.position = CGPointMake(cmdLX+cmbXUnit*0, cmbY); max_link.position = CGPointMake(cmdLX+cmbXUnit*1 + 10, cmbY); bryce_link.position = CGPointMake(cmdLX+cmbXUnit*2 + 10, cmbY); chance_link.zPosition = cmdLZ; max_link.zPosition = cmdLZ; bryce_link.zPosition = cmdLZ; chance_link.name = @"c_handle"; max_link.name = @"m_handle"; bryce_link.name = @"b_handle"; [self addChild:chance_link]; [self addChild:max_link]; [self addChild:bryce_link]; } -(void) addStory{ int stX = self.frame.size.width/2; int stZ = 2; SKColor *stColor = [SKColor whiteColor]; story1 = [SKLabelNode labelNodeWithFontNamed:@"BebasNeue"]; story2 = [SKLabelNode labelNodeWithFontNamed:@"BebasNeue"]; story3 = [SKLabelNode labelNodeWithFontNamed:@"BebasNeue"]; story1.text = @"Gon"; story2.text = @"Help Gon-Gon and his friends get"; story3.text = @"Back to the time they came from!"; story1.fontColor = stColor; story2.fontColor = stColor; story3.fontColor = stColor; story1.zPosition = stZ; story2.zPosition = stZ; story3.zPosition = stZ; story1.position = CGPointMake(stX, self.frame.size.height - 55); story1.fontSize = 50; story2.position = CGPointMake(stX, self.frame.size.height - 95); story2.fontSize = 30; story3.position = CGPointMake(stX, self.frame.size.height - 120); story3.fontSize = 20; [self addChild:story1]; [self addChild:story2]; [self addChild:story3]; } -(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch *touchUI = [touches anyObject]; CGPoint touchPoint = [touchUI locationInNode:self]; SKNode *touchNode = [self nodeAtPoint:touchPoint]; if([touchNode.name isEqualToString:@"home"]){ StartViewController *svc = [[StartViewController alloc] initWithSize:self.size]; SKTransition *fade = [SKTransition fadeWithColor :[SKColor blackColor] duration:0.4]; [self.view presentScene:svc transition:fade]; } if([touchNode.name isEqualToString:@"play"]){ gameViewController *gvc = [[gameViewController alloc] initWithSize:self.size]; SKTransition *fade = [SKTransition fadeWithColor :[SKColor blackColor] duration:0.4]; [self.view presentScene:gvc transition:fade]; } if([touchNode.name isEqualToString:@"gc"]){ NSDictionary * dict = [[NSDictionary alloc]initWithObjectsAndKeys:[NSNumber numberWithBool:1], @"showGC", nil]; [[NSNotificationCenter defaultCenter]postNotificationName:@"kNotificationUpdateBannerView" object:self userInfo:dict]; } if([touchNode.name isEqualToString:@"c_handle"]){ NSURL *urlApp = [NSURL URLWithString: [NSString stringWithFormat:@"%@", @"twitter:///user?screen_name=ChanceOfThat"]]; int worked = [[UIApplication shanetworkingApplication] openURL:urlApp]; if(!worked){ NSURL *urlApp = [NSURL URLWithString: [NSString stringWithFormat:@"%@", @"https://twitter.com/#!/ChanceOfThat"]]; [[UIApplication shanetworkingApplication] openURL:urlApp]; } } if([touchNode.name isEqualToString:@"m_handle"]){ NSURL *urlApp = [NSURL URLWithString: [NSString stringWithFormat:@"%@", @"twitter:///user?screen_name=max_hud"]]; int worked = [[UIApplication shanetworkingApplication] openURL:urlApp]; if(!worked){ NSURL *urlApp = [NSURL URLWithString: [NSString stringWithFormat:@"%@", @"https://twitter.com/#!/max_hud"]]; [[UIApplication shanetworkingApplication] openURL:urlApp]; } } if([touchNode.name isEqualToString:@"b_handle"]){ NSURL *urlApp = [NSURL URLWithString: [NSString stringWithFormat:@"%@", @"twitter:///user?screen_name=BryceOfLife"]]; int worked = [[UIApplication shanetworkingApplication] openURL:urlApp]; if(!worked){ NSURL *urlApp = [NSURL URLWithString: [NSString stringWithFormat:@"%@", @"https://twitter.com/#!/BryceOfLife"]]; [[UIApplication shanetworkingApplication] openURL:urlApp]; } } } @end 

Un problema similar se enfrentó la persona que hizo esta pregunta .

Cuando se le preguntó si fue capaz de resolverlo, dijeron:

Sí, lo hice, no había nada que pudiera hacer al respecto desde la escena o Sprite Kit para ese asunto, simplemente necesitaba eliminar la escena y la vista que lo contiene por completo de la vista principal, cortar todos sus enlaces al otro partes del sistema, para que la memory se desasigne también.

Debería usar vistas separadas para cada escena y transición entre estas vistas. Puede seguir estos pasos para que se vea natural:

1 – En el punto en el que desea realizar la transición de una escena a la otra, tome una instantánea de la escena con el siguiente código:

 UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, scale); [self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES]; UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); 

Luego, agregue esta image como una subvista del SKView de la escena actual y elimine la escena.

2 – Inicialice la nueva escena en otra vista y haga una transición entre las dos vistas usando la animation UIView.

3 – Eliminar la primera vista de su supervisión.

Estoy empezando con SpriteKit en Swift y tuve el mismo problema: Mi escena "intro" estaba reproduciendo algo de música que quería detener después de pasar a la escena del menu principal, y pensé que Deinit sería un buen lugar para poner la música Llamada de AVAudioPlayer.stop, pero nunca se llamó al desinicializador.

Después de mirar alnetworkingedor, supe que puede ser debido a algunas fuertes references a la escena, así que cambié

 let s = StartupScene(size: skView.bounds.size) skView.presentScene(s) 

a

 skView.presentScene(StartupScene(size: skView.bounds.size)) 

en mi GameViewController: UIViewController, y

 let s = MainMenuScene(size: self.size) let t = SKTransition.crossFadeWithDuration(1) self.scene.view.presentScene(s, transition: t) 

a

 self.scene.view.presentScene(MainMenuScene(size: self.size), transition: SKTransition.crossFadeWithDuration(1)) 

En la escena que quería desasignar, ¡y funcionó! Una vez completada la transición, se llamó al método deinit.

Es de esperar que alguien más experimentado pueda editar esta respuesta para explicar mejor qué ocurrió exactamente aquí.

EDIT: Mi idea anterior sobre un NSTimer fue irrelevante

Para asegurarte de que este es un problema aislado en esta escena, anula los methods dealloc de todas las escenas que puedas tener (incluida esta) como esta:

  -(void)dealloc { NSLog(@"Dealloc <scene name>"); } 

Mire sus otras transiciones de escena, vea si desasignan apropiadamente. Encuentra las diferencias entre estas escenas. Esto lo ayudará a ver si se trata de un problema aislado o un problema mayor. Una vez que haya solucionado el problema, asegúrese de comentar o eliminar el método dealloc ya que está reemplazando al que realmente desasigna la memory. Espero que esto ayude!

No hay nada malo con SKScene o SKView, siempre que pueda ver. Asegúrese de que la instancia de la escena no sea una reference fuerte en ningún otro lugar, especialmente dentro de un bloque. Los bloques son altamente probables para ser ignorados.

Más información acerca de la reference débil dentro de un bloque: https://stackoverflow.com/a/17105368/571489

Por lo que veo, tienes un bloque que hace reference a la instancia de la escena:

 [SKTexture preloadTextures:to_preload withCompletionHandler:^{ [self fadeRemove:masterOverlay]; [self initialize]; }];