34

是否有工具或方法可以在我的 SWIFT 代码中定位强引用循环?

强引用循环是当两个类实例在没有适当安全措施(weak/ unowned)的情况下相互引用时,因此一旦我创建的所有变量停止引用这些对象,垃圾收集器就无法处理它们。

4

5 回答 5

40

在 Swift 中查找强引用循环的方法与在 Objective-C 中相同。

您可以从 Xcode 运行应用程序,充分锻炼应用程序以显示循环,然后点击“调试内存图”按钮 ( 调试内存图)。然后您可以在左侧面板中选择一个未释放的对象,它会显示内存图,通常可以清楚地说明强引用循环:

调试内存图

有时内存周期并不那么明显,但您至少可以看到哪个对象保持对相关对象的强引用。如有必要,您可以向后跟踪并确定是什么保持了对它的强引用,依此类推。

有时知道什么样的对象保持强引用是不够的,你真的想知道强引用是在代码中的什么位置建立的。“malloc stack”选项,如https://stackoverflow.com/a/30993476/1271826所示,可用于识别建立此强引用时调用堆栈是什么(通常让您识别精确的代码行建立这些强有力的参考资料的地方)。有关详细信息,请参阅 WWDC 2016 视频Visual Debugging with Xcode

您还可以使用 Instruments 来识别泄漏的对象。只需使用 Allocations 工具通过 Instruments 运行应用程序,反复(不仅仅是一次或两次)将应用程序返回到某个稳态条件,如果内存继续增加,那么您可能有一个强大的参考周期。您可以使用分配工具来确定哪些类型的对象没有被释放,使用“记录引用计数”功能来准确确定这些强引用的建立位置等。

有关识别和解决内存问题的介绍,请参阅 WWDC 2013 视频修复内存问题和 WWDC 2012 视频iOS 应用性能:内存。那里提出的基本技术今天仍然适用(尽管 Instruments 工具的 UI 发生了一些变化……如果您想了解稍微改变的 UI,请参阅 WWDC 2014 视频使用 Instruments 改进您的应用程序)。

顺便说一句,“垃圾收集”指的是一个非常不同的内存系统,在这里并不适用。

于 2015-08-28T02:09:53.383 回答
29

您可以将 deinit 函数添加到您的类中,这些函数将在您的对象被释放时被调用。

如果在您的应用程序运行时没有调用 deinit,您可以按下 Debug Memory Graph 按钮(在下方圈出)并检查哪些内容对哪些内容有引用。

调试内存图表按钮

使用中间窗格顶部的下拉菜单在类和类实例之间切换。

如果某些东西被一遍又一遍地分配而没有被释放,你应该看到多个实例,并且你应该能够通过方向图看到它的一个孩子是否持有对其父母的强引用。

于 2017-03-30T19:06:12.997 回答
1

使用仪器检查泄漏和记忆丢失。在 Instruments 上的 Allocations 工具中使用标记生成(Heapshot)。

有关如何使用 Heapshot 查找内存占用,请参阅:bbum 博客

基本上,该方法是运行 Instruments allocate 工具,获取一个 heapshot,运行代码的迭代并获取另一个 heapshot,重复 3 或 4 次。这将指示在迭代期间已分配但未释放的内存。

要弄清楚结果,请查看个人分配。

如果您需要查看对象使用工具的保留、释放和自动释放发生的位置:

在仪器中运行,在分配中设置“记录引用计数”(对于 Xcode 5 及更低版本,您必须停止记录以设置选项)。使应用程序运行、停止记录、向下钻取,您将能够看到所有保留、释放和自动释放发生的位置。

于 2015-08-28T02:07:18.077 回答
0

非常简单的方法是在 deinitialiser 中打印

deinit {
   print("<yourviewcontroller> destroyed.")
}

确保您看到此行打印在控制台上。将 deinit 放入所有视图控制器中。如果您无法看到特定的视图控制器,则意味着它们是一个参考周期。可能的原因是委托很强大,闭包捕获了自我,计时器没有被入侵等。

于 2015-08-28T09:28:24.290 回答
0

你可以使用 Instruments来做到这一点。正如本文最后一段所说:

一旦 Instruments 打开,您应该启动您的应用程序并进行一些交互,特别是在您要测试的区域或视图控制器中。任何检测到的泄漏都将在“泄漏”部分显示为红线。助手视图包括一个区域,仪器将在其中向您显示泄漏中涉及的堆栈跟踪,让您深入了解问题可能出在哪里,甚至允许您直接导航到有问题的代码。

于 2015-08-28T02:02:51.527 回答