内存泄露问题分析
一般引起内存泄露的可能
1、delegate使用strong引用,如果父VC持有子VC,并设置VC的delegate为self(也就是父VC),这样的结果就是子VC也间接持有了父VC,造成了循环引用,在Pop子VC的时候不会调用dealloc
2、timer是否没有invalidate,scheduledTimerWithTimeInterval:target:selector:userInfo:
这里的target一般是self,这个时候就需要注意了,在退出时候,如果timer还在执行的话,有timer会持有self,所以也不会调dealloc
确保timer有invalidate,或使用弱引用weakSelf来替代self
3、最常见的其实是block引起的循环引用问题
block的引用分以下三种情况
1)不作为成员变量
[self oneBlockSucess:^{
[self doSomething];
}];
2)成员变量,直接引用
self.secondBlock = ^{
[self doSomething];
};
3)成员变量,间接引用
typedef void(^BtnPressedBlock)(UIButton*btn);
@interface XXSubmitBottomView:UIView
@property(nonatomic,copy)BtnPressedBlock block;
-(void)submittBtnPressed:(BtnPressedBlock)block;
@end
@implementation XXConfirmOrderController
if(!_bottomView) {
_bottomView = [[XXSubmitBottomView alloc] initWithFrame:CGRectMake(0,self.view.height -50, Width,50)];
_bottomView.currentVc =self;
#warning self.bottomView.block self间接持有了BtnPressedBlock 必须使用weak!
WEAKSELF//ps: weakSelf的宏定义#define WEAKSELF typeof(self) __weak weakSelf = self;
[_bottomView submittBtnPressed:^(UIButton*btn) {
}
}
把self通过参数回传给block,这样就不会产生循环引用了。
block回传的self可以声明成id类型,这样使用的时候可以在入参声明具体self类型,避免显式类型转换,方便开发。
注意:block 内部有延迟操作的时候,使用外部声明的强引用

这时候你会发现,在打印 block 内容的之前,person 对象已经被释放了,自然,name 属性的值,也是空的了。
另外也不只是after,asyn等都是这种情况。
此时需要使用
__strong ClassName *sself = wself;

总结:存在使用了类的成员变量而导致循环引用的情况,需要使用__weak来弱引用。
要判断A是否引用B
4、CoreFoundation对象(C对象,或单独与C混编) : 只要函数中包含了create\new\copy\retain等关键字, 那么这些方法产生的对象, 就必须在不再使用的时候调用1次CFRelease或者其他release函数
以上是内存泄露的可能原因,但是如何检测内存泄露,并快速定位呢?
Instrument其实有很多不便
Leaks
先看看 Leaks,从苹果的开发者文档里可以看到,一个 app 的内存分三类:
Leaked memory: Memory unreferenced by your application that cannot be used again or freed (also detectable by using the Leaks instrument).
Abandoned memory: Memory still referenced by your application that has no useful purpose.
Cached memory: Memory still referenced by your application that might be used again for better performance.
1、 Leaked memory 和 Abandoned memory 都属于应该释放而没释放的内存,都是【内存泄露】。
2、而 Leaks 工具只负责检测 Leaked memory,而不管 Abandoned memory。在 MRC 时代 Leaked memory 很常见,因为很容易忘了调用 release,但在 ARC 时代更常见的内存泄露是循环引用导致的 Abandoned memory,Leaks 工具查不出这类内存泄露,应用有限。
MLeaksFinder 框架
MLeaksFinder提供了内存泄露检测更好的解决方案。
1、只需要引入 MLeaksFinder,就可以自动在 App 运行过程检测到内存泄露的对象并立即提醒,
2、无需打开额外的工具。
3、也无需为了检测内存泄露而一个个场景去重复地操作。
总结:MLeaksFinder 目前能自动检测 UIViewController 和 UIView 对象的内存泄露,而且也可以扩展以检测其它类型的对象。
MLeaksFinder 的使用很简单
参照https://github.com/Zepo/MLeaksFinder,基本上就是把 MLeaksFinder 目录下的文件添加到你的项目中,就可以在运行时(debug 模式下)帮助你检测项目里的内存泄露了,无需修改任何业务逻辑代码,而且只在 debug 下开启,完全不影响你的 release 包。
当发生内存泄露时,MLeaksFinder 会中断言,并准确的告诉你哪个对象泄露了。这里设计为中断言而不是打日志让程序继续跑,是因为很多人不会去看日志,断言则能强制开发者注意到并去修改,而不是犯拖延症。
中断言时,控制台会有如下提示,View-ViewController stack 从上往下看,该 stack 告诉你,MyTableViewController 的 UITableView 的 subview UITableViewWrapperView 的 subview MyTableViewCell 没被释放。而且,这里我们可以肯定的是 MyTableViewController,UITableView,UITableViewWrapperView 这三个已经成功释放了。

从 MLeaksFinder 的使用方法可以看出,MLeaksFinder 具备以下优点:
1、使用简单,不侵入业务逻辑代码。
2、不用打开 Instrument不需要额外的操作,你只需开发你的业务逻辑,在你运行调试时就能帮你检测内存泄露发现及时,更改完代码后一运行即能发现(这点很重要,你马上就能意识到哪里写错了)精准,能准确地告诉你哪个对象没被释放