20

我正在为新的 iOS 7 和 Sprite Kit 构建一个 iOS 游戏,使用发射器节点和物理来增强游戏性。在开发应用程序时,我遇到了一个严重的问题:您创建了场景、节点、效果,但是当您完成并需要返回主屏幕时,您如何释放这些资源分配的所有内存?

理想情况下,ARC 应该释放所有内容,并且应用程序应该恢复到创建场景之前的内存消耗水平,但这不是发生的情况。

我添加了以下代码,作为视图的 dealloc 方法,它绘制场景并负责在关闭(删除)时删除所有内容:

- (void) dealloc
{
    if (scene != nil)
    {
        [scene setPaused:YES];

        [scene removeAllActions];
        [scene removeAllChildren];

        scene = nil;

        [((SKView *)sceneView) presentScene:nil];

        sceneView = nil;
    }
}
  • sceneView 是一个 UIView,它是场景的容器
  • 场景是 SKScene 类的扩展,创建所有 SKSpriteNode 对象

我非常感谢您在此问题上的任何帮助。

4

7 回答 7

19

我在使用 Sprite Kit 时遇到了很多内存问题,我使用技术支持票来获取信息,这可能与这里有关。我在问是否启动一个新的 SKScene 会完全释放前一个使用的所有内存。我发现了这个:

+textureWithImageNamed: 分配的底层内存在切换到新的 SKScene 时可能会或可能不会(通常不会)释放。你不能依赖它。iOS 在它认为合适时释放由 +textureWithImageNamed: 或 +imageNamed: 缓存的内存,例如当它检测到内存不足的情况时。

如果您希望在完成纹理后立即释放内存,则必须避免使用 +textureWithImageNamed:/+imageNamed:。创建 SKTextures 的另一种方法是:首先使用 +imageWithContentsOfFile: 创建 UIImages,然后通过调用 SKTexture/+textureWithImage:(UIImage*) 从生成的 UIImage 对象创建 SKTextures。

我不知道这是否有帮助。

于 2014-02-04T02:19:53.493 回答
10

所有这些代码都是多余的。假设您的代码中没有内存泄漏或保留周期,一旦您释放 Sprite Kit 视图,所有内容都将从内存中清除。

Sprite Kit 在底层使用了缓存机制,但我们无法控制它,如果它被正确实现(假设是安全的),我们也不需要控制它。

如果这不是您在 Instruments 中看到的,请检查保留周期、泄漏。验证是否调用了场景和视图的 dealloc。确保没有对视图、场景或其他节点的强引用保留在其他对象中(尤其是单例和全局变量)。

于 2013-10-08T16:52:48.373 回答
9

经过几天的斗争,关键实际上是:[sceneView presentScene:nil]; 或者对于 Swift:sceneView.presentScene(nil)

这可以在 viewDidDisappear 中完成。没有这个,即使在被解雇后,你的观点也会死死抓住场景,并继续咀嚼记忆。

于 2015-03-12T18:42:46.647 回答
5

斯威夫特 3

以我个人的经验,我在 Xcode 工具的帮助下解决了问题,首先是使用活动监视器,它立即向我展示了巨大的内存增加,而不是分配和泄漏。

但也有一个有用的方法,调试控制台

deinit {
       print("\n THE SCENE \(type(of:self)) WAS REMOVED FROM MEMORY (DEINIT) \n")
}

deinit这是查看是否每次要删除场景时调用的另一个帮助。

你从来没有对你的类sceneparent在你的类中有强引用,如果你有任何人,你必须将它转换为弱,例如:

weak var parentScene:SKScene?

协议也是一样的,你可以像这个例子一样使用属性将它声明为弱class

protocol ResumeBtnSelectorDelegate: class {
    func didPressResumeBtn(resumeBtn:SKSpriteNode)
}

weak var resumeBtnDelegate:ResumeBtnSelectorDelegate?

ARC 完成了我们需要的所有工作,但是,如果您认为您忘记正确编写某些属性(初始化、块..),我在调试阶段也使用了一些类似的功能:

func cleanScene() {
    if let s = self.view?.scene {
        NotificationCenter.default.removeObserver(self)
        self.children
            .forEach {
                $0.removeAllActions()
                $0.removeAllChildren()
                $0.removeFromParent()
        }
        s.removeAllActions()
        s.removeAllChildren()
        s.removeFromParent()
    }
}

override func willMove(from view: SKView) {
    cleanScene()
    self.removeAllActions()
    self.removeAllChildren()
}
于 2016-09-03T11:32:20.397 回答
1

我遇到了像你@user2857148 类似的问题。我会向VC介绍:

[self presentViewController:myViewController animated:YES completion:nil];

@implementation myViewController我有:

- (void)viewDidLayoutSubviews
{
    // Configure the view.
    SKView * skView = (SKView *)self.view;
    skView.showsFPS = YES;
    skView.showsNodeCount = YES;
    self.ballonMGScene = [[MBDBallonMiniGame alloc] initWithSize:skView.bounds.size andBallonImageNames:self.ballonObjectsArray];
    self.ballonMGScene.parentVC = self;
    self.ballonMGScene.scaleMode = SKSceneScaleModeAspectFill;
    self.ballonMGScene.physicsWorld.gravity = CGVectorMake(0, 0);
    // Present the scene.
    [skView presentScene:self.ballonMGScene];
} 

问题出在:

self.ballonMGScene.parentVC = self;

因为在:

@interface MBDBallonMiniGame : SKScene <SKPhysicsContactDelegate>

parentVC 被声明为 strong :

@property (nonatomic,strong) WBMMiniGameVCTemplate *parentVC;

解决方案1:

并将其更改为:

@property (nonatomic,weak) WBMMiniGameVCTemplate *parentVC;

为我解决了这个问题。

说明: 对作为 a 的 parentVC ( myViewController)的引用UIViewController已存储在某处。由于这个 VC 对 SKScene 有很强的引用,因此它与它一起存储。我什至有来自这个 SKScene 的控制台输出,就像它仍然处于活动状态一样。关于为什么会发生这种情况,我最好的问题是我有最有力的指示。

解决方案2:

在我的myViewController下面:

- (void)viewDidDisappear:(BOOL)animated

我打了电话 :

self.ballonMGScene.parentVC = nil;

在离开当前的 VC ( myViewController) 时,我将指针设置为 nil,删除内存和所有的东西。

这两种解决方案对我有用。我用调试器对其进行了测试。内存消耗正确地上升和下降。

希望这有助于理解问题和解决方案。

于 2014-04-29T11:56:42.253 回答
0

在删除场景之前尝试保留场景视图。

-(void)dealloc
{
[sceneView presentScene:nil];
[sceneView release];
[super dealloc];
}
于 2014-10-10T16:13:32.060 回答
0

它采取了一些步骤,但我完全解决了我的问题:

1) 在我的 ViewControl 中,我创建了一个强制销毁所有子项的方法:

-(void)destroyAllSub:(SKNode*)node
{
    if(node == nil) return;
    if(![node isKindOfClass:[SKNode class]]) return;

    [node removeAllActions];
    for (SKNode *subNode in node.children) {
        [self destroyAllSub:subNode];
    }
    [node removeAllChildren];
}

2)由于我在我的场景中创建了一个强大的协议并在我的 ViewControl 中引用它并且我的场景也很强大,所以我销毁了所有引用,如下所示:

[self.mainScene.view presentScene:nil]; //mainScene: the name of the Scene pointer
self.mainScene.myProt = nil; //myProt: The name of the strong protocol

@autoreleasepool {
    [self destroyAllSub:self.mainScene];
    self.mainScene = nil;
}
于 2015-11-16T01:28:52.717 回答