偶然发现自己的 ViewController 退出后没有释放,存在内存泄露,而代码量大的情况下自己找可能的 retain cycle 不太现实,于是学习了用官方的 Instruments 工具来找出代码的问题。
一、打开 Instruments
If Instruments has access to information about your app’s source code, a leak is reported as a class name. Otherwise, a leak is reported as a memory address, such as Malloc-size. To ensure that Instruments has access to information about your code, initiate profiling from Xcode (see Profile from Xcode) or configure a symbol mappings file (see Map Data to Source Code).
上面这段话来自 Apple 官方文档Find Memory Leaks。
在开始之前,为了确保 Instruments 可以访问到代码从而更好地定位问题,需要在 Xcode 中执行 Profile。首先在 Xcode 中 build 或 run 最新代码,成功后选择菜单栏中的 Product->Profile(或快捷键 Cmd+I),完成后在弹出的菜单中选择 Leaks,如图:
二、使用 Leaks 定位问题
点击红色圆圈按钮,即可 luanch app,并开始录制内存使用情况。界面如图:
默认显示 Statistics 详情面板。为了迅速筛选出感兴趣的 allocation,可以在底部输入框中输入相关关键字,例如「viewcontroller」,这样就只剩下项目相关的 view controller 了:
其中,两个需要关注的数据栏是 # Persistent
和 # Transient
:# Persistent
表示现在在内存中各个类别的 object 的数量,正在使用内存;而 # Transient
表示存在过但是目前已被销毁的 object 的数量,其占用的内存已被释放。
操作若干次后,内存分配情况如图:
可以明显地看出,ConferenceViewController
目前存在 4 个,释放过的个数为 0。因此该 view controller 有内存泄露。点击 type 右侧箭头(如图中所示):
可以看到 4 个 object 的地址、大小、代码来源等信息。点击地址右侧箭头,可以进一步看到详细的引用计数变化情况。点击与项目代码相关的使引用计数增加的一行,可以看到右侧的 stack trace 信息:
圈出的黑色部分,即项目的相关调用。双击最上面的一行,左侧详情面板出现了具体代码,接下来即可根据有颜色的提示找到项目的具体问题。
对于示例,很显然是 - (void)loadData
函数出了问题。最常见的情况就是传递给将要保存的 block 中强引用了 self
,导致 retain cycle。改为对 self 的弱引用,再重复上述步骤,可以看到反复操作后,# Transient
的数量正常增加了。
三、结语
以上是 Leaks 工具快速查找 memory leak 的一个用法,还有其他方法可以查看 allocation 情况,但在本例中对作者帮助不大,就不详细列出了。可以参考官方文献 Find Memory Leaks,和文章 Instruments Tutorial with Swift: Getting Started,有更详细的用法讲解。