iOS - 剖析性能优化相关
性能优化的几个点:
1.卡顿优化
在了解卡顿优化相关的前头,首先要了解 CPU 和 GPU。
CPU(Central Processing Unit,中央处理器)
对象的创建和销毁、对象属性的调整、布局计算、文本的计算和排版、图片的格式转换和解码、图像的绘制(Core Graphics)都是通过 CPU 来做的。
GPU(Graphics Processing Unit,图形处理器)
纹理的渲染、
所要显示的信息一般是通过 CPU 计算或者解码,经过 CPU 的数据交给 GPU 渲染,渲染的工作在帧缓存的地方完成,然后从帧缓存读取数据到视频控制器上,最终显示在屏幕上。
在 iOS 中有双缓存机制,有前帧缓存、后帧缓存,这样渲染的效率很高。
屏幕成像原理
卡顿成因
解决办法
按照 60fps 的刷帧率,每隔 16ms 就会有一次 VSync 信号产生。那么针对 CPU 和 GPU 有以下优化方案:
尽量用轻量级的对象 如:不用处理事件的 UI 控件可以考虑使用 CALayer;不要频繁地调用UIView
的相关属性 如:frame、bounds、transform 等;尽量提前计算好布局,在有需要的时候一次性调整对应属性,不要多次修改;Autolayout
会比直接设置 frame 消耗更多的 CPU 资源;图片的 size 和 UIImageView 的 size 保持一致;控制线程的最大并发数量;耗时操作放入子线程;如文本的尺寸计算、绘制,图片的解码、绘制等;
- 尽量避免短时间内大量图片显示;
- GPU 能处理的最大纹理尺寸是 4096 * 4096,超过这个尺寸就会占用 CPU 资源,所以纹理不能超过这个尺寸;
- 尽量减少透视图的数量和层次;
- 减少透明的视图(alpha < 1),不透明的就设置 opaque 为 YES;
- 尽量避免离屏渲染;
离屏渲染
在 OpenGL 中,GPU 有两种渲染方式:
On-Screen Rendering:当前屏幕渲染,在当前用于显示的屏幕缓冲区进行渲染操作;
Off-Screen Rendering:离屏渲染,在当前屏幕缓冲区外开辟新的缓冲区进行渲染操作;
离屏渲染消耗性能的原因:
离屏渲染的整个过程,需要多次切换上下文环境,先是从当前屏幕(On-Screen)切换到离屏(Off-Screen),渲染结束后,将离屏缓冲区的渲染结果显示到屏幕上,上下文环境从离屏切换到当前屏幕,这个过程会造成性能的消耗。
光栅化,layer.shouldRasterize = YES
遮罩,layer.mask
圆角,同时设置layer.masksToBounds = YES
,layer.cornerRadius > 0
阴影
- 可以用 CoreGraphics 绘制裁剪圆角
- 如果设置了
layer.shadowPath
不会产生离屏渲染
卡顿检测
耗电优化
1.CPU 处理;
2.网络请求;
3.定位;
4.图像渲染;
优化思路
1.尽可能降低 CPU、GPU 功耗;
2.少用定时器;
3.优化 I/O 操作;
尽量不要频繁写入小数据,最好一次性批量写入;
读写大量重要数据时,可以用 dispatch_io,它提供了基于 GCD 的异步操作文件的 API,使用该 API 会优化磁盘访问;
数据量大时,用数据库管理数据;
4.网络优化;
减少、压缩网络数据(JSON 比 XML 文件性能更高);若多次网络请求结果相同,尽量使用缓存;使用断点续传,否则网络不稳定时可能多次传输相同的内容;网络不可用时,不进行网络请求;让用户可以取消长时间运行或者速度很慢的网络操作,设置合适的超时时间;批量传输,如下载视频,不要传输很小的数据包,直接下载整个文件或者大块下载,然后慢慢展示;
5.定位优化
如果只是需要快速确定用户位置,用CLLocationManager
的requestLocation
方法定位,定位完成后,定位硬件会自动断电;若不是导航应用,尽量不要实时更新位置,并为完毕就关掉定位服务;尽量降低定位精度,如不要使用精度最高的KCLLocationAccuracyBest
;需要后台定位时,尽量设置pausesLocationUpdatesAutomatically
为 YES,若用户不怎么移动的时候,系统会自暂停位置更新;
启动优化
App 的启动分为两种:冷启动(Cold Launch) 和热启动(Warm Launch)。
前者表示从零开始启动 App,后者表示 App 已经存在内存中,在后台依然活着,再次点击图标启动 App。
App 启动的优化主要是针对冷启动的优化,通过添加环境变量可以打印出 App 的启动时间分析:Edit Scheme -> Run -> Arguments -> Environment Variables 添加 DYLD_PRINT_STATISTICS
设置为 1。
这里打印的是在执行 main
函数之前的耗时信息,若想打印更详细的信息则添加环境变量为:DYLD_PRINT_STATISTICS_DETAILS
设置为 1。
App 冷启动
冷启动可分为三个阶段:dyld 阶段、Runtime 阶段、main 阶段。
第一个阶段就是处理程序的镜像的阶段,第二个阶段是加载本程序的类、分类信息等等的 Runtime 阶段,最后是调用 main 函数阶段。
dyld
启动 App 时,dyld 会装载 App 的可执行文件,同时会递归加载所有依赖的动态库,当 dyld 把可执行文件、动态库都装载完毕后,会通知 Runtime 进行做下一步的处理。
Runtime
map_images
进行可执行文件的内容解析和处理,再 load_images
中调用 call_load_methods
调用所有 Class 和 Category 的 load
方法,然后进行 objc 结构的初始化(注册类、初始化类对象等)。然后调用 C++ 静态初始化器和 __attribute_((constructor))
修饰的函数,到此为止,可执行文件的和动态库中所有的符号(类、协议、方法等)都已经按照格式加载到内存中,被 Runtime 管理。main
在 Runtime 阶段完成后,dyld 会调用 main 函数,接下来是 UIApplication 函数,AppDelegate 的 application: didFinishLaunchingWithOptions:
函数。
启动优化思路
针对不同的阶段,有不同的优化思路:
dyld
1.减少动态库、合并动态库,定期清理不必要的动态库;
2.减少类、分类的数量,减少 Selector 的数量,定期清理不必要的类、分类;
3.减少 C++ 虚函数数量;
4.Swift 开发尽量使用 struct;
虚函数和 Java 中的抽象函数有点类似,但区别是,基类定义的虚函数,子类可以实现也可以不实现,而抽象函数子类一定要实现。
Runtime
用 inilialize 方法和 dispatch_once 取代所有的 __attribute_((constructor))、C++ 静态构造器、以及 Objective-C 中的 load 方法;
main
将一些耗时操作延迟执行,不要全部都放在 finishLaunching 方法中;
安装包瘦身
安装包(ipa)主要由可执行文件和资源文件组成,若不管理妥善则会造成安装包体积越来越大,所以针对资源优化我们可以将资源采取无损压缩,去除没用的资源。
对于可执行文件的瘦身,我们可以:
1.从编译器层面优化
1.Strip Linked Product、Make Strings Read-Only、Symbols Hidden by Default 设置为 YES
2.去掉异常支持,Enable C++ Exceptions、Enable Objective-C Exceptions 设置为 NO,Other C Flags 添加 -fno-exceptions;
3.利用 AppCode,检测未使用代码检测:菜单栏 -> Code -> Inspect Code;
4.编写 LLVM 插件检测重复代码、未调用代码;
5.通过生成 LinkMap 文件检测;
LinkMap
Build Setting -> LD_MAP_FILE_PATH: 设置文件路径 ,Build Setting -> LD_GENERSTE_MAP_FILE -> YES
运行程序可看到:
打开可看见各种信息:
我们可根据这个信息针对某个类进行优化。
摘自链接:https://www.jianshu.com/p/fe566ec32d28