您的当前位置:首页正文

内存泄露问题分析

来源:花图问答

一般引起内存泄露的可能

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不需要额外的操作,你只需开发你的业务逻辑,在你运行调试时就能帮你检测内存泄露发现及时,更改完代码后一运行即能发现(这点很重要,你马上就能意识到哪里写错了)精准,能准确地告诉你哪个对象没被释放