本文将从以下两个层面解决iOS内存走漏问题:

  • 内存走漏排查办法(东西)
  • 内存走漏原因剖析(解决方案

在正式开端前,咱们先区别两个基本概念:

  1. 内存走漏(memory leak):是指请求的内存空间运用结束之后未收回一次内存泄露危害能够忽略,但若一向走漏,不管有多少内存,早晚都会被占用光,最终导致程序crash。(因而,开发中咱们要尽量防止内存走漏的呈现)
  2. 内存溢出(out of memory):是指程序在请求内存时,没有满足的内存空间供其运用。 浅显理解就是内存不够用了,通常在运转大型运用或游戏时,运用或游戏所需求的内存远远超出了你主机内装置的内存所接受巨细,就叫内存溢出。最终导致机器重启或许程序crash

简单来说:

概念 区别说明
内存走漏 供应方(操作系统)能提供给需求方(App)的内存越来越少。
内存溢出 需求方(App)需求的内存过大,超过供应方(操作系统)负载。

一、排查办法

咱们知道,iOS开发有“ARC机制”帮忙办理内存,但在实践开发中,假如处理不好堆空间上的内存还是会存在内存走漏的问题。假如内存走漏严重,最终会导致程序的溃散。

首要,咱们需求查看咱们的App有没有内存走漏,并且快速定位到内存走漏的代码。现在比较常用的内存走漏的排查办法有两种,都在Xcode中能够直接运用:

  • 第一种:静态剖析办法(Analyze
  • 第二种:动态剖析办法(Instrument东西库里的Leaks)。一般引荐运用第二种。

1.1 静态内存走漏剖析办法:

  • 第一步:经过Xcode翻开项目,然后点击Product->Analyze,开端进入静态内存走漏剖析。 如下图所示:

    iOS 内存泄漏排查方法及原因分析

  • 第二步:等候剖析成果。

  • 第三步:依据剖析的成果对可能形成内存走漏的代码进行排查,如下图所示。

iOS 内存泄漏排查方法及原因分析

PS:静态内存走漏剖析能发现大部分问题,但仅仅静态剖析,并且并不准确,仅仅有可能发生内存走漏。一些动态内存分配的情形并没有剖析。假如需求更精准一些,那就要用到下面要介绍的动态内存走漏剖析办法(Instruments东西中的Leaks办法)进行排查。

1.2 动态内存走漏剖析办法:

静态内存走漏剖析不能把所有的内存走漏排查出来,由于有的内存走漏发生在运转时,当用户做某些操作时才发生内存走漏。这是就要运用动态内存走漏检测办法了。

步骤如下:

  • 第一步:经过Xcode翻开项目,然后点击Product->Profile,如下图所示:

iOS 内存泄漏排查方法及原因分析

  • 第二步:按上面操作,build成功后跳出Instruments东西,如上图右侧图所示。选择Leaks选项,点击右下角的【choose】按钮。如下图:

iOS 内存泄漏排查方法及原因分析

  • 第三步:这时候项目程序也在模拟器或手机上运转起来了,在手机或模拟器上对程序进行操作,东西显现效果如下:

iOS 内存泄漏排查方法及原因分析

点击左上角的赤色圆点,这时项目开端启动了,由于Leaks是动态监测,所以手动进行一系列操作,可查看项目中是否存在内存走漏问题。如图所示,橙色矩形框中所示绿色为正常,假如呈现如右侧赤色矩形框中显现赤色,则表明呈现内存走漏。

iOS 内存泄漏排查方法及原因分析

选中Leaks Checks,在Details所在栏中选择CallTree,并且在右下角勾选Invert Call TreeHide System Libraries,会发现显现若干行代码,双击即可跳转到呈现内存走漏的当地,修正即可。

举个比如:

iOS 内存泄漏排查方法及原因分析

PS:AFHTTPSessionManager内存走漏是一个很常见的问题:解决办法有两种:点击这儿


二、内存走漏的原因剖析

现在,在ARC环境下,导致内存走漏的底子原因是代码中存在循环引证,然后导致一些内存无法释放,最终导致dealloc()办法无法被调用。主要原因大概有一下几种类型:

2.1 ViewController中存在NSTimer

假如你的ViewController中有NSTimer,那么你就要注意了,由于当你调用

[NSTimer scheduledTimerWithTimeInterval:1.0
                                 target:self 
                               selector:@selector(updateTime:) 
                               userInfo:nil 
                                repeats:YES];
  • 理由:这时 target: self,增加了ViewController的retain count, 即self强引证timertimer强引证self。形成循环引证。
  • 解决方案:在恰当机遇调用[timer invalidate]即可。

2.2 ViewController中的署理delegate

署理在一般情况下,需求运用weak润饰。假如你这个VC需求外部传某个delegate进来,经过delegate+protocol的方法传参数给其他对象,那么这个delegate一定不要强引证,尽量运用weak润饰,不然你的VC会继续持有这个delegate,直到署理自身被释放。

  • 理由:假如署理用strong润饰,ViewController(self)会强引证ViewView强引证delegatedelegate内部强引证ViewController(self)。形成内存走漏。
  • 解决方案:署理尽量运用weak润饰。

举个比如:署理一般用weak润饰,防止循环引证。

@class QiAnimationButton;
@protocol QiAnimationButtonDelegate <NSObject>
@optional
- (void)animationButton:(QiAnimationButton *)button willStartAnimationWithCircleView:(QiCircleAnimationView *)circleView;
- (void)animationButton:(QiAnimationButton *)button didStartAnimationWithCircleView:(QiCircleAnimationView *)circleView;
- (void)animationButton:(QiAnimationButton *)button willStopAnimationWithCircleView:(QiCircleAnimationView *)circleView;
- (void)animationButton:(QiAnimationButton *)button didStopAnimationWithCircleView:(QiCircleAnimationView *)circleView;
- (void)animationButton:(QiAnimationButton *)button didRevisedAnimationWithCircleView:(QiCircleAnimationView *)circleView;
@end
@interface QiAnimationButton : UIButton
@property (nonatomic, weak) id <QiAnimationButtonDelegate> delegate;
- (void)startAnimation; //!< 开端动画
- (void)stopAnimation; //!< 结束动画
- (void)reverseAnimation; //!< 最终的修正动画

2.3 ViewController中Block

在咱们日常开发中,假如block运用不当,很容易导致内存走漏。

  • 理由:假如block被当时ViewController(self)持有,这时,假如block内部再持有ViewController(self),就会形成循环引证。
  • 解决方案:在block外部对弱化self,再在block内部强化现已弱化的weakSelf

For Example:

    __weak typeof(self) weakSelf = self;
    [self.operationQueue addOperationWithBlock:^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (completionHandler) {
            KTVHCLogDataStorage(@"serial reader async end, %@", request.URLString);
            completionHandler([strongSelf serialReaderWithRequest:request]);
        }
    }];