呈现 SpriteKit 场景时内存泄漏

Memory leak when presenting SpriteKit scenes

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

问题是每次我呈现房屋场景时,内存中都会出现尖峰。这是预料之中的,因为我们正在加载一个新的房屋场景。但是当我退出房屋场景并 return 到邻里场景时,内存使用量并没有下降。我通过一次又一次地进出房屋来测试这一点。每次我进入一所房子时,内存使用量都会以一致的数量(~2 MB)攀升。最终,内存使用量变得非常大,游戏开始掉帧(但仅限于房屋场景,而不是邻里场景),最终变得无法播放。

为什么会这样?

在使用 SpriteKit 时,我认为最佳做法是使用 presentScene to transition the player to a substantially different "realm" of the game world (e.g. a different level), which is what I believe I am doing here. I also thought that you are not supposed to take any action to "unload" your old scene when you load and then transition to a new scene. Indeed, the SKView 文档没有任何方法可以在您呈现新场景时从旧场景中清除不需要的对象。我认为您应该信任 OS 来处理从内存中删除旧场景对象的工作。这就是为什么我只呈现新场景而不担心旧场景占用内存的原因。

我在 Instruments 中分析了该应用程序以检查内存泄漏(此时 Instruments 仍然有点让我头疼)。我确实发现了一些看起来很小的内存泄漏,但似乎没有泄漏直接导致每次呈现新房屋场景时发生的大而一致的内存峰值。

我是不是做错了什么?

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

我确实注意到 SKScene documentation has several methods related to presenting a scene: sceneDidLoad(), willMove(from:), and didMove(to:)。这让我想知道当我在不同场景之间转换时,我是否应该使用这些方法以某种方式尝试从内存中清除不需要的对象。

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

首先,您正确地呈现了 SKScene,并且您是正确的,您应该能够相信旧场景会得到清理。我的意思是您无需执行任何其他操作即可发布它。

话虽如此,您可能已经做了一些事情来创建循环引用。希望这些检查中的一些可以帮助您找到它。

我总是首先看你的播放器。如果您的场景有一个播放器 属性 而您的播放器有一个场景 属性 这可能会阻止场景重新分配。玩家抓住场景,场景抓住玩家。我怀疑这是你的情况,但值得检查。

第二个要看的地方有没有其他参考或属性那个场景的?一个常见的问题是当您创建自己的委托方法时。如果你的玩家做了一个动作,然后调用一个方法回到那个场景。玩家应该只对该场景有微弱的参考。我自己做过这个,也看到其他人在创建自定义委托或协议并保持对它的强引用而不是弱引用时这样做。

第三个要看的地方是代码中你调用 self 的任何地方。这在 运行 个 SKActions 块中很常见。您可能有一个在场景中调用方法的动作,并且您可能在该 SKAction 的那个场景中有一个 属性。由于 运行 块,动作引用了场景,场景引用了动作。所以寻找自我,看看那个对象是否是那个场景中的属性。

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