iOS 开发继续加油iOS面试总结面试

iOS - 性能优化

2019-05-12  本文已影响69人  valentizx
image.png

卡顿优化

在了解卡顿优化相关的前头,首先要了解 CPU 和 GPU。

CPU(Central Processing Unit,中央处理器)
对象的创建和销毁、对象属性的调整、布局计算、文本的计算和排版、图片的格式转换和解码、图像的绘制(Core Graphics)都是通过 CPU 来做的。

GPU(Graphics Processing Unit,图形处理器)
纹理的渲染、

image

所要显示的信息一般是通过 CPU 计算或者解码,经过 CPU 的数据交给 GPU 渲染,渲染的工作在帧缓存的地方完成,然后从帧缓存读取数据到视频控制器上,最终显示在屏幕上。

在 iOS 中有双缓存机制,有前帧缓存、后帧缓存,这样渲染的效率很高。

屏幕成像原理

我们所看到的动态的屏幕的成像其实和视频一样也是一帧一帧组成的。为了把显示器的显示过程和系统的视频控制器进行同步,显示器(或者其他硬件)会用硬件时钟产生一系列的定时信号。当电子枪换到新的一行,准备进行扫描时,显示器会发出一个水平同步信号(Horizonal Synchronization),简称 HSync;而当一帧画面绘制完成后,电子枪回复到原位,准备画下一帧前,显示器会发出一个垂直同步信号(Vertical Synchronization),简称 VSync。显示器通常以固定频率进行刷新,这个刷新率就是 VSync 信号产生的频率。

卡顿现象

卡顿成因

前面我们知道,完成显示信息的过程是:CPU 计算数据 -> GPU 进行渲染 -> 屏幕发出 VSync 信号 -> 成像,假如屏幕已经发出了 VSync 但 GPU 还没有渲染完成,则只能将上一次的数据显示出来,以致于当前计算的帧数据丢失,这样就产生了卡顿,当前的帧数据计算好后只能等待下一个周期去渲染。

解决办法

解决卡顿现象的主要思路就是:尽可能减少 CPU 和 GPU 资源的消耗。
按照 60fps 的刷帧率,每隔 16ms 就会有一次 VSync 信号产生。那么针对 CPU 和 GPU 有以下优化方案:
CPU

GPU

离屏渲染

在 OpenGL 中,GPU 有两种渲染方式:
On-Screen Rendering:当前屏幕渲染,在当前用于显示的屏幕缓冲区进行渲染操作;
Off-Screen Rendering:离屏渲染,在当前屏幕缓冲区外开辟新的缓冲区进行渲染操作;

离屏渲染消耗性能的原因:
离屏渲染的整个过程,需要多次切换上下文环境,先是从当前屏幕(On-Screen)切换到离屏(Off-Screen),渲染结束后,将离屏缓冲区的渲染结果显示到屏幕上,上下文环境从离屏切换到当前屏幕,这个过程会造成性能的消耗。

哪些操作会触发离屏渲染?

卡顿检测

这里的卡顿检测主要是针对在主线程执行了耗时的操作所造成的,这样可以通过 RunLoop 来检测卡顿:添加 Observer 到主线程 RunLoop 中,通过监听 RunLoop 状态的切换的耗时,达到监控卡顿的目的。

耗电优化

耗电的主要来源为:

优化思路

启动优化

App 的启动分为两种:冷启动(Cold Launch)热启动(Warm Launch)
前者表示从零开始启动 App,后者表示 App 已经存在内存中,在后台依然活着,再次点击图标启动 App。

App 启动的优化主要是针对冷启动的优化,通过添加环境变量可以打印出 App 的启动时间分析:Edit Scheme -> Run -> Arguments -> Environment Variables 添加 DYLD_PRINT_STATISTICS 设置为 1。

image.png

运行程序则会打印:


image.png

这里打印的是在执行 main 函数之前的耗时信息,若想打印更详细的信息则添加环境变量为:
DYLD_PRINT_STATISTICS_DETAILS 设置为 1。

image.png

App 冷启动

冷启动可分为三个阶段:dyld 阶段、Runtime 阶段、main 阶段。

第一个阶段就是处理程序的镜像的阶段,第二个阶段是加载本程序的类、分类信息等等的 Runtime 阶段,最后是调用 main 函数阶段。

dyld

dyld(Dynamic Link Editor),Apple 的动态链接器,可以用来装载 Mach-O 文件(可执行文件、动态库等)。

image.png

启动 App 时,dyld 会装载 App 的可执行文件,同时会递归加载所有依赖的动态库,当 dyld 把可执行文件、动态库都装载完毕后,会通知 Runtime 进行做下一步的处理。

Runtime

启动 App 时,调用 map_images 进行可执行文件的内容解析和处理,再 load_images 中调用 call_load_methods 调用所有 Class 和 Category 的 load 方法,然后进行 objc 结构的初始化(注册类、初始化类对象等)。然后调用 C++ 静态初始化器和 __attribute_((constructor)) 修饰的函数,到此为止,可执行文件的和动态库中所有的符号(类、协议、方法等)都已经按照格式加载到内存中,被 Runtime 管理。

main

在 Runtime 阶段完成后,dyld 会调用 main 函数,接下来是 UIApplication 函数,AppDelegate 的 application: didFinishLaunchingWithOptions: 函数。

启动优化思路

针对不同的阶段,有不同的优化思路:
dyld

虚函数和 Java 中的抽象函数有点类似,但区别是,基类定义的虚函数,子类可以实现也可以不实现,而抽象函数子类一定要实现。

Runtime

main

安装包瘦身

安装包(ipa)主要由可执行文件和资源文件组成,若不管理妥善则会造成安装包体积越来越大,所以针对资源优化我们可以将资源采取无损压缩,去除没用的资源。

对于可执行文件的瘦身,我们可以:

上一篇下一篇

猜你喜欢

热点阅读