- 不久之前的学习笔记了,整理下~
- 结合源码看更佳~
内存泄露检测
- Leaks检测leaded memory,Allocations通过MarkGeneration检测abandoned memory(仍然被引用但是没用的内存),操作麻烦,不够及时精准定位
- MLeaksFinder:无侵入,即时,无需为了检测内存泄露重复操作某个场景。精准定位是哪个对象没释放。有白名单机制;目前只检测ViewController和View对象(可扩展,MLCheck())
原理
入口
- [MLeaksFinder start]; //进行swizzle
- [MLeaksFinder stop]; //设置静态局部参数,为NO,hook的函数不会执行到关键代码
NSObject
添加willDealloc方法
- 检测该对象是否在白名单内,白名单通过类判断
- 判断该对象是否是上一次发送action的对象,是的话,不进行内存检测
- 弱指针指向self,3s延迟,若被释放,给nil发消息直接返回,不触发断言,认为已经释放
assertNotDealloc
- 判断leakedObjectPtrs跟对象的父节点集合有交集,则说明父节点已经弹过,无需再处理
- [MLeakedObjectProxy isAnyObjectLeakedAtPtrs:[self parentPtrs]]
- [MLeakedObjectProxy addLeakedObject:self]; //内部调用弹alert
- 判断leakedObjectPtrs跟对象的父节点集合有交集,则说明父节点已经弹过,无需再处理
- NSObject除了上面的两个函数,还有如下函数:
- willReleaseChild/willReleaseChildren:释放某个对象,以及子对象(调willDealloc)
- 关联对象:viewStack
- 关联对象:parentPtrs,在willReleaseChildren会对已经处理过的对象进行添加,这样在assertNotDealloc中,会判断当前对象是否与父节点集合有交集
- 白名单:classNamesInWhiteList
- willReleaseChild/willReleaseChildren:释放某个对象,以及子对象(调willDealloc)
MLeakedObjectProxy
- 前面assertNotDealloc已经两度提及该类的方法
- isAnyObjectLeakedAtPtrs:
- addLeakedObject:
- 构造MLeakedObjectProxy对象
- object强持有proxy,proxy若持有object。如果object释放,proxy也会释放
- 弹窗提示
- dealloc
- 析构函数,弹窗提示前面的内存泄露对象在某个时间点重新释放了
willDealloc的执行时机:寻找释放点
- 其调用是在第一步swizzle里面,swizzle了UIViewController,UIApplication,UINavigationController、UITouch的方法,hook后的方法里调用的(本质上就是在认为该释放的地方调用,寻找释放点)
- 其实各种代码看到最后,MLeaksFinder检测的就是跟VC相关的代码,核心代码在VC里swizzle
UIViewController+MemoryLeak
- 模态视图hook -> dismissViewControllerAnimated:completion:
- 对将要dismiss的VC调willDealloc
- navigationController被pop出来
- hook navigationController的方法UINavigationController+MemoryLeak
- popViewControllerAnimated
- 代码截图省略了,splitViewController相关的代码
- 上面并不是直接调willDealloc,而是设置了VC的一个关联参数属性kHasBeenPoppedKey为YES,因为用过左滑时间超过2s不放开,VC是不会释放的,因此此处通过关联对象处理
- 如果VC出栈且释放,hookVC的viewDidDisappear,如果该关联参数为YES就调willDealloc
- 如果滑到一半回来,hookVC的viewWillAppear,设置该关联参数为NO
- popToViewController:animated:
- popToRootViewControllerAnimated:
- 后两种方法,系统方法调用是会返回pop掉的数组的,hook掉,获取pop调的数组,遍历,对每个vc依次调willDealloc
- popViewControllerAnimated
- navigationController也加了willDealloc,如果navigationController被释放,释放其viewControllers
- hook navigationController的方法UINavigationController+MemoryLeak
- VC除了hook dismiss还hook了viewDidDisappear,viewWillAppear
- 这两个方法前面都提到了,以及里面做了什么事情
- willDealloc
- 调Super,NSObject的willDealloc,前面介绍了,判断白名单,进行延迟调断言
- 释放子VC,presentedVC,以及子view
- 释放子view时,view的willDealloc方法里,是会调[self willReleaseChildren:self.subviews];
- 这样看完了,能够完整的创建view的堆栈,就是因为willDealloc里会继续调子对象的释放willDealloc
特殊情况
- 有时候即使调了pop,dismiss,也不会被释放,比如单例。如果某个特别的对象不会被释放,开发者可以重写willDealloc,return NO
- 部分系统的view是不会被释放的,需要建立白名单
- MLeaksFinder支持手动扩展,通过MLCheck()宏来检测其他类型的对象的内存泄露,为传进来的对象建立View-ViewController stack信息
- 结合FBRetainCycleDetector一起使用时:
- 内存泄漏不一定是循环引用造成的
- 有的循环引用 FBRetainCycleDetector 不一定能找出