Instruments Leaks性能检测
前言
Memory Leaking, 顾名思义就是内存泄漏,作为一个iOS开发者,这是必须引起我们高度重视的性能之一,一款优秀的app在内存泄漏上也必须尽量做到尽善尽美, 才能发挥出最淋漓尽致的性能体验。在ARC出来之前,Memory Leaking更是显得尤为重要,因为我们必须做到 “谁retain,谁release” 的原则,也即是我们需要手动去管理内存,这样就需要在开发过程中特别小心,诚然,这样做不仅浪费了developer大量的时间,甚至一不小心就会因为某块内存泄露而导致app的Crash和溢出, app性能不好,用户体验效果差,运营成本高...因此,充分利用和管理好内存十分重要! 幸运的是,iOS 5以后, Apple推出了 ARC( Automatic Reference Counting), 这在一定程度上提高了developer的开发效率,节省了开发时间和成本,但是,尽管ARC给我们在内存上带来了巨大的便利,一不小心,依旧会存在一些内存泄露的情况, 比如一些Closure的使用, 对self的持有,某些第三方了使用不当等都会导致一序列的Memory Leaking问题。因此,如果你想要你的app拥有一个良好的性能,那么,你有必要用心了解甚至深入学习和使用Instruments来检测Memory Leaking, 开门见山, 直奔主题。
</br>
实验测试环境
- Xcode 8.3
- Simulator iPhone 7 Plus
- Instruments / Leaks
</br>
Instruments的种类
目前,性能检测工具Instruments主要如下
instruments leaks开始Instruments Leaks
Leaks内存泄露 的主页面打开方式有三种:
第一种方式
- 打开 Xcode --> Open Developer Tool --> Instruments --> Leaks
第二种方式
- 直接在Xcode使用快捷键 : command + i 或者 Xcode --> Product --> Profile
出来是这个样子(和第一种方式的第二张图一样):
test3第三种方式
- 打开Xcode项目工程, 先运行你的项目( command + R ), 然后点击左上导航栏目:show the debug navigator --> Memory --> Profile in Instruments --> transfer
通过以上任何一种方式,第一种和第二种方式需要点击Choose
按钮,第三种方式当你点击 transfer
的时候就直接进入到 Instruments / Leaks 检测性能界面了, 如下图:
Leaks监测参数设置释义
test6</br>
相应属性(设置)见下表
序号 | 属性名 | 释义 |
---|---|---|
1 | record | 开始录制按钮,是个红色的小圆点。当你点击之后,就表示开始检测 memory leaks 了。 默认只显示了一个icon图标,你也可以单机右键,会出现 Icon and Text 和 Icon Only , 默认就是 Icon Only , 如果你选择了 Icon and Text , 它就会显示图片和文本, 这样调试看起来会清晰点,当然,如果你很熟悉 Instruments 的使用,那只看Icon也是可以的。 |
2 | pause | 暂停按钮。正在录制的过程中,你可以点击该按钮选择暂停检测,暂停之前的监测的内存泄露记录不变。 |
3 | device | 设备名称,你可以选择模拟器(simulator),也可以选择真机。 |
4 | target | 所要监测的工程项目,也就是你要监测的可能会有 memory leaks 的项目。 |
5 | Library | 产生当前调用的库名。也就是你所选择的监测性能工具的小模块,点击后, 会出现:Leaks、Core Animation、Allocations等等 |
6 | Show the CPU data | 显示当前CPU运行数据 |
7 | Show the Instruments data | 显示当前运行的检测数据。 |
8 | Show the Thread data | 显示当前运行的线程数据 |
9 | 是否收起底部运行结果区域 | 可以选择是否隐藏运行结果,也就是捕捉到内存泄露的结果显示 |
10 | 是否显示右边运行结果详细 | 可以选择是否隐藏右边运行结果详细,可以看到内存泄露的线程、文件等 |
11 | 内存分配情况监测工具 | 这个工具是默认就有的,每个项目都有内存分配情况,所以我们可以选择是否查看 |
12 | 内存泄露监测工具(Leaks) | 这个工具也是我们本文所要讲的,内存泄露,当运行项目,开始监测(record)数据的时候,选择这个就好了 |
好了, 到此为止, 性能检测界面的上半身说明了一下(当然还有运行的时候怎么查看检测情况,稍后再说), 但是下面的一些设置属性和用法我们并不清楚, 接着看下图:
test7注: 上图是基于已经在运行监听内存泄露的状态下截图!
序号 | 属性名 | 释义 |
---|---|---|
13 | new leaks |
内存泄露。这个红色醒目的 x 号是关键之处, 因为它代表着这里有内存泄露,当出现这样的红色叉号标志时,我们用鼠标选中该标志的两边,表示选中内存泄露区域,就可以查看内存泄露的状态,泄露的内存占用了多少字节?百分比?定位在哪个文件?哪句或哪段代码? |
14 | no new leaks | 表示没有内存泄露。 与序号13相反,只要这里不出现红色x号 ,很好理解, 这就代表未出现内存泄露,所以不用管! |
15 | 监听的时间间隔 | 内存泄露或者未出现内存泄露的时间段,我们一般是以 10 秒为单位,如果你的mac性能好,可以选择5秒的间隔,但是系统默认就是10秒,推荐默认设置! |
16 | Details | 监听的详细,鼠标点击一下,会出现3个选项:Details、Console(控制台)、Run issues, 这里一般只要选择Details了,用不着选择Console和Run issues, 这里也不做讲解,只要知道不必要就行了,当然感兴趣的也可以去研究下 |
17 | Call Tree | 调用树。我们要选择的就是这个,当然,你点击也会出现3个选项:Leaks 、Cycles & Roots 、Call Tree , 但是一般我们只要选择 Call Tree 就好, 因为它显示的更加清晰,以树的结构展示可以看出内存泄露的层级关系,并且找出相应的内存泄露出现在哪里? Leaks显示的通常是 Leaked Object (内存泄露对象)、Address (内存泄露的地址)等等,这些我们一般是不需要的,除非你自己想研究深一点,而现在我们的目的是找出 memory leaks在哪里?Cycles & Roots可以查看对象的引用计数等,大致同理,有兴趣自己去看。 |
18 | Bytes Used | 相应symbol对象使用掉的字节量。百分比代表内存泄露的可能性,比如 100% 代表一定存在内存泄露 |
19 | #Leaks | 当前调用所检测到的内存泄露bytes大小,比如 1 就代表泄露了1个字节内存 |
20 | Symbol Name | 调用名(在哪个方法中调用的,点击可逐级深入,一直到main函数入口) |
21 | Snapshots | 快照。这就是设置监测内存泄露的时间间隔,你可以在这里设置时间间隔为多少,一般默认是10秒,电脑性能好的可以设置5秒等等 |
22 | Call Tree | 调用树的设置,鼠标点击会出现Separate by Thread、Invert Call Tree、Hide System Libraries、Flatten Recursion, 一般我们要勾上Invert Call Tree 、Hide System Libraries , 其余不用管 |
23 | Call Tree Constraints | 调用树约束。不用设置,当然,你也可以设置#Leaks 或者Bytes 的Min 、Max 来过滤内存泄露。 |
24 | Data Mining | 数据挖掘。有兴趣自己去研究。 |
25 | Extended Detail | 这里是显示内存泄露的文件位置和路径详细,双击这里就可以直接定位到内存泄露的位置或者可能出现内存泄露的地方, 当然, 双击序号20对应的下面文件也可以定位! |
26 | run info | 运行信息。不用管,一般没用。 |
27 | Hide system call in the stack trace | 隐藏堆回溯的系统调用。这样可以是查看内存泄露的状态简洁、清晰一点,但是只针对Leaks 和Cycles & Roots , 因为我们一般选择的是 Call Tree , 所以不用管! |
到此为止, 性能检测的界面大致介绍完毕,相信你也对检测的使用有一个基本的认识,现在我们来实际操作一下,主要针对如何判断内存泄露,如何定位内存泄露的位置?然后去做性能优化
为了方便理解,这里以一个简单循环引用为例:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let dog = Dog()
let cat = Cat()
dog.variableCat = cat
cat.variableDog = dog
}
}
class Cat {
var variableDog: Dog!
}
class Dog {
var variableCat: Cat!
}
我们知道也很熟悉上面的这段代码,就是一个因为循环引用而造成的内存泄露,这种情况也是在项目当中不经意间就犯的错误,所以要特别当心!
运行Xcode, 使用快捷键command + i
进入到内存泄露监测的主界面,点击红色的record
录制按钮开始监测内存情况,当然还要选择好前面说的设置便于定位内存泄露的位置,结果如下:
然后我们选择出现红色x
号的区域,查看内存泄露的地方:
test9
定位到代码的地方:
图一:
test10图二:
test11图三:
test12现在,从上面的图中标注的内存泄露的信息,我们也定位到了Leaks相应的位置,知道了内存泄露的地方,这样,通过 Instruments 的 Leaks 工具, 我们成功地检测到了内存泄露,那么问题来了,现在我们找到了内存泄露的地方,那肯定要优化代码,避免内存泄露的情况,接着我们对代码做了一些优化,如下:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let dog = Dog()
let cat = Cat()
dog.variableCat = cat
cat.variableDog = dog
}
}
class Cat {
// !!! 这里我们加了一个 weak 关键字, 来弱引用 对象 variableDog
weak var variableDog: Dog!
}
class Dog {
var variableCat: Cat!
}
然后再用 Instruments Leaks
来检测一下情况,见下图:
test13
开心吧,看一下内存引用图:
test14</br>
小结
Instruments的功能异常强大,除了内存泄漏
Memory Leaks
外,还有着许多的监测小工具,见本文的第一张图, 都可以帮助我们优化自己的代码,建立起优良的用户体验,这也是Apple的良心之作,作为一名开发者,多了解、多学习性能优化方面的知识特别重要!但是,顺便提一下本文的内存泄露性能检测,这个检测定位也不是绝对的准确,有时候尽管我们想在Xcode中定位到内存泄露的具体代码位置,但是往往不能,有时候只会定位到存在内存泄露的某一个片段,所以,这就要求你在代码设计优化方面有着敏锐的洞察分析能力,有着深厚的专业知识,你才能优化一些比较深层次的内存泄露!!!注:本文的有些属性理解,解释得不是那么的全面,甚至有可能你理解起来有些模棱两可,但是没关系,欢迎留言共同探讨学习! 另外,你也可以自己实际操作,我相信你可以更好的理解,毕竟毛主席曾经说过:“实践是检验真理的唯一标准!”我希望我们作为合格的 Developer engineer, 不要眼高手低,多动手,读万卷书不如行万里路!
</br>
最后: 有不足的地方是难免的,请多多指出,作者一定虚心学习请教! 赠人玫瑰,手留余香~~
</br>
欢迎加入 iOS(swift)开发互助群:QQ群号: 558179558, 相互讨论和学习!