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

 更新时间:2023年07月13日 11:35:42   作者:齐舞647  
本文主要介绍了iOS 内存泄漏排查方法及原因,将从以下两个层面解决iOS内存泄漏问题,内存泄漏排查方法和内存泄漏原因分析,文中有详细的图文介绍,需要的朋友可以参考下

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

  • 内存泄漏排查方法(工具)
  • 内存泄漏原因分析(解决方案)

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

内存泄漏(memory leak):是指申请的内存空间使用完毕之后未回收。 一次内存泄露危害可以忽略,但若一直泄漏,无论有多少内存,迟早都会被占用光,最终导致程序crash。(因此,开发中我们要尽量避免内存泄漏的出现)

内存溢出(out of memory):是指程序在申请内存时,没有足够的内存空间供其使用。 通俗理解就是内存不够用了,通常在运行大型应用或游戏时,应用或游戏所需要的内存远远超出了你主机内安装的内存所承受大小,就叫内存溢出。最终导致机器重启或者程序crash

简单来说:

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

一、排查方法

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

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

  • 第一种:静态分析方法(Analyze
  • 第二种:动态分析方法(Instrument工具库里的Leaks)。一般推荐使用第二种。

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

  • 第一步:通过Xcode打开项目,然后点击Product->Analyze,开始进入静态内存泄漏分析。 如下图所示:

  • 第二步:等待分析结果。

  • 第三步:根据分析的结果对可能造成内存泄漏的代码进行排查,如下图所示。

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

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

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

步骤如下:

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

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

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

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

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

举个例子:

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

第一种方案:把该manager封装成单例

  • 解决理由:内存中的某一块固定的地址就用来存放manager,专门用来网络请求和释放。

  • 方案代码:

static AFHTTPSessionManager *manager;
/* 封装成 单例会话管理者 */
+ (AFHTTPSessionManager *)sharedManager {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 初始化请求管理类
        manager = [AFHTTPSessionManager manager];
        manager.requestSerializer = [AFJSONRequestSerializer serializer];
        // 设置15秒超时 - 取消请求
        manager.requestSerializer.timeoutInterval = 15.0;
        // 编码
        manager.requestSerializer.stringEncoding = NSUTF8StringEncoding;
        // 缓存策略
        manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
        manager.responseSerializer = [AFJSONResponseSerializer serializer];
        // 支持内容格式
        manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/plain", @"text/javascript", @"text/json", @"text/html", nil];
    });
    return manager;
}

问题:很明显,同一时刻只能有一个网络请求。异步会有问题。当两个线程同时申请manager对象时,肯定有一个manager申请不到,无法网络请求

第二种方案:在网络请求的block内把task取消掉

无论是success,还是failure的回调都取消掉,当然在block外部需要弱化一下manager对象

__weak typeof(manager) weakManager = manager;

然后在两个回调方法里加上

[weakManager invalidateSessionCancelingTasks:YES];

两种方案都可以解决内存泄漏问题。

二、内存泄漏的原因分析

目前,在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]);
        }
    }];

以上就是iOS 内存泄漏排查方法及原因分析的详细内容,更多关于iOS 内存泄漏的资料请关注脚本之家其它相关文章!

相关文章

  • 扫描二维码控件的封装iOS实现

    扫描二维码控件的封装iOS实现

    这篇文章主要为大家详细介绍了iOS实现扫描二维码控件的封装,具有一定的实用性和参考价值,感兴趣的小伙伴们可以参考一下
    2016-08-08
  • iOS ScrollView嵌套tableView联动滚动的思路与最佳实践

    iOS ScrollView嵌套tableView联动滚动的思路与最佳实践

    这篇文章主要给大家介绍了关于ScrollView嵌套tableView联动滚动的思路与最佳实践,文中通过示例代码介绍的非常详细,对各位iOS开发者们具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-10-10
  • iOS中常见的几种加密方法总结

    iOS中常见的几种加密方法总结

    这篇文章主要给大家介绍了关于iOS中常见的几种加密方法,其中包括 base64加密、POST加密、Token值介绍、MD5加密--(信息-摘要算法) 哈希算法之一、时间戳密码以及指纹识别等方法,文中通过示例代码介绍的非常详细,需要的朋友可以参考下。
    2017-12-12
  • UILabel显示定时器文本跳动问题的解决方法

    UILabel显示定时器文本跳动问题的解决方法

    这篇文章主要给大家介绍了关于UILabel显示定时器文本跳动问题的解决方法,文中通过示例代码介绍的非常详细,对各位iOS开发者们具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-07-07
  • iOS开发APP跳转到设置或系统页面详解

    iOS开发APP跳转到设置或系统页面详解

    这篇文章主要为大家介绍了iOS开发APP跳转到设置或系统页面详解,<BR>有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • iOS使用WKWebView加载HTML5不显示屏幕宽度的问题解决

    iOS使用WKWebView加载HTML5不显示屏幕宽度的问题解决

    这篇文章主要介绍了iOS使用WKWebView加载HTML5不显示屏幕宽度的问题解决,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-12-12
  • 谈谈iOS中的几种锁

    谈谈iOS中的几种锁

    这篇文章主要介绍了谈谈iOS中的几种锁,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2016-11-11
  • 总结IOS关闭键盘/退出键盘的五种方式

    总结IOS关闭键盘/退出键盘的五种方式

    IOS开发中经常要用到输入框,默认情况下点击输入框就会弹出键盘,但是必须要实现输入框return的委托方法才能取消键盘的显示,对于用户体验来说很不友好,我们可以实现例如点击键盘以外的空白区域来将键盘关闭的功能,以下是我总结出的几种关闭键盘的方法。
    2016-08-08
  • iOS开发中文件的上传和下载功能的基本实现

    iOS开发中文件的上传和下载功能的基本实现

    这篇文章主要介绍了iOS开发中文件的上传和下载功能的基本实现,并且下载方面讲到了大文件的多线程断点下载,需要的朋友可以参考下
    2015-11-11
  • iOS开发之tableView cell的展开收回功能实现代码

    iOS开发之tableView cell的展开收回功能实现代码

    本文介绍了iOS开发之tableView cell的展开收回功能实现代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01

最新评论