版权声明:本文为博主原创文章,未经博主允许不得转载。 https://cloud.tencent.com/developer/article/1337757
在iOS 开发过程中我们有时会遇到内存泄漏的问题,我们也会对内存泄漏进行监测,如苹果自带的Instrument。我们会使用Instrument中的Leaks/Allocations对内存泄漏进行监测,但在使用过程中却会存在各种问题以及不便。
leaks的问题:
其中 Leaked memory 和 Abandoned memory 都属于应该释放而没释放的内存,都是内存泄露,而 Leaks 工具只负责检测 Leaked memory,而不管 Abandoned memory。在 MRC 时代 Leaked memory 很常见,因为很容易忘了调用 release,但在 ARC 时代更常见的内存泄露是循环引用导致的 Abandoned memory,Leaks 工具查不出这类内存泄露,应用有限。
Allocations的问题:
对于 Abandoned memory,可以用 Instrument 的 Allocations 检测出来。检测方法是用 Mark Generation 的方式,当你每次点击 Mark Generation 时,Allocations 会生成当前 App 的内存快照,而且 Allocations 会记录从上回内存快照到这次内存快照这个时间段内,新分配的内存信息。举一个最简单的例子:
我们可以不断重复 push 和 pop 同一个 UIViewController,理论上来说,push 之前跟 pop 之后,app 会回到相同的状态。因此,在 push 过程中新分配的内存,在 pop 之后应该被 dealloc 掉,除了前几次 push 可能有预热数据和 cache 数据的情况。如果在数次 push 跟 pop 之后,内存还不断增长,则有内存泄露。因此,我们在每回 push 之前跟 pop 之后,都 Mark Generation 一下,以此观察内存是不是无限制增长。这个方法在 WWDC 的视频里: Session 311 - Advanced Memory Analysis with Instruments ,以及苹果的开发者文档: Finding Abandoned Memory 里有介绍。
用这种方法来发现内存泄露还是很不方便的:
首先,你得打开 Allocations
其次,你得一个个场景去重复的操作
无法及时得知泄露,得专门做一遍上述操作,十分繁琐
之前在项目中就使用了一个更好的监测内存泄漏的工具,可一直没有记录下来,这次有时间就赶紧记录在此。这个工具的名字是MLeaksFinder。先附上MLeaksFinder的下载地址:https://github.com/Zepo/MLeaksFinder
我们只要将MLeaksFinder导入到项目中就能监测项目中的内存泄漏的代码了,而无需我们做更多的操作。MLeaksFinder只在debug模式下有效,而不会影响我们的release包。当程序出现内存泄漏,会弹出一个内存泄漏的提示框,并显示是谁发生了内存泄漏,如图所示:
这样我们就能很快地定位到内存泄漏的页面了。
MLeaksFinder的原理:
MLeaksFinder一开始是从UIViewController入手的,UIViewController在POP或dismiss之后该控制器及其上的view,view的subviews都会被释放掉,MleaksFinder就是在控制器POP或dismiss之后去查看该控制器和其上的view是否都被释放掉。
具体的方法是,为基类 NSObject 添加一个方法 -willDealloc 方法,该方法的作用是,先用一个弱指针指向 self,并在一小段时间(3秒)后,通过这个弱指针调用 -assertNotDealloc,而 -assertNotDealloc 主要作用是直接中断言。这样,当我们认为某个对象应该要被释放了,在释放前调用这个方法,如果3秒后它被释放成功,weakSelf 就指向 nil,不会调用到 -assertNotDealloc 方法,也就不会中断言,如果它没被释放(泄露了),-assertNotDealloc 就会被调用中断言。这样,当一个 UIViewController 被 pop 或 dismiss 时(我们认为它应该要被释放了),我们遍历该 UIViewController 上的所有 view,依次调 -willDealloc,若3秒后没被释放,就会中断言。
简而言之就是当一个对象3秒之后还没释放,那么指向它的 weak 指针还是存在的,所以可以调用其 runtime 绑定的方法 willDealloc 从而提示内存泄漏。