是什么导致了这种内存泄漏?

What's causing this memory leak?

问题

I'm currently building an iPad game using SpriteKit. The gameplay is driven by sound provided by EZAudio. After running the Instrumentation tools to profile my app, I noticed that whenever the GameViewController is shown memory allocation jumps up. When I repeatedly show the screen (5+ times) it crashes the app. My project uses ARC.

导航

导航包含4个ViewControllers:

  1. MenuViewController:显示菜单
  2. CharacterSelectionViewController:它可以让您选择要使用的角色
  3. GameViewController:这让您可以与选定的玩家一起玩游戏
  4. ScoreViewController: 显示你在游戏中获得的分数

1 - 菜单视图控制器

从这里您可以通过 Show (e.g. Push) segue 导航至 CharacterSelectionViewController

2 - CharacterSelectionViewController

您可以通过 Show (e.g. Push) segue 导航至 GameViewController。还有一个返回到 MenuViewController 的后退按钮,代码如下:

[self.navigationController popViewControllerAnimated:YES];

3 - GameViewController

  1. 它首先显示 5 秒倒计时(使用 NSTimer
  2. 游戏从CharacterSelectionViewController
  3. 中选择的角色开始
  4. 游戏可以暂停,允许您通过手动 Show Detail (e.g. Replace) segue 退出并返回 MenuViewController
  5. 游戏结束时,将调用手动 Show (e.g. Push) segue,导航到 ScoreViewController

它的视图层次由三组视图组成——一组用于倒计时,一组用于暂停菜单,一组用于游戏。这些随后是 shown/hidden。请参阅下面的视图层次结构:

4 - ScoreViewController

这允许您退出或重新启动游戏。按下退出时,它会执行 Show Detail (e.g. Replace)MenuViewController 的转场。如果按下重新启动,它会执行 unwindCharacterSelectionViewController

回应

请提供以下方面的答案:

分配

在下面,当我循环浏览应用程序屏幕以重复显示 GameViewController 时,您可以看到分配增加。我使用 Mark Generation 来显示内存分配的增加。

泄漏

在这里您可以看到发生的内存泄漏。这是按尺码排序的。

暂时忽略泄漏;先修复世代累加。

那一代快照代表了典型快照之后剩下的东西吗?通常,您希望显示视图控制器、拍摄快照、隐藏然后显示视图控制器、拍摄快照等...尽可能多次而不会崩溃(如果不崩溃则为 10 次)。

然后查看第 3 代或第 4 代,因为这将是每代增长的最稳定代表。

如果它具有代表性,那么看起来您正在泄漏视图控制器通常分配的所有内容。最终,您正在寻找保存所有内容的对象图的 "root"。修复根仍然存在的原因,其余的可能会消失。

我写了一篇关于此的博客 post。它有点过时,但分析工作流程保持不变。

http://www.friday.com/bbum/2010/10/17/when-is-a-leak-not-a-leak-using-heapshot-analysis-to-find-undesirable-memory-growth/

您如何从各种视图控制器中解脱出来?我注意到你提到当游戏结束时你正在将另一个 VC 推入堆栈,但我认为这个 VC 链会在某个时候返回到你的初始菜单? (本质上,我想知道你是否只是在循环,因此每次玩游戏时都会向堆栈添加新的 VC。)

要创建一个 un-wind segue,只需在 destination VC(即:您的主菜单)中创建一个空方法,如下所示:

- (IBAction)unwindToMainMenu:(UIStoryboardSegue*)sender
{
    // Intentional NOP
}

(N.B.: 确保它也列在 header.)

然后,您可以像调用故事板中的任何其他 segue 一样调用它,方法是从有问题的源 object 拖动到 VC 顶部的退出选项包含故事板中的来源 object。这将为您提供可供选择的转场列表。 (您可以通过在情节提要中选择源 object 来验证转场是否已正确设置 - 连接检查器应在触发转场部分中列出展开转场。)