MLeaksFinder
作用
MLeaksFinder helps you find memory leaks in your iOS apps at develop time
优点 VS Instrument
MLeaksFinder 具备以下优点:
- 使用简单,不侵入业务逻辑代码,不用打开 Instrument
- 不需要额外的操作,你只需开发你的业务逻辑,在你运行调试时就能帮你检测
- 内存泄露发现及时,更改完代码后一运行即能发现(这点很重要,你马上就能意识到哪里写错了)
- 精准,能准确地告诉你哪个对象没被释放
使用
1 | Memory Leak |
MLeaksFinder can automatically find leaks in UIView and UIViewController objects. 通过断言的方式弹窗展示未被释放的UIView或UIViewController对象
关闭
一些不应该被释放的对象比如全局对象,static,单例,又不想被弹窗误报override
1 | - (BOOL)willDealloc { |
实现
Podspec文件
1 | Pod::Spec.new do |s| |
1 | 重要: FBRetainCycleDetector is removed from the podspec due to Facebook's BSD-plus-Patents license. If you |
willDealloc方法
为基类 NSObject 添加一个方法 -willDealloc 方法,该方法的作用是,先用一个弱指针指向 self,并在一小段时间(3秒)后,通过这个弱指针调用 -assertNotDealloc,而 -assertNotDealloc 主要作用是直接中断言。
1 | - (BOOL)willDealloc { |
果3秒后它被释放成功,weakSelf 就指向 nil,不会调用到 -assertNotDealloc 方法,也就不会中断言,如果它没被释放(泄露了),-assertNotDealloc 就会被调用中断言
FBRetainCycleDetector
An iOS library that finds retain cycles using runtime analysis
用于检测引起内存泄漏对象的环形引用链
使用
1 | FBRetainCycleDetector *detector = [FBRetainCycleDetector new]; |
如何检测饮用成环
点击 “Retain Cycle” 按钮,MLeaksFinder 将调用 FBRetainCycleDetector 进行详细问题检测
1 |
|
NSObject对象循环引用
FBRetainCycleDetector 基于外部传入的object 以及查找深度,进行深度优先遍历所有强引用属性,和动态运行时关联的强引用属性,同时将这些 关联对象的地址 放入 objectSet (set)的集合中, 将对象信息计入 objectOnPath 集合中(set), 并且将对象在对象栈 stack 中存储一份,便于查找对应环。
首先判断 如果传入的 object 是 NSObject 的话,获取对象的 class,使用
1 | const char *class_getIvarLayout(Class cls); |
获取 class 的所有定义的 property 的布局信息,取出 object 对应的 property 的value值,将value 对象的地址(数字)加入 objectSet 中,将对象指针加入到 objectOnPath,在整个树形遍历中,如果遍历到的新节点,在原来的 objectSet 地址表中已经存在了,代表形成了引用环,即原本的树形结构连成了图。此时可以根据 stack中记录的路径,结合 重复的 object构建一个环形图,作为环形引用链返回。
树是没有环的图
NSBlock类型对像
在C的结构体中是不存在强弱引用区分的,在编译期,编译器会将所谓的强引用通过一个 copy_helper 的function 做copy 操作,并为 block 生成的 struct 构造 dispose_helper 的 function,dispose_helper 负责在 struct 将要释放时,去释放它所引用的对象。下面是编译器生成的 dispose_helper function 的定义 ,入参为 struct 的地址 _Block_object_dispose 是编译器的 funtion
1 | void __block_dispose_4(struct __block_literal_4 *src) { |
作者利用黑盒测试,基于原有的 block对象 ,拿到对应block对象的 descriptor指针 ,descriptor记录了block对象释放的时候要执行的 dispose_helper 方法和block对象所有引用对象的数组,
这个数组包括了强引用对象和弱应用对象 *src。 也就是说,block被释放时,执行的 dispose_helper 方法的入参 是 *scr;那么只需要伪装一个被引用的数组,传入dispose_helper 做个测试,数组中哪一个对象呗调用了 release 方法,那么谁就是被强引用的,记住src对应下标的地址就好。
注意
特殊情况
对于某些特殊情况,释放的时机不大一样(比如系统手势返回时,在划到一半时 hold 住,虽然已被 pop,但这时还不会被释放,ViewController 要等到完全 disappear 后才释放),需要做特殊处理,具体的特殊处理视具体情况而定。
系统View
某些系统的私有 View,不会被释放(可能是系统 bug 或者是系统出于某些原因故意这样做的,这里就不去深究了),因此需要建立白名单
手动扩展
MLeaksFinder目前只检测 ViewController 跟 View 对象。为此,MLeaksFinder 提供了一个手动扩展的机制,你可以从 UIViewController 跟 UIView 出发,去检测其它类型的对象的内存泄露。如下所示,我们可以检测 UIViewController 底下的 View Model:
1 | - (BOOL)willDealloc { |