iOS性能优化

/ 5

CPU和GPU

在屏幕成像的过程中,CPU 和 GPU 起着至关重要的作用。

CPU(Central Processing Unit,中央处理器)

GPU(Graphics Processing Unit,图形处理器)

在 iOS 中是双缓冲机制,有前帧缓存、后帧缓存。

卡顿解决的主要思路就是尽可能减少 CPU、GPU 资源消耗。

屏幕成像原理

当电子束完成一帧的扫描,将要从头开始扫描时,就会发出一个 垂直同步信号,然后发出多个 水平同步信号

只有当视频控制器接收到 VSync 之后,才会将帧缓冲器中的位图更新为下一帧,进而将帧缓存中的数据传输给显示器。

卡顿产生原因

在收到 VSync 后,CPU 计算 和 GPU 渲染还未处理完,就会导致丢帧(掉帧),屏幕上还是显示上一帧的数据。

卡顿优化

CPU

尽量用轻量级的对象,比如用不到事件处理的地方,可以考虑使用 CALayer 取代 UIView

不要 频繁地 调用 UIView 的相关属性,比如 frameboundstransform 等属性,尽量减少不必要的修改。

尽量提前计算好布局,在有需要时一次性调整对应的属性,不要多次修改属性。

Autolayout 会比直接设置 frame 消耗更多的 CPU 资源。

图片的 size 最好刚好跟 UIImageViewsize 保持一致。

控制一下线程的最大并发数量。

尽量把耗时的操作放到子线程,比如:

GPU

尽量避免短时间内大量图片的显示,尽可能将多张图片合成一张进行显示。

GPU 能处理的最大纹理尺寸是 4096x4096,一旦超过这个尺寸,就会占用 CPU 资源进行处理,所以纹理尽量不要超过这个尺寸。

尽量减少视图数量和层次。

减少透明的视图(alpha<1),不透明的就设置 opaqueYES

尽量避免出现 离屏渲染

离屏渲染

OpenGL 中,GPU 有 2 种渲染方式:

离屏渲染消耗性能的原因:

哪些操作会触发离屏渲染?
光栅化:layer.shouldRasterize = YES

遮罩:layer.mask

圆角:同时设置 layer.masksToBounds = YESlayer.cornerRadius 大于 0。考虑通过 CoreGraphics 绘制裁剪圆角,或者叫美工提供圆角图片。

阴影:layer.shadowXXX,如果设置了 layer.shadowPath 就不会产生离屏渲染。

卡顿检测

平时所说的 卡顿 主要是因为在主线程执行了比较耗时的操作。

可以添加 Observer 到主线程 RunLoop 中,通过监听 RunLoop 状态切换的耗时,以达到监控卡顿的目的。

具体流程

创建一个 CFRunLoopObserverContext 观察者。

将创建好的观察者添加到主线程 RunLoopkCFRunLoopCommonModes 模式下观察。

创建一个持续的子线程专门用来监控主线程的 RunLoop 状态。

一旦发现进入睡眠前的 kCFRunLoopBeforeSources 状态,或者唤醒后的状态 kCFRunLoopAfterWaiting,在设置的时间阈值内一直没有变化,即可判定为卡顿。

dump 出堆栈的信息,从而进一步分析出具体是哪个方法的执行时间过长。

RunLoop 所有状态:

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry , // 进入 loop
    kCFRunLoopBeforeTimers , // 触发 Timer 回调
    kCFRunLoopBeforeSources , // 触发 Source0 回调
    kCFRunLoopBeforeWaiting , // 等待 mach_port 消息
    kCFRunLoopAfterWaiting ), // 接收 mach_port 消息
    kCFRunLoopExit , // 退出 loop
    kCFRunLoopAllActivities  // loop 所有状态改变
}

开源的卡顿监控代码:https://github.com/UIControl/LXDAppFluecyMonitor

耗电优化

iOS 中耗电主要来源于下面4个方面:

耗电优化具体实施

尽可能降低 CPU、GPU 功耗

少用定时器

优化 I/O 操作

网络优化

定位优化

硬件检测优化

APP的启动优化

APP 的启动可以分为2种:

APP 启动时间的优化,主要是针对冷启动进行优化。

通过添加环境变量可以打印出 APP 的启动时间分析(Edit scheme -> Run -> Arguments
DYLD_PRINT_STATISTICS 设置为 1

如果需要更详细的信息,那就将 DYLD_PRINT_STATISTICS_DETAILS 设置为 1

冷启动过程

APP 的冷启动可以概括为3大阶段:dyldruntimemain

dyld

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

启动 APP 时,dyld 所做的事情有:

runtime

启动 APP 时,runtime 所做的事情有:

到此为止,可执行文件和动态库中所有的符号 (ClassProtocolSelectorIMP,…) 都已经按格式成功加载到内存中,被 runtime 所管理。

main

APP 的启动由 dyld 主导,将可执行文件加载到内存,顺便加载所有依赖的动态库。

并由 runtime 负责加载成 objc 定义的结构。

所有初始化工作结束后,dyld 就会调用 main 函数。

接下来就是执行 UIApplicationMain 函数,AppDelegateapplication:didFinishLaunchingWithOptions: 方法。

冷启动优化

dyld 阶段:

runtime 阶段:

main 阶段:

安装包瘦身

安装包(IPA)主要由可执行文件、资源组成。

资源(图片、音频、视频等)

可执行文件瘦身

利用 AppCode 检测未使用的代码:菜单栏 -> Code -> Inspect Code。

编写 LLVM 插件检测出重复代码、未被调用的代码。

LinkMap

生成 LinkMap 文件,可以查看可执行文件的具体组成,检查每个类占用空间大小。

Build Settings -> Write Link Map File 设置为 YES 即可生成 LinkMap 文件。

可借助第三方工具解析 LinkMap 文件:https://github.com/huanxsd/LinkMap