iOS性能优化

(一)卡顿优化

【了解CPU和GPU】

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

  • CPU - Central Processing Unit,中央处理器,在iOS程序中,负责对象的创建和销毁、对象属性的调整、布局的计算、文本的计算和排版规格、图片的格式转码和解码、图像的绘制(Core Graphic
  • GPU - Graphics Processing Unit,图形处理器,负责纹理的渲染。如果没有接触过OpenGL的朋友,可能不太好理解纹理渲染这个概念,我们知道,屏幕上面的物理元件是像素,我们在屏幕上面看到的图片,文字,视频,就是由屏幕上的所有像素,通过控制色值变化而呈现出来的。那么像素的色值数据,就是由GPU计算得出的,然后将这些数据提交给视频控制器,由它负责显示到屏幕上。
image

iOS中采用的是双缓冲机制,分为前帧缓存和后帧缓存。

【屏幕成像原理】 屏幕成像原理是一个非常庞大的知识体系,这里仅介绍一下我们当前所需要了解的部分,以便我们接下来的话题。 屏幕的显示,是受控于两种信号
  • 垂直同步信号(VSync)

屏幕发出VSync之后,就表示将要进行新一帧画面的显示,于是开始从帧缓存里面读取经过GPU渲染好的用于显示的数据

  • 水平同步信号(HSync)

显示器从帧缓存里拿到数据之后,是从上到下一行一行的刷新的,刷新完一行,就发出一个HSync,直到最下面一层显示出来,这样,一帧的画面就完成了显示。

image

我们可以把屏幕想象成刷墙师傅,每一帧的数据就是桶里的油漆,而GPU就是负责提供油漆的店老板。

题库资料已上传到Github:https://github.com/Henry-ley/rest/blob/main/README.md内附整理的学习思维导图以及一些iOS资料。

【卡顿产生的原因】

我们手机屏幕的刷帧率是60FPS(Frame per Second 帧/秒),也就是会所1秒钟的时间,屏幕可以刷新60帧(次)。完成一帧刷新的用时是16.6毫秒。因此垂直同步信号VSync就是每16.6毫秒发出一次。

两次VSync之间的这16.6毫秒,就是被CPU和GPU共同完成下一帧画面的计算和渲染工作的时间。但是CPU计算和GPU渲染所用的时间是取决于任务的运算量的,因此就有可能大于16.6毫秒,也有可能小于或者等于16.6毫秒。

这里我们假设Tc=CPU计算时间,Tg=GPU渲染时间。如果Tc+Tg <= 16.6ms,那么完美,下一帧画面的数据可以在VSync到来之间就准备好;但是如果Tc+Tg > 16.6ms,意味着屏幕将要开始显示下一帧画面了,但是CPU和GPU那里却还在咔咔咔的准备着画面数据,那么没办法,在接下来的16.6ms周期里面,屏幕就继续用上一帧的画面数据来显示。同一个画面被显示了过长的时间,就造成了视觉上可感知到的卡顿现象。再通过下图来体会一下

画面卡顿的产生

【卡顿优化-CPU】

上看我们了解了产生的原因,就是由于CPU计算时间和GPU的渲染时间过长导致的。因此想要优化卡顿问题,无非就是从CPU和GPU下手,减轻它们的工作量,以控制它们的操作耗时。

首先开看看CPU,我们有如下途径来减轻它的计算任务

尽量用轻量级的对象,比如简单的数字,尽量选择基础数据类型,不要使用NSNumber,对象操作的开销肯定大于基础数据类型的开销。

CALayer是用来显示图像的,UIView是负责处理触摸交互事件的,UIView内部封装了CALayer属性,因此UIView的图像显示实际上是它内部的这个CALayer来完成的。因此如果我们不需要考虑触摸事件,只是单纯的要显示内容的话,可以考虑用CALayer取代UIView

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

图片的size最好是跟UIImageViewsize刚好一致,可以省去图片剪裁的操作开销

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

尽量把耗时操作放到子线程处理(比如文本的尺寸计算、绘制,图片的解码、绘制)

【卡顿优化-GPU】

对与GPU,有下列方案可以减少渲染开销

  • 尽量避免段时间内大量的图片显示,尽可能将多张图片合成一张图片显示,比如说三张图片同时显示,不如将这三张图片合成到一块作为一张图片来显示
  • GPU能处理的最大纹理尺寸是4096*4096,一旦超过这个尺寸,就会占用CPU资源进行处理,这样势必影响CPU的运算效率,因此纹理尽量不要超过这个尺寸
  • 尽量减少视图的数量和层级
  • 减少不必要的透明的视图
  • 尽量避免离屏渲染
【离屏渲染】

在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.shadowXXX

🥝如果设置了layer.shadowPath就不会产生离屏渲染🥝

【卡顿检测】

平时我们所碰到的卡顿,主要是在主线程执行了比较耗时的操作,可以在主线程RunLoop中添加observer,通过监听RunLoop的状态切换耗时,来监控卡顿。

(二)耗电优化

iOS 设备耗电的主要来源有一下几种
iOS设备耗电来源
  • CPU处理 Processing
  • 网络 Networking
  • 定位 Location
  • 图像 Graphics

耗电优化方案

  • 尽可能降低CPU、GPU功耗
  • 少用定时器
  • 优化I/O操作
    1. 尽量不要频繁的进行数据写入操作,最好批量一次性写入
    2. 读写大量重要数据时,考虑用dispatch_io,它提供了基于GCD的异步操作文件I/O的API。使用它时,系统会优化磁盘的访问
    3. 数据量比较大的话,建议使用数据库
  • 网络优化
    1. 减少、压缩网络数据
    2. 如果多次请求的结果是相同的,尽量使用缓存
    3. 使用断点续传,否则网络不稳定时可能会导致多次传输相同内容
    4. 网络不可用时,不要尝试执行网络请求
    5. 让用户取消长时间运行或者速度很慢的网络操作,设置合适的超时时间
    6. 批量传输,比如,下载视频流是,不要传输很小的数据包,直接下载整个文件或者一大块一大块下载。如果下载广告,一次性多下载一些,然后慢慢的拿出来展示。如果狭隘电子邮件,就一次下载多封,不要一封一封下载
  • 定位优化
    1. 如果只是需要确定用户的位置,最好用CLLocationManagerrequestLocation方法。定位完成后,会自动让定位硬件断电。
    2. 如果不是导航引用,尽量不要实时更新位置,定位完毕就关闭掉定位服务
    3. 需要后台定位时,尽量设置pauseLocationUpdateAutomaticallyYES,这样,如果用户不太可能移动的时候,系统就会自动暂停位置更新。
    4. 尽量不要使用startMonitoringSignificantLocationChanges,优先考虑startMonitoringForRegion:
  • 硬件检测优化:

用户移动、摇晃、倾斜设备时,会产生动作事件(motion),这些事件是由加速度计、陀螺仪、磁力传感器等硬件检测的。在不需要检测的场合,应该及时关闭这些硬件。

(三)启动优化

【App启动过程分析】

App的启动可以分为两种

  • 冷启动:从零开始启动App
  • 热启动:App已经在内存中,处在后台状态中,在次点击图标启动App

App的启动时间优化,主要是针对冷启动来进行优化的。那么首先我们就需要了解一下App的冷启动过程包含哪些步骤。

  • (一)dyld阶段:dyld1dynamic link editor),Apple的动态链接器,可用来加载Mach-O文件(可执行文件、动态库等等)。冷启动一个app之后,首先是dyld开始工作,它负责两件事情:
    1. 加载App的可执行文件,同时会递归加载所有依赖库的动态库。
    2. 当完成可执行文件和动态库的加载之后,就通知Runtime进行下一步处理。
  • (二)Runtime阶段:在这个阶段,Runtime做了如下的工作:
    1. 调用map_images函数对可执行文件的内容进行解析和处理。
    2. load_images函数中调用call_load_methods,以调用所有Class和Category的+load方法。
    3. 进行各种objc结构的初始化(例如objc类的注册,初始化类对象等等)
    4. 调用C++静态初始化器以及被_attribute_((constructor))修饰的函数

到此为止,可执行文件和动态库中的所有所有Symbols(符号,包括 Class, Protocol, Selector, IMP等等)都已经按照规定格式加载到内存中,并且被runtime所管理

  • (三)main函数阶段

小结

  • App的冷启动由dyld主导,将可执行文件加载到内存,顺便把所依赖的动态库也加载到内存,然后通知runtime进行相应处理
  • runtime负责将上面的内容初始化成`bjc定义的结构体
  • 最后当初始化工作完成后,dyld就会调用main函数。接下来便是main函数-->UIApplicationMain函数 -->didFinishLaunchingWithOptions方法
image
【App启动优化】

dyld阶段优化

  • 减少动态库,合并一些动态库,定期清理不必要的动态库
  • 减少Objc类、Category的数量,减少Selector数量,定期清理不再需要的ClassCategory
  • 减少C++虚函数的数量,因为虚函数会导致额外的虚表的存在
  • 如果是Swift尽量使用struct

Runtime阶段优化

  • +initialize方法+dispatch_once的组合来取代所有的 _attribute_((constructor))、C++静态构造器、Objc的+load方法。

main阶段优化

在不影响用户体验的前提下,尽可能讲一些操作延迟,不要全都放在didFinishLaunchingWithOptions 方法中。 ###(四)安装包瘦身 iOS的安装包(ipa)主要有可执行文件和资源组成 可执行文件——就是由我们iOS项目的代码经过编译连接产生的二进制文件,要想对可执行文件进行瘦身,有如下几种思路:

  • 编译器优化

Strip Linked ProductMake Strings Read-OnlySymbols Hidden by Default设置为YES 去掉异常支持,Enable C++ ExceptionsEnable Objective-C Exceptions设置为NOOther C Flags添加-fno-exceptions

  • 通过第三方工具AppCode检测项目中用不到的代码:菜单 --> Code --> Inspect Code

资源——包括我们iOS项目中的图片、音频、视频、storyboard等,对其进行优化的思路有

题库资料已上传到Github:https://github.com/Henry-ley/rest/blob/main/README.md内附整理的学习思维导图以及一些iOS资料。

作者:RUNNING_NIUER
链接:https://juejin.cn/post/6966859086008680455

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

推荐阅读更多精彩内容

  • (一)卡顿优化 【了解CPU和GPU】 在屏幕成像过程中,CPU和GPU的作用是至关重要的。 CPU - Cent...
    RUNNING_NIUER阅读 417评论 0 1
  • 一、UI卡优化 1.1、图像显示原理 CPU和GPU是通过总线连接起来的,CPU输出的结果往往是一个位图,经由总线...
    AlanGe阅读 1,058评论 0 0
  • 转自iOS 性能优化篇 好的 app 应该有好的性能流畅度,本篇文章就大概讲一下 ios 性能优化。 先来谈谈 C...
    shen888阅读 389评论 0 1
  • 处理器优化 CPU和GPU 在屏幕成像的过程中,CPU和GPU起着至关重要的作用CPU:(Central Proc...
    大冯宇宙阅读 2,709评论 0 24
  • 表情是什么,我认为表情就是表现出来的情绪。表情可以传达很多信息。高兴了当然就笑了,难过就哭了。两者是相互影响密不可...
    Persistenc_6aea阅读 123,969评论 2 7