一般引起内存泄露的可能
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 框架
1、只需要引入 MLeaksFinder,就可以自动在 App 运行过程检测到内存泄露的对象并立即提醒,
2、无需打开额外的工具。
3、也无需为了检测内存泄露而一个个场景去重复地操作。
总结:MLeaksFinder 目前能自动检测 UIViewController 和 UIView 对象的内存泄露,而且也可以扩展以检测其它类型的对象。
MLeaksFinder 的使用很简单
当发生内存泄露时,MLeaksFinder 会中断言,并准确的告诉你哪个对象泄露了。这里设计为中断言而不是打日志让程序继续跑,是因为很多人不会去看日志,断言则能强制开发者注意到并去修改,而不是犯拖延症。
中断言时,控制台会有如下提示,View-ViewController stack 从上往下看,该 stack 告诉你,MyTableViewController 的 UITableView 的 subview UITableViewWrapperView 的 subview MyTableViewCell 没被释放。而且,这里我们可以肯定的是 MyTableViewController,UITableView,UITableViewWrapperView 这三个已经成功释放了。
从 MLeaksFinder 的使用方法可以看出,MLeaksFinder 具备以下优点:
1、使用简单,不侵入业务逻辑代码。
2、不用打开 Instrument不需要额外的操作,你只需开发你的业务逻辑,在你运行调试时就能帮你检测内存泄露发现及时,更改完代码后一运行即能发现(这点很重要,你马上就能意识到哪里写错了)精准,能准确地告诉你哪个对象没被释放