iOS开发攻城狮的集散地QiShare文章汇总iOS面试知识点

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

2018-09-19  本文已影响121人  QiShare

级别: ★★☆☆☆
标签:「iOS」「内存泄漏排查」「Leaks工具」
作者: MrLiuQ
审校: QiShare团队

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

在正式开始前,我们先区分两个基本概念:

  1. 内存泄漏(memory leak):是指申请的内存空间使用完毕之后未回收
    一次内存泄露危害可以忽略,但若一直泄漏,无论有多少内存,迟早都会被占用光,最终导致程序crash。(因此,开发中我们要尽量避免内存泄漏的出现)
  2. 内存溢出(out of memory):是指程序在申请内存时,没有足够的内存空间供其使用。
    通俗理解就是内存不够用了,通常在运行大型应用或游戏时,应用或游戏所需要的内存远远超出了你主机内安装的内存所承受大小,就叫内存溢出。最终导致机器重启或者程序crash

简单来说:

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

一、排查方法

我们知道,iOS开发有“ARC机制”帮忙管理内存,但在实际开发中,如果处理不好堆空间上的内存还是会存在内存泄漏的问题。如果内存泄漏严重,最终会导致程序的崩溃。

首先,我们需要检查我们的App有没有内存泄漏,并且快速定位到内存泄漏的代码。目前比较常用的内存泄漏的排查方法有两种,都在Xcode中可以直接使用:

1.1 静态内存泄漏分析方法:

静态内存泄漏分析结果

PS:静态内存泄漏分析能发现大部分问题,但只是静态分析,并且并不准确,只是有可能发生内存泄漏。一些动态内存分配的情形并没有分析。如果需要更精准一些,那就要用到下面要介绍的动态内存泄漏分析方法(Instruments工具中的Leaks方法)进行排查。

1.2 动态内存泄漏分析方法:

静态内存泄漏分析不能把所有的内存泄漏排查出来,因为有的内存泄漏发生在运行时,当用户做某些操作时才发生内存泄漏。这是就要使用动态内存泄漏检测方法了。

步骤如下:

动态内存泄漏分析-1 动态内存泄漏分析-2 Leaks使用

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

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

举个例子:

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


二、内存泄漏的原因分析

目前,在ARC环境下,导致内存泄漏的根本原因是代码中存在循环引用,从而导致一些内存无法释放,最终导致dealloc()方法无法被调用。主要原因大概有一下几种类型:

2.1 ViewController中存在NSTimer

如果你的ViewController中有NSTimer,那么你就要注意了,因为当你调用

[NSTimer scheduledTimerWithTimeInterval:1.0 
                                 target:self 
                               selector:@selector(updateTime:) 
                               userInfo:nil 
                                repeats:YES];

2.2 ViewController中的代理delegate

代理在一般情况下,需要使用weak修饰。如果你这个VC需要外部传某个delegate进来,通过delegate+protocol的方式传参数给其他对象,那么这个delegate一定不要强引用,尽量使用weak修饰,否则你的VC会持续持有这个delegate,直到代理自身被释放。

举个例子:代理一般用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使用不当,很容易导致内存泄漏。

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]);
        }
    }];

关注我们的途径有:
QiShare(简书)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公众号)

推荐文章:
Web安全漏洞之CSRF
2018苹果秋季新品发布会速览

上一篇下一篇

猜你喜欢

热点阅读