【一】应用性能分析报表:
1、cpu使用率
真机调试时,查看运行状态标签,CPU一栏:最高使用,最低使用,平均使用
Instruments——timerProfile :CPU使用率详情
2、线程状态
查看运行状态标签,CPU一栏:app总计开启线程数
转到Instruments,操作app,可以看到当前活动线程,并可以看到创建这个线程的调用详情
3、内存使用率
真机调试时,查看运行状态标签,memory一栏:动态监测内存状态
Instruments——Activity Monitor :内存使用详情
4、磁盘使用情况
真机调试时,查看运行状态标签,disk一栏:动态监测磁盘使用情况,读取,写入,文件打开数
5、网络使用流量
Instruments——NetWork
动态监测上传和下载的数据流量
6、url响应时间采样分析
在响应页面查看听云打印log
7、电量消耗
Instruments——energyDiagnostics
8、GPU使用情况
Instruments——GPU Driver [Device utilization]
9、帧率+页面结构和渲染
Instruments——CoreAnimation
10、内存申请情况
Instruments——Allocation
11、内存泄漏
Instruments——Leaks
见【二】
12、操作执行时间追踪
Instruments——timerProfile
见【二】
【二】定位性能问题
上文提到的众多工具中,一部分只能展示出app运行时的性能数据,这一部分工具没有定位问题到具体的代码调用点的能力,一般来说对我们调试性能和debug来说比较重要的工具有以下几个:
1、leaks——检查内存泄露(基本使用)
使用方法:
1、暂停程序
2、观察leaks有无内存泄露,用标尺选择需要产生内存泄露的时间区域
3、选择calltree,查看代码调用树
4、选择反转调用栈+隐藏系统库调用两个选项(使detail区域展示更加清晰)
5、观察detail区,此时应该能发现我们自己编写的方法调用 6、查看调用栈,直接锁定到具体出问题的代码上
2、timerProfile (基本使用)
时间分析器工具用来检测CPU的使用情况。它可以告诉我们程序中的哪个方法正在消耗大量的CPU时间。
使用方法:
1、暂停程序
2、用标尺选择需要分析的时间区域
3、选择calltree,查看代码调用树
4、选择反转调用栈+隐藏系统库调用两个选项(使detail区域展示更加清晰)
5、观察detail区,此时应该能发现我们自己编写的方法调用 6、查看调用栈,直接锁定到具体出问题的代码上
3、Allocation + Activity Monitor——检查内存申请
使用方法:
1、定位造成内存增长的操作;观察Activity Monitor,看内存是否稳定,如果内存不能稳定在一个特定的区间,而是一直增长,则说明出现了只申请不释放的问题
2、切换到Allocation,等之前操作全部结束,在没有干扰的情况下,开启分段检测,重复执行并且只执行步骤1定位到的操作,会发现堆的内存增长不为0(如果全部堆内存都被合理的释放掉了,那么每一段的堆内存增长量应该是0)。
3、在detail窗口中选择只执行了步骤1定位到的操作 的这段detail,开启calltree,查看调用栈,锁定这一次操作中所有可能申请内存的方法
4、屏蔽掉无关的代码,只保留带有步骤1定位到的操作 的代码,执行程序,如果还有内存不释放的问题,说明定位准确,然后根据实际情况解决问题
5、invite call tree 选择之后,会在details区中展示调用栈最里层的方法,不方便查找,所以不勾选
6、在Generation标签(选择call tree 的地方)中,理想状态是每个generation区间相对于前一个generation区间的内存增长(memory growing)是0,如果不为0,则说明发生了内存可以申请但不能释放的问题。
注意:用户操作的方法是在main里体现的,所以找问题的时候要先把main点开,从main的下拉方法列表中定位出现内存问题的方法(分享时一直没有操作成功的原因)
4、CoreAnimation + GPU Driver + timerProfile——检查图层问题
使用方法:
GPU占用过高,需要通过调整视图渲染方式解决,通过以下几种视图渲染选项调试:
Color Blended Layers - 这个选项基于渲染程度对屏幕中的混合区域进行绿到红的高亮(也就是多个半透明图层的叠加)。由于重绘的原因,混合对GPU性能会有影响,同时也是滑动或者动画帧率下降的罪魁祸首之一,红色越深,说明叠加层数越多。
ColorHitsGreenandMissesRed - 当对一个View使用shouldRasterizep属性的时候,耗时的图层绘制会被缓存,然后当做一个简单的扁平图片呈现。我对栅格华的理解是:将矢量图绘制为为像素图,并进行缓存,滑动时,能看见每部分视图都会被染成红或绿两种颜色,绿色部分是不需要缓存的,红色部分是正在创建缓存。如果滑动的时候,红色区域不停闪烁,就说明是缓存正在频繁创建,这说明栅格化可能会有负面的性能影响。
Color Copied Images - 有时候寄宿图片的生成意味着Core Animation被强制生成一些图片,然后发送到渲染服务器,而不是简单的指向原始指针。这个选项把这些图片渲染成蓝色。复制图片对内存和CPU使用来说都是一项非常昂贵的操作,所以应该尽可能的避免。
Color Immediately - 通常Core Animation Instruments以每毫秒10次的频率更新图层调试颜色。对某些效果来说,这显然太慢了。这个选项就可以用来设置每帧都更新(可能会影响到渲染性能,而且会导致帧率测量不准,所以不要一直都设置它)
Color Misaligned Images - 这里会高亮那些被缩放或者拉伸以及没有正确对齐到像素边界的图片(也就是非整型坐标)。这些中的大多数通常都会导致图片的不正常缩放,如果把一张大图当缩略图显示,或者不正确地模糊图像,那么这个选项将会帮你识别出问题所在。
Color Offscreen-Rendered Yellow - 这里会把那些需要离屏渲染的图层高亮成黄色。这些图层很可能需要用shadowPath或者shouldRasterize来优化。
Color OpenGL Fast Path Blue - 这个选项会对任何直接使用OpenGL绘制的图层进行高亮。如果仅仅使用UIKit或者Core Animation的API,那么不会有任何效果。如果使用GLKView或者CAEAGLLayer,那如果不显示蓝色块的话就意味着你正在强制CPU渲染额外的纹理,而不是绘制到屏幕。
Flash Updated Regions - 这个选项会对重绘的内容高亮成黄色(也就是任何在软件层面使用Core Graphics绘制的图层)。这种绘图的速度很慢。如果频繁发生这种情况的话,这意味着有一个隐藏的bug或者说通过增加缓存或者使用替代方案会有提升性能的空间。
我们平时主要关注的是Color Blended Layers和Color Offscreen-Rendered Yellow
一般我们的程序写完之后,使用CoreAnimation + timeProfile + GPUDriver 这三个工具测试一下,如果平均帧率在55+FPS,且GPU和CPU都没有过多占用,就没有必要进行视图优化
视图性能优化的原则就是平衡CPU与GPU的工作,二者任何一个占用率过多都会导致界面卡顿
CPU占用通过TimerProfile工具的查看 running time 确定
GPU占用通过GPUDriver工具查看Renderer Utilization 和 Tiler utilization 确定
Instruments视图调优深度好文:http://www.cocoachina.com/ios/20150429/11712.html
使用方法:
点击Analyze选项,等待编译通过,查看编译结果标签,可能出现问题的代码都会出现在编译结果标签里
Analyze主要分析以下四种问题:
1、逻辑错误:访问空指针或未初始化的变量等;
2、内存管理错误:如内存泄漏等;
3、声明错误:从未使用过的变量;
4、Api调用错误:未包含使用的库和框架。
建议编码完毕之后,先使用Analyze做最初步的代码漏洞检查
【三】杂记
【1】tableView中,cell中的图片优先使用PNG格式的
(虽然苹果支持其他格式,但是对PNG格式支持程度最佳)
【2】tableView中,如果cell高度由内容相关,需要缓存高度以提高滑动效率
(这样不需要每次滑动都计算一遍高度)
【3】tableView中,cell高度固定,使用tableView.rowHeight = 100 这种方式设置cell高度
(这样不会频繁调用height代理方法,效率最优)
【4】tableView中计算高度的方法苹果原生估算或FD_TemplatLayoutCell分类
(二者互有优缺点:苹果原生估算的加载时间和滑动效果都比较完美,但不支持ios6;FD_TemplatLayoutCell分类相比纯代码计算高度效率有明显提升,加载速度不如苹果原生方法,总结:自适应高度就用xib布局+FD_TemplatLayoutCell)
【5】页面中少用动态添加View,而是在初始化时全部画好,控制是否隐藏或者视图切换实现动态效果
(原则就是把子控件利用加载的时间一次创建好,不要在用户操作的同时进行视图的申请操作,view是很大的对象,创建它会消耗较多资源,并且也影响渲染的性能)
【6】View层级扁平化,少用圆角,阴影,透明等
(这些是需要GPU大量运算渲染的)
【7】加载图片的时候需要使用多线程+缓存
(多线程保证UI线程流畅,缓存避免重复加载)
【8】自行绘制cell
(因为GPU是计算墨迹型的,所以在使用直接绘制的方法会显著的提升渲染速度,理论上这样得到的fps接近最大,方法是通过复写drawRecr方法绘制自己的cell,然后在每个元素中调用不同的方法绘制每个UI元素)
【9】离屏渲染
离屏渲染是指GPU一边在当前屏幕上进行绘制,而另一边在屏幕还没有处理图像信息之前通过CPU来生成图像信息的处理过程,离屏渲染会在以下情况下触发:
1、Core Graphics(任何以CG开头的类)
2、在drawRect方法里的操作,甚至是空的drawRect都会引起离屏渲染
3、所有使用了mask(setMasksToBounds)和动态阴影(setShadow*)的CALayer对象
(尽量避免离屏渲染,如果必须进行离屏渲染,就将渲染后的结果缓存起来)
为什么离屏渲染会浪费性能:http://www.tuicool.com/articles/2iYbMfE
【10】开销比较大的对象,例如dateFormatter,需要重用
(如果每次使用都创建消耗太大,所以要只创建一次,在内存缓存起来,重用)
【11】UIKit中的类和大多数CG、CA、Foundation的类,所以必须在主线程使用
(因为它们不是线程安全的)
【12】处理内存警告,如果一个界面有多个sheet,纪录当前sheet,当发生内存警告时,将出了当前展示sheet之外的所有sheet置为nil
(这样可以在用户没察觉不到的情况下释放大量内存)
【13】不要在UserDefault中保存图片等大数据
(UserDefault本质上是一个plist文件,保存数据的同时被反序列化)
【14】磁盘,网络,Lock,dispatch_sync等耗时操作不要在主线程中做,要异步进行耗时操作,并在内存允许的情况下做内存缓存
(参考sd_webImage)
【15】如果tableView中少数cell的model更新了,不要重新加载整个TableView,而是用带section和indexPath的方法只reload发生变化的那几个cell
(采用局部reload避免对没做修改的model进行刷新)
推荐大家看看《iOS应用性能调优的25个建议和技巧》这篇文章
【四】性能问题的处理流程
[1]发现和重现问题
[2]利用Instruments工具剖析
[3]假设出现问题的原因
[4]改进代码和设计
上述四个步骤循环反复,直到问题解决
Instruments中的众多工具是解决程序性能问题和漏洞的协助手段,一般情况下不会只执行一次就能精准定位问题,需要多次循环反复,不断测试直到问题解决。
【五】对性能优化的看法
[1]Instruments这样的优化工具需要学会使用,出现问题时,可以利用它们协助定位问题。
[2]优化时为了解决影响用户体验的问题的,不出现用户体验上的问题,就不要优化。