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
的另一个帮助。
您的课程中没有对scene
或parent
的强引用,如果您有任何人,您必须将其转换为弱引用,例如:
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 释放内存的主要内容,如果未能解决你的问题,请参考以下文章