iOS - 性能优化

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

  • 尽量用轻量级的对象 如:不用处理事件的 UI 控件可以考虑使用 CALayer;
  • 不要频繁地调用 UIView 的相关属性 如:frame、bounds、transform 等;
  • 尽量提前计算好布局,在有需要的时候一次性调整对应属性,不要多次修改;
  • Autolayout 会比直接设置 frame 消耗更多的 CPU 资源;
  • 图片的 size 和 UIImageView 的 size 保持一致;
  • 控制线程的最大并发数量;
  • 耗时操作放入子线程;如文本的尺寸计算、绘制,图片的解码、绘制等;

GPU

  • 尽量避免短时间内大量图片显示;
  • 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 = YESlayer.cornerRadius > 0
    • 可以用 CoreGraphics 绘制裁剪圆角
  • 阴影
    • 如果设置了 layer.shadowPath 不会产生离屏渲染

卡顿检测

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

耗电优化

耗电的主要来源为:

  • CPU 处理;
  • 网络请求;
  • 定位;
  • 图像渲染;

优化思路

  • 尽可能降低 CPU、GPU 功耗;
  • 少用定时器;
  • 优化 I/O 操作;
    • 尽量不要频繁写入小数据,最好一次性批量写入;
    • 读写大量重要数据时,可以用 dispatch_io,它提供了基于 GCD 的异步操作文件的 API,使用该 API 会优化磁盘访问;
    • 数据量大时,用数据库管理数据;
  • 网络优化;
    • 减少、压缩网络数据(JSON 比 XML 文件性能更高);
    • 若多次网络请求结果相同,尽量使用缓存;
    • 使用断点续传,否则网络不稳定时可能多次传输相同的内容;
    • 网络不可用时,不进行网络请求;
    • 让用户可以取消长时间运行或者速度很慢的网络操作,设置合适的超时时间;
    • 批量传输,如下载视频,不要传输很小的数据包,直接下载整个文件或者大块下载,然后慢慢展示;
  • 定位优化;
    • 如果只是需要快速确定用户位置,用 CLLocationManagerrequestLocation 方法定位,定位完成后,定位硬件会自动断电;
    • 若不是导航应用,尽量不要实时更新位置,并为完毕就关掉定位服务;
    • 尽量降低定位精度,如不要使用精度最高的 KCLLocationAccuracyBest
    • 需要后台定位时,尽量设置 pausesLocationUpdatesAutomatically 为 YES,若用户不怎么移动的时候,系统会自暂停位置更新;

启动优化

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

  • 减少动态库、合并动态库,定期清理不必要的动态库;
  • 减少类、分类的数量,减少 Selector 的数量,定期清理不必要的类、分类;
  • 减少 C++ 虚函数数量;
  • Swift 开发尽量使用 struct;

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

Runtime

  • inilialize 方法和 dispatch_once 取代所有的 __attribute_((constructor))、C++ 静态构造器、以及 Objective-C 中的 load 方法;

main

  • 将一些耗时操作延迟执行,不要全部都放在 finishLaunching 方法中;

安装包瘦身

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

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

  • 从编译器层面优化

    • Strip Linked ProductMake Strings Read-OnlySymbols Hidden by Default 设置为 YES;
    • 去掉异常支持,Enable C++ ExceptionsEnable Objective-C Exceptions 设置为 NO,Other C Flags 添加 -fno-exceptions
    • 利用 AppCode,检测未使用代码检测:菜单栏 -> Code -> Inspect Code;
    • 编写 LLVM 插件检测重复代码、未调用代码;
    • 通过生成 LinkMap 文件检测;

    LinkMap

    Build Setting -> LD_MAP_FILE_PATH: 设置文件路径Build Setting -> LD_GENERSTE_MAP_FILE -> YES

    image

    运行程序可看到:
    image.png

    打开可看见各种信息:


    image.png

    我们可根据这个信息针对某个类进行优化。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容

  • 好的app应该有好的性能流畅度,本篇文章就大概讲一下ios性能优化。 先来谈谈CPU和GPU 在屏幕成像的过程中,...
    slimsallen阅读 1,152评论 0 3
  • 随着项目功能的不断增加和扩展,如果你的代码没有经过严格的调试和优化,我相信你的app一定出现过一些卡顿现象要么就是...
    Jack_Baiyx阅读 644评论 0 0
  • 一、卡顿优化 在屏幕成像的过程中,CPU和GPU起着至关重要的作用。CPU(Central Processing ...
    伶俐ll阅读 746评论 0 1
  • 前言 iOS 性能优化是个不变的专题,他涉及到各个方面的内容,从启动时间到代码规范,再到屏幕渲染等等。首先我们简单...
    咖啡豆8888阅读 612评论 0 11
  • 愁客登临,怅望旧山河,城池环岭。一湾水道,三津港口,十里绝崖横亘。暗想当初,送别地、寒砧流影。从分难舍,恁...
    刘小地阅读 2,043评论 31 118