iOS 7 Sprite Kit 释放内存

Posted

技术标签:

【中文标题】iOS 7 Sprite Kit 释放内存【英文标题】:iOS 7 Sprite Kit freeing up memory 【发布时间】:2013-10-15 14:10:27 【问题描述】:

我正在为新的 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,是场景的容器 scene 是 SKScene 类的扩展,创建所有 SKSpriteNode 对象

我非常感谢您在这件事上的任何帮助。

【问题讨论】:

同样的问题,即使我调用了dismissViewControllerAnimated,我的场景仍然在运行,你解决了吗? 是的,我做到了,我无法从场景或 Sprite Kit 中对此做任何事情,我只需要从父视图中完全删除场景和包含它的视图,切断它与系统其他部分的所有联系,以便同时释放内存。 【参考方案1】:

Swift 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()

【讨论】:

【参考方案2】:

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

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;

【讨论】:

【参考方案3】:

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

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

【讨论】:

我已经这样做了,但是即使我在设置 sceneView.presentScene(nil) 后呈现下一个场景,它也会使屏幕变为空白。有什么办法可以解决这个问题?【参考方案4】:

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

-(void)dealloc

[sceneView presentScene:nil];
[sceneView release];
[super dealloc];

【讨论】:

每次回到舞台,我的表现都在下降。我从 Apple 的 SpriteKit 默认模板开始。在 Swift 中,我刚刚添加了此功能,性能似乎恢复正常,覆盖 func viewWillDisappear(animated: Bool) if let skView = self.view as? SKView skView.presentScene(nil) 【参考方案5】:

我遇到了和你@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;

为我解决了问题。

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

解决方案 2:

在我的myViewController下:

- (void)viewDidDisappear:(BOOL)animated

我打过电话:

self.ballonMGScene.parentVC = nil;

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

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

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

【讨论】:

【参考方案6】:

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

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

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

我不知道这是否有帮助。

【讨论】:

【参考方案7】:

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

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

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

【讨论】:

感谢您的快速回答!实际上,不存在泄漏(除了物理模块产生的几个 64 字节泄漏),没有包含场景或 SKView 对象的保留周期或强引用,但正常行为仍然是,如果我开始创建分配了 180MB 内存的场景,在场景使用期间增加到 230MB,在我退出后,它应该回落到非常接近 180MB 的值 - 这当然没有任何缓存。你认为 Sprite Kit 的缓存可以证明这种行为是合理的吗? 可能需要明确的测试来验证。也许 SK 保留缓存的纹理以供以后重用,并仅在内存警告时释放它们。尝试在模拟器中发送内存警告,看看内存是否会下降。 嗯,有趣的想法,但不,即使在内存警告之后它也不会下降。

以上是关于iOS 7 Sprite Kit 释放内存的主要内容,如果未能解决你的问题,请参考以下文章

iOS:ARC,不释放内存

IOS内存管理机制

iOS 7 Sprite Kit 动画速度变化

Unity3D移动端内存优化(NGUI方面)

Windows 7系统中内存快速释放的方法

读书笔记iOS-自动释放池