iOS 日常内存泄漏检测

准备工作

内存泄漏一般使用 xcode 内置的 Instruments 里的 Leaks,第三方库检测的范围有限

image

选中真机,然后再选择启动的项目,点击左上方的红色按钮启动调试

image

启动后,在扫描结果中,如果出现红色✖️,说明可能出现内存泄漏,然后按照图上指示,选中一个泄漏图标✖️,左侧点击 Leaks,在下方就能看出泄漏的堆栈信息。

image

右侧的曲线图含义:

  • Allocations,内存分配
  • Leaks,内存泄漏

然后通过底部筛选项进行一些筛选,能看到更详细更有用的参数

image

其他可选配置

1. Snapshots - 设置快照时间

image

勾选 Automatic Snapshotting 时,会自动生成捕获结果分析,设置快照生成时间根据设备能力来设置,设备性能好,可以设置更短的时间,比如5s。除非会手动捕获,可以不勾选,然后点击 Snapshot Now 来捕获

2. Call Tree 的设置

  • Separate By Thread:线程分离,只有这样才能在调用路径中能够清晰看到占用CPU最大的线程.每个线程应该分开考虑。只有这样你才能揪出那些大量占用CPU的"重"线程,按线程分开做分析,这样更容易揪出那些吃资源的问题线程。特别是对于主线程,它要处理和渲染所有的接口数据,一旦受到阻塞,程序必然卡顿或停止响应。

  • Invert Call Tree:从上到下跟踪堆栈信息.这个选项可以快捷的看到方法调用路径最深方法占用CPU耗时(这意味着你看到的表中的方法,将已从第0帧开始取样,这通常你是想要的,只有这样你才能看到CPU中话费时间最深的方法),比如FuncA{FunB{FunC}},勾选后堆栈以C->B->A把调用层级最深的C显示最外面.反向输出调用树。把调用层级最深的方法显示在最上面,更容易找到最耗时的操作。

  • Hide Missing Symbols:如果dSYM无法找到你的APP或者调用系统框架的话,那么表中将看到调用方法名只能看到16进制的数值,勾选这个选项则可以隐藏这些符号,便于简化分析数据.

  • Hide System Libraries:表示隐藏系统的函数,调用这个就更有用了,勾选后耗时调用路径只会显示app耗时的代码,性能分析普遍我们都比较关系自己代码的耗时而不是系统的.基本是必选项.注意有些代码耗时也会纳入系统层级,可以进行勾选前后前后对执行路径进行比对会非常有用.因为通常你只关心cpu花在自己代码上的时间不是系统上的,隐藏系统库文件。过滤掉各种系统调用,只显示自己的代码调用。隐藏缺失符号。如果 dSYM 文件或其他系统架构缺失,列表中会出现很多奇怪的十六进制的数值,用此选项把这些干扰元素屏蔽掉,让列表回归清爽。

  • Show Obj-C Only: 只显示oc代码 ,如果你的程序是像OpenGl这样的程序,不要勾选侧向因为他有可能是C++的

  • Flatten Recursion: 递归函数, 每个堆栈跟踪一个条目,拼合递归。将同一递归函数产生的多条堆栈(因为递归函数会调用自己)合并为一条。

  • Top Functions:找到最耗时的函数或方法。 一个函数花费的时间直接在该函数中的总和,以及在函数调用该函数所花费的时间的总时间。因此,如果函数A调用B,那么A的时间报告在A花费的时间加上B.花费的时间,这非常真有用,因为它可以让你每次下到调用堆栈时挑最大的时间数字,归零在你最耗时的方法。

发现泄漏

出现了跟 AF 相关的内存泄漏

image

建议最好是用真机测试,然后有可能用 Leaks 单独打开应用时显示的堆栈信息没有具体到函数名上,那这个是符号表没有配置的问题,需要在 Xcode-Target-Build Settings,搜索 dsym,编译在哪个模式下就在哪个模式下选择,调试一般是 debug 模式:

image

最后代码是走到了 AFURLSessionManager 内的构造函数 initWithSessionConfiguration 里,从这里分析一下内部代码,看看是哪里出现的泄漏~~

image

通过 NSURLSession 的头文件我们发现,NSURLSession 对于它的 delegate 属性是强引用。

image

这就意味着当 session 存在时,其 delegate 就不会被释放。另外,由 session 发起请求的缓存相关对象也会被其强引用并一直保留在内存中。

所以为了避免内存泄漏,根据 Apple 文档,当一个 session 不再使用时,我们应该调用 invalidateSessionCancelingTasks: 把 session 显式地置为无效 (invalidated),以释放对相关对象的引用。

结果

修改完后,大部分的内存泄漏已经不存在,里面涉及到的一些 Allocated Prior to Attach ,在操作系统已经开始运行之后,调试器和仪器等应用程序可能会挂接到 app 中。该消息的意思是它不知道给定的内存是如何分配的,因为它是在 Instruments 连接到 app 之前分配的。所以它无法追踪是来自哪里。

image

另外,如果是单例模式下,AFURLSessionManager 会在内存中常驻且可以被访问到,因此不会造成内存泄漏。同时请求相同接口也不需要重新建立 TCP 连接

结论

内存泄漏会导致内存上涨,几次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光

你可能感兴趣的:(iOS 日常内存泄漏检测)