呈现SpriteKit场景时内存泄漏

我有一个SKScene ,这是我的2D游戏的世界。 这就像玩家可以探索的社区。 附近有很多房子。 玩家可以自由进出房屋。 当玩家进入房屋时,我会调用skView.presentScene(newHouse)将房屋内部呈现为新场景。 当玩家离开房子时,我打电话给skView.presentScene(overworld)再次呈现邻居。 玩家在探索邻居时可能会多次进出房屋。 因此,邻居场景和房屋场景的新实例被多次呈现。

问题是每次我呈现一个房子场景时,记忆中都会出现尖峰。 这是预期的,因为我们正在装载一个新的房子场景。 但是当我退出房屋场景并返回邻居场景时,内存使用量不会下降。 我通过进入然后一遍又一遍地退出房屋来测试这个。 每次进入房屋时,内存使用量都会增加(约2 MB)。 最终,内存使用量变得非常大,游戏开始丢帧(但仅限于室内场景,而不是邻居场景)并最终变得无法播放。

为什么会这样?

在使用SpriteKit时,我认为最好的做法是使用presentScene将玩家转换为游戏世界的一个截然不同的“领域”(例如不同的级别),这就是我相信我在这里做的事情。 我还认为当你加载然后转换到新场景时,你不应该采取任何行动来“卸载”你的旧场景。 实际上,当您呈现新场景时, SKView文档没有任何方法可以清除旧场景中不需要的对象。 我认为你应该相信操作系统来处理从内存中删除旧场景的对象的工作。 这就是为什么我只是呈现新场景而不用担心占用内存的旧场景。

我在仪器中分析了应用程序来检查内存泄漏(仪器在这一点上仍然有点过头)。 我确实发现了一些看起来非常小的内存泄漏,但是没有任何泄漏似乎直接导致每次我呈现一个新的房子场景时发生的大而一致的内存峰值。

难道我做错了什么?

我相信我正在采取正确的方法,提出新的场景,并让操作系统处理清理旧场景的工作。 但也许我在我的应用程序设计中犯了一个错误,导致了这个内存问题。 当我呈现一个新的房子场景时,我将一些信息从邻居场景传递到新房子场景。 这些信息与房屋的外观(颜色,纹理,内容等)有关,这是必要的,因为房屋是程序生成的:每个房屋都是独一无二的。 当我回到邻居场景时,我将一些信息从房屋场景传递到邻居场景。 但也许我在场景之间传递信息的方式无意中导致对象被保留在内存中。 如果是这样,我该怎么做以确保不会发生这种情况? 当我呈现一个新场景时,是否应该运行一些代码来清除内存中不需要的对象?

我注意到SKScene文档有几个与呈现场景相关的方法: sceneDidLoad()willMove(from:)didMove(to:) 。 这让我想知道当我在不同的场景之间转换时,我是否应该以某种方式使用这些方法来尝试从内存中清除不需要的对象。

可能是我的应用程序架构很糟糕(它已经给我带来了其他问题)。 如果是这样,那么解决方案就是从检修我的应用程序架构开始改进它。 所以基本上,我试图确定我的坏应用程序架构是否导致此内存膨胀问题,或者原因是否与SpriteKit以及场景呈现方式有关。

首先,你正确地呈现SKScene,你是对的,你应该能够相信旧的场景会被清理干净。 我的意思是你没有别的办法让它发布。

现在有了这样说,你可能已经做了一些事情来创建循环引用。 希望这些检查中的一些可以帮助您追踪它。

我一直看的第一个地方是你的球员。 如果您的场景具有玩家属性且您的玩家具有场景属性,则可能会阻止场景解除分配。 球员坚持现场,场景坚持球员。 我怀疑这是你的情况,但值得检查。

第二个要看的是那个场景还有什么参考或属性? 一个常见问题是当您创建自己的委托方法时。 如果您的播放器执行某个操作,然后将方法调用回该场景。 玩家应该只对该场景有弱引用。 我自己做了这个,并看到其他人在他们创建自定义委托或协议的地方做,并保持强烈的参考,而不是弱。

第三个要查看的地方是代码中您称之为self的位置。 这在SKActions的运行块中很常见。 您可能有一个动作调用场景中的方法,并且您可能在该SKAction的场景上拥有属性。 该动作由于运行块而引用场景,并且场景具有对动作的引用。 因此,寻找自我并​​查看该对象是否是该场景的属性。

希望这可以帮助您追踪它。 我知道跟踪这样的泄漏会令人沮丧,这些都是我过去看到的常见问题。