经验之道:最有效的 iOS 内存泄漏检测

作者:胡涛

使用instruments工具来更好的调试我们的工程中新开发或者修改过的模块的内存状况。

iOS设备性能越来越好,iOS App 也相应的变得越来越庞大,App代码的量级也在快速的增长,开发一个小的模块在工程中调试变的越来越难,通常我们是通过观察Allocation的内存变化高低,或者内存分配快照对比来寻找泄漏的情况,但这几乎是一个让人抓狂的调试方法,尤其在非常复杂的项目中,一个模块的推入与推出在Allocation上的变化微乎其微,而且受制于项目的复杂度,各种你所未知的对象的创建与销毁带来的Allocation图形高度的影响,对于观察Allocation的分配图像的高低变化来说,能够参考的意义就变的非常有限。

通过过去参与过的复杂大型的iOS项目开发经验,开发新的模块的时候,总结出了一套完整的iOS内存自测的方法,通过Instruments来逐步跟踪检测我们创建和主动销毁的对象是否真的销毁了。

在演示之前,需要强调一件事情就是,以文件夹目录作为你的空间命名很重要,请遵循开发新模块的时候使用你的模块的命名作为前缀。因为iOS没有命名空间这个东西,通常为了保证不冲突,我们都是以项目前缀+空间命名来保证文件的独立性,空间名就是目录和模块的名字了。这个在我们使用instruments进行内存检测的时候是非常重要的,接下来的使用过程就可以证实这一良好的命名习惯所带来的巨大好处,不只是优雅,更重要的是帮助。

除了命名,还有一件很重要的事情就是,你需要对你的模块在各种UI操作或者事件发生过程的情况下,对你的对象分配过程要非常非常清楚,因为这样,你才能看出相应的变化,哪些对象是应该存在的,哪些对象在某个动作结束后是必定会销毁的,这个应该很容易,你应该纯天然的就知道,因为你开发的整个逻辑。

最近在开发一个新的模块,正好需要在内存方面做一个完整的自测,既要确保效率,也要确保内存的正确分配与释放。

重要:(以下教程开始所有示例图片,请自行点击图片看大图,看的倍儿清楚!)

  1. 首先,我先上图,看一下我的命名组织结构,遵循的就是模块化的命名,因为手Q的跨部门合作,所以用部门标记作为前缀。代码首先是要让人来看的,是人在维护程序,所以可读性非常重要,在开发完这些功能后,我对于所有对象在运行过程中的创建与销毁是很清楚的。
  2. 接下来我要进入instruments来进行内存测试,profile运行Leaks就OK了,就会进入下面看到的界面,详细讲解一下都是什么吧,这些对对象怎么分配内存的很重要。
  1. 然后我就需要操作模拟器来进入我所开发的功能模块,会看到非常复杂的对象分配情况,所以这一步非常关键,我只需要在搜索框搜索模块的前缀就可以只显示当前模块所涉及的对象分配与销毁情况,如下图,进入了的模块视图:
  2. 接下来我执行一个环境查询的命令,再看一下执行之后的内存分配情况。
  1. 我的逻辑是这样的,点击一次创建一个命令(VASDebugPlatformServerCmd),通过这个命令初始化并执行一个任务,任务结束后就销毁这个任务对象(VASDebugPlatformBaseOperation),相应的命令也作为任务的成员一起被销毁。所以,在动作执行完后我们应该可以在已销毁对象中找到这个实例,运行截图如下:
  2. 我看到任务是已经被销毁了的,可是用来初始化的命令对象为什么没有被销毁,我需要深挖一下这个命令对象(VASDebugPlatformServerCmd)的引用计数到底怎么发生变化的,就需要用到下面的步骤了,按照图解去深挖它:
  1. 我挖到命令对象的内部,一路挖到底,我发现命令对象最终的引用计数是1,证明它还在内存中活着,截图是这样的:
  2. 所以,我就看看,任务对象销毁了,那任务对象到底发生了什么事 截图是这样的:
  1. 我就是不死心,我就是要看到,到底是不是真的呢,为了进一步佐证命令对象在内存中,我在对象内部观察了一个内存检测的通知,收到通知后弹一个alert出来,如果对象被销毁了,它肯定收不到这个通知,如下面截图所示的工作:
  2. 因为这是我自己写的逻辑,我很清楚对象在哪里分配内存,然后我就去查代码,命令对象到底经历了什么,从开始到结束的执行过程是什么样的:
  1. 看完上面的截图,再去看命令对象的引用计数变化就知道为什么了:
  2. 当然,我也看出了为什么引用计数没有归零,所以,我在下面进行了修复:
  1. 再次profile->Leaks , 然后我再做一次查询任务,看看这次命令对象的引用计数变化,命令对象被释放了!:
    总结:
  2. 保持你的模块拥有一个良好的命名空间

  • 请深刻并且清楚的知道,触发什么事件,执行UI动作之后,你的对象分配会是什么样的,谁此刻应该存在内存中,谁应该被销毁,然后利用上面的原理去查看,它是不是被销毁了,如果没有被销毁,那么你应该去查你的代码,到底在执行过程中,哪里没有平衡引用计数。
  • 此方法在ARC和MRC的情况都是适用的,目标是观察具体哪个实例对象没有被销毁,然后根据引用计数变化跳转到代码中去确认哪里出现了内存问题。

文章来源于公众号:小时光茶社(Tech Teahouse)

相关推荐

App性能监控,拒绝用户流失

快速定位手游内存占用过高问题

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏技术之路

Go 单例模式[个人翻译]

  原文地址:http://marcio.io/2015/07/singleton-pattern-in-go/   最近几年go语言的增长速度非常惊人,吸引...

2649
来自专栏LEo的网络日志

coding感想(二)

3409
来自专栏CDA数据分析师

Python 快速教程(标准库)

Python有一套很有用的标准库(standard library)。标准库会随着Python解释器,一起安装在你的电脑中的。它是Python的一个组成部分。这...

1959
来自专栏Python专栏

Python | 使用进程池统计指定范围内素数的个数

(1)了解使用Python标准库multiprocessing编写多进程程序的方法。

1252
来自专栏铭毅天下

Elasticsearch聚合优化 | 聚合速度提升5倍!

? 1、聚合为什么慢? 大多数时候对单个字段的聚合查询还是非常快的, 但是当需要同时聚合多个字段时,就可能会产生大量的分组,最终结果就是占用 Elastic...

4937
来自专栏xingoo, 一个梦想做发明家的程序员

Oozie分布式工作流——流控制

最近又开始捅咕上oozie了,所以回头还是翻译一下oozie的文档。文档里面最重要就属这一章了——工作流定义。 一提到工作流,首先想到的应该是工作流都支持...

20210
来自专栏逸鹏说道

c# 温故而知新: 线程篇(一) 上

Thread 目录: 目录: 1 线程基础的简单介绍 2 线程同步与线程异步的简单介绍 3 前台线程与后台线程的简单介绍 4 细说下Thread 最为关键的构造...

2598
来自专栏尚国

深入剖析最新IE0day漏洞

在2018年4月下旬,我们使用沙箱发现了IE0day漏洞;自从在野外发现上一个样本(CVE-2016-0189)已经有两年多了。从许多方面来看,这个特别的漏洞及...

1032
来自专栏章鱼的慢慢技术路

运用模块方式写单片机程序

1496
来自专栏红色石头的机器学习之路

Jupyter notebook入门教程(下)

Jupyter notebook的入门教程第二部分的英文原文出处: Getting started with the Jupyter notebook (p...

2680

扫码关注云+社区