【发布时间】:2015-07-16 04:45:24
【问题描述】:
我的 SpriteKit 游戏目前有三个场景; Menu.m、LevelSelect.m 和 Level.m。当我启动应用程序时,内存使用量为 35MB。从主菜单切换到关卡选择场景后,它几乎保持在 35MB 左右。从关卡选择场景移动到关卡本身,它的大小最高可达 150MB。当然,在创建关卡中涉及更多的精灵和类。但是,当我通过我作为精灵拥有的覆盖菜单重新加载关卡时,每次重新加载的内存使用量继续增加约 2MB。这可能是保留周期问题吗?如果我切换回关卡选择场景然后重新进入关卡本身也是如此,内存继续爬升。我担心我的状态机的实现。我将在下面发布一些骨架代码:
LevelScene.m
-(void)didMoveToView:(SKView *)view {
...
[self initGameStateMachine];
...
}
...
//other methods for setting up the level present
...
-(void) initGameStateMachine {
LevelSceneActiveState *levelSceneActiveState = [[LevelSceneActiveState alloc] initLevelScene:self];
LevelSceneConfigureState *levelSceneConfigureState = [[LevelSceneConfigureState alloc] initLevelScene:self];
LevelSceneFailState *levelSceneFailState = [[LevelSceneFailState alloc] initLevelScene:self];
LevelSceneSuccessState *levelSceneSuccessState = [[LevelSceneSuccessState alloc] initLevelScene:self];
NSMutableDictionary *states = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
levelSceneActiveState, @"LevelSceneActiveState",
levelSceneConfigureState, @"LevelSceneConfigureState",
levelSceneFailState, @"LevelSceneFailState",
levelSceneSuccessState, @"LevelSceneSuccessState",
nil];
_gameStateMachine = [[StateMachine alloc] initWithStates:states];
[_gameStateMachine enterState:levelSceneActiveState];
}
-(void)update:(CFTimeInterval)currentTime {
//update the on screen keyboard with notes that are being played
[_gameStateMachine updateWithDeltaTime:[NSNumber numberWithDouble:currentTime]];
}
-(void) willMoveFromView:(SKView *)view {
[self removeAllChildren];
}
StateMachine.m
#import "StateMachine.h"
#import "GameState.h"
@interface StateMachine()
@property NSMutableDictionary *states;
@end
@implementation StateMachine
-(instancetype)initWithStates:(NSMutableDictionary*)states {
if (self = [super init]) {
for (id key in [states allValues]) {
if (![key conformsToProtocol:@protocol(GameState)]) {
NSLog(@"%@ does not conform to @protocol(GameState)", key);
return nil;
}
}
_states = states;
}
return self;
}
//this method will be used to start the state machine process
-(bool)enterState:(id)nextState {
if (!_currentState) {
_currentState = [_states objectForKey:[nextState className]];
[[NSNotificationCenter defaultCenter] addObserver:_currentState selector:@selector(keyPressed:) name:@"KeyPressedNotificationKey" object:nil];
return YES;
}
else if ([_currentState isValidNextState:nextState]) {
[_currentState performSelector:@selector(willLeaveState)];
_currentState = [_states objectForKey:[nextState className]];
[[NSNotificationCenter defaultCenter] addObserver:_currentState selector:@selector(keyPressed:) name:@"KeyPressedNotificationKey" object:nil];
return YES;
}
return NO;
}
-(void)updateWithDeltaTime:(NSNumber*)currentTime {
[_currentState performSelector:@selector(updateWithDeltaTime:) withObject:currentTime];
}
@end
LevelSceneActiveState //这是四种状态之一
#import "LevelSceneActiveState.h"
#import "LevelSceneConfigureState.h"
#import "LevelSceneSuccessState.h"
#import "LevelSceneFailState.h"
#import "StateMachine.h"
#import "LevelScene.h"
#import "SSBitmapFontLabelNode.h"
@interface LevelSceneActiveState()
@property LevelScene *levelScene;
@end
@implementation LevelSceneActiveState
-(instancetype)initLevelScene:(LevelScene *)levelScene {
if (self = [super init])
_levelScene = levelScene;
return self;
}
-(void)updateWithDeltaTime:(NSNumber*)currentTime {
//game variables created here
....
//state machine needs to be set here...if set in init, it does not have a value in the LevelScene yet
if (_gameStateMachine == nil)
_gameStateMachine = _levelScene.gameStateMachine;
//game logic performed here
...
//check for timer finishing
if (!_levelScene.timer.isValid) {
//success
if (_levelScene.score >= 7) {
[_gameStateMachine enterState:LevelSceneSuccessState.self];
}
else { //failure
[_gameStateMachine enterState:LevelSceneFailState.self];
}
}
}
//another class is used to trigger notifications of key presses
-(void) keyPressed:(NSNotification*)notification {
NSNumber *keyCodeObject = notification.userInfo[@"keyCode"];
NSInteger keyCode = keyCodeObject.integerValue;
if (keyCode == 53)
[self escapePressed];
}
-(void) escapePressed {
[_gameStateMachine enterState:LevelSceneConfigureState.self];
[_levelScene childNodeWithName:@"timer"].paused = YES;
}
-(bool)isValidNextState:(id)nextState {
if ([[nextState className] isEqualToString:@"LevelSceneConfigureState"])
return YES;
...
return NO;
}
//this makes sure that we're not notifying an object that may not exist
-(void) willLeaveState {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:@"KeyPressedNotificationKey"
object:nil];
}
@end
在状态之间切换时,我不希望 LevelScene 场景消失。我知道这很难诊断,尤其是当您没有坐在整个项目面前时。我可以做些什么来自我诊断?任何有用的提示/技巧都会很棒。
[更新] 我尝试了 Product->Profile->Instruments->Allocation 的东西,但我不知道如何处理它。内存表明它还在继续上升。
【问题讨论】:
标签: macos memory-leaks sprite-kit retain-cycle