ios性能优化实践

2023-05-16

本文将从原理出发,解释卡顿发生的原理,然后会讲解项目中行之有效的几个优化点,最后会展望一下接下来将要尝试的方向。下面进入正题。


屏幕显示的原理


基本原理


屏幕显示原理


我们知道,远古时代的CRT显示器的显示原理是用电子枪扫描荧光屏来发光。如上图所示,电子枪按照从左到右,然后从上到下的顺序扫描。当电子枪换到新的一行准备进行扫描时,也就是上图A4、B4、C4、D4的位置,显示器会发出一个水平同步信号;而当一帧画面绘制完成后,电子枪回复到原位准备画下一帧前,也就是上图D4的位置,显示器会发出一个垂直同步信号。垂直同步信号的作用一方面是通知显示器回到A1位置,另外一方面,也通知显卡,准备输出下一帧画面。现在已经是液晶显示器的时代了,不再使用电子枪扫描了,但是原理还是类似的,水平同步信号和垂直同步信号还是一样被使用的。


计算机工作原理


计算机系统的工作原理如上图所示:首先是CPU的工作,包括创建视图分配内存、计算布局、图片解码以及文本绘制等;接下来轮到GPU工作了,GPU负责视图变换、合成和渲染等;GPU渲染完提交到帧缓冲区中,等收到垂直同步信号后将帧缓冲区的内容显示到屏幕上。


屏幕撕裂(Screen tearing)


上述的简单的屏幕显示原理其实会产生这样一个问题:假设我们的显卡速度很快,每秒生产的帧数肯定要超过显示器刷新率。那么在实际数据处理过程中,缓冲区的数据,在被输出之前,就被显卡不断的刷新重写。但是缓冲区并不是“先清空再写入数据”,这太没有效率,而是采用“新数据覆盖老数据”的方式。

假设这样一种情况,缓冲区已经有一副完整的帧画面(A帧),然后显卡生成了下一帧画面(B帧),新一帧的数据开始写入缓冲区,写到一半的时候,垂直同步信号来 了,于是缓冲区的数据被输出到显示器。但问题是,这时缓冲区的数据,是由一半A帧和一半B帧数据合成的。因此最终显示器上显示出来的画面就不是一副完整的 画面,这就是“画面撕裂”现象出现的原因(如下图)。


屏幕撕裂


那怎么才能解决画面撕裂呢?简单来说只要让帧缓冲区里的数据始终保持一副完整的画面就可以了。从技术角度出发,其实就是利用刚刚提到的垂直同步信号。

具体说起来就是,当显卡生成了一副完整画面并写入了帧缓冲区之后,暂停!然后开始等待垂直同步信号,当得到垂直同步信号后,再继续渲染下一帧写入缓冲区。这样就可以保证在缓冲区的数据始终是一副完整的画面,不会出现前后帧混合的问题。


卡顿产生原因


但是呢,垂直同步带来了一个新的问题—掉帧。所谓的掉帧,跟垂直同步有一定关系,因为垂直同步机制决定了如果在一个时钟周期内CPU或者GPU没有完成各自的任务的话,就会将帧缓冲区里的内容直接丢弃!掉帧并不能完全怪罪于垂直同步机制,更重要的原因是我们作为开发者没有进行足够的优化,将过重的任务派发到了CPU或者GPU上,下图(from:iOS 保持界面流畅的技巧)是掉帧的图示,表明CPU或者GPU任意一个没能在时钟周期内完成自己的任务的话都会导致卡顿掉帧。


卡顿图示


行之有效的优化点


提前布局


提前布局可以说是最重要的优化点了。其实在从服务端拿到 JSON 数据的时候,关于视图的布局就已经确定了,包括每个控件的frame、cell的高度以及文本排版结果等等,在这个时候完全可以在后台线程计算并封装为对应的布局对象XXXTableViewCellLayout,每个cellLayout的内存占用并不是很多,所以直接全部缓存到内存中。当列表滚动到某个cell的时候,直接拿到对应的cellLayout配置这个cell的对应属性即可。当然,该有的计算是免不了的,只是提前算好并缓存,免去了在滚动的时候计算和重复的计算。通过这一个优化,将本来的fps50的列表优化到了55、56左右,可以说从肉眼上已经看不出有卡顿掉帧了。


cellLayout示例图


上图是项目中某个cellLayout的部分代码,可以看到里面存的就是所有控件的frame和文本的排版结果而已,里面没有任何的黑科技,只是将本来在滚动中才做的事情提前了而已。


按页加载缓存


  • 现状分析:90%的APP有tableview,90%的tableview里有上拉刷新和下拉加载。以我司的项目ZAKER中的热点新闻界面为例,简单流程大概是这样的:①应用启动的时候会将磁盘中所有的新闻一次性读取出来显示到屏幕上; ②在每次下拉刷新和上拉加载的时候会将内存中所有新闻缓存到磁盘中,也即全量读写。这意味着大部分的新闻数据会反复写入到磁盘中,这样的写入是冗余的,因为前面的这些新闻数据并没有发生改变。


  • 改进方案:所以优化的方法就是将这些列表数组进行分割,分割成一页一页,每次写入的数据量很小,而且避免了冗余写入的问题。现在的流程变为:①启动时只读取第一批新闻显示在屏幕中;②下拉刷新和上拉加载的时候只把当前服务器返回的一批新闻写入缓存中;③在上拉加载的时候会先查看磁盘中是否有未读的缓存,若有则读取缓存,否则才从服务器下载一批新的文章。


  • 直观图示:


按页缓存


可以看到,优化之前整个新闻列表以及其他配置都在一个文件里,刷新10几次之后文件大小达到2MB,并且随着不断刷新而越来越大;优化之后,其他的配置还是在刚刚的文件中,但是不断增长的新闻数组被分割成一页一页的文件,每一页里面有10多条的新闻数据,同时有一个configure文件保存这些页的信息以及页的顺序。根据测试人员的反馈,进行按页加载缓存优化能减少5%~8%的CPU占用,使用的内存也有一定的下降。还是有很明显的优化效果的。


后台线程处理图片


圆形头像、图片裁圆角等处理可以说是非常常见的需求了,包括从iOS11的系统各处都能看到,整体的页面控件都变得更加圆润了。但是,对图片处理必然是消耗资源的,实现过图片圆角效果的应该都知道,最简单的就是 layer.cornerRadius+layer.masksToBounds 的方式,但是这种做法在tableview中往往会使滚动变得卡顿,因为这种实现方式会触发离屏渲染,屏幕外缓冲区跟当前屏幕缓冲区上下文切换是很耗性能的,所以离屏渲染往往会造成卡顿(参考:iOS 离屏渲染的研究)。

那要怎么处理图片呢?可以使用Core Graphics,Core Graphic通常是线程安全的,所以可以进行异步绘制,显示的时候再放回主线程。我们在项目中实现了一个后台处理图片的框架,核心代码如下:


NSBlockOperation *transformOperation = [[NSBlockOperation alloc] init];

       [transformOperation addExecutionBlock:^{        

           // 此处处理图片

           ...

           dispatch_async(dispatch_get_main_queue(), ^{

                    // 主线程设置图片

                   [self setImage:transformedImage forState:UIControlStateNormal];

               }

           });

       }];


更加高效的控件


还可以直接从开源库中选用更加高效的控件替换项目中性能没那么好的控件。项目中将之前的TTTAttributedLabel、M80AttributedLabel全部替换为YYLabel,开启YYLabel的displaysAsynchronously、ignoreCommonProperties属性可以异步绘制文本以及忽略不需要的属性。更加追求性能的话,可以结合第1点的提前布局机制,在提前布局的阶段生成好YYLabel渲染时用到的textLayout,显示的时候直接赋值textLayout就可以了。


其他


还有一些比较微小的优化,对性能可以说没有多大的影响,但是可以在开发阶段稍加留意,养成良好的习惯。


  • 尽量减少视图层级,合并多余的视图。同样以 ZAKER 为例,用户显示时的蓝V标签、达人标签以及楼主图片等几个视图,之前是用不同的view来展示的,优化过程将这几个view合并为一个view,一个view管理这些相似的事物,也可以减少某些相同逻辑的代码。


  • 减少频繁的addSubview、removeSubview,remove之后视图的实例对象会被释放,再add的时候会再次调用初始化函数。替代方案的话,可以用hidden属性隐藏不显示的视图。


接下来的方向


异步绘制


从一开始接触iOS的我们就反复被告知,UIKit的东西是绝对不能在后台线程调用的,一定得在主线程调用,所以主线程也被叫做UI线程。在后台线程调用UIKit的东西有一定几率导致崩溃,或者出现视图不显示、显示错乱等等问题。但是呢,根据刚刚所说的,Core Graphics的那一套东西是线程安全的,所以可以通过Core Graphics在后台将视图渲染到一张图片上,显示的时候在主线程将这张图片设置到相应位置上。Facebook著名的AsyncDisplayKit的核心实现应该也是基于这个原理,接下来的优化可以尝试这个方案。


Metal


根据Apple官方说法,Metal框架被设计用来实现两个目标: 3D 图形渲染和并行计算。这两者有很多共同点。它们都在数量庞大的数据上并行运行特殊的代码,并可以在GPU上执行。目前正在研究学习阶段,看项目中是否能利用Metal进行一定的优化。


APM?


Application Performance Management(APM):应用程序性能管理, 通过对应用的可靠性、稳定性等方面的监控,进而达到可以快速修复问题、提高用户体验的目的。目前比较有代表性的 APM 产品有:听云、阿里百川、腾讯 bugly等,现在也在考虑自己研发一套APM系统,先从比较简单的指标入手,先对卡顿和崩溃这两个指标着手,做的顺利的话再逐步扩展别的指标的检测管理。



以上。






















参考:https://www.jianshu.com/p/c83cb4eec901



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ios性能优化实践 的相关文章

随机推荐

  • qt/qtcreator在Arm架构下的安装

    环境 xff1a 银河麒麟系统 xff0c linux arm架构 1 安装zlib zlib下载地址 xff1a http www zlib net 解压后 xff0c 看里面README文件 xff0c 编译安装分为几步骤 xff1a
  • sqlite bolb数据的写入和存储

    1 什么是BLOB数据 BLOB binary large object 即二进制大对象 xff0c 是一种可以存储二进制文件的容器 在计算机中 xff0c BLOB常常是数据库中用来存储二进制文件的字段类型 常见的BLOB文件有图片 声音
  • 计算机视觉基础大纲

    计算机视觉思维导图大纲 1 GPU的概念 什么是GPU呢 xff1f 首先说一下什么是GPU xff0c GPU Graphics Processing Unit 即图形处理器 xff0c 又称显示核心 xff0c 也就是显卡的核心 xff
  • kqueue示例

    网络服务器通常都使用epoll进行异步IO处理 xff0c 而开发者通常使用mac xff0c 为了方便开发 xff0c 我把自己的handy库移植到了mac平台上 移植过程中 xff0c 网上居然没有搜到kqueue的使用例子 xff0c
  • AI写代码 GitHub Copilot + Idea 安装和使用教程

    文章目录 一 GitHub Copilot订阅服务二 Idea安装Copilot插件三 插件使用四 参考文档 GitHub Copilot 是微软与OpenAI共同推出的一款AI编程工具 xff0c 基于GitHub及其他网站的源代码 xf
  • 5GC 网元AMF、SMF、AUSF、UPF、PCF、UDM、NRF、NSSF、NEF介绍

    AMF Access and Mobility Management Function xff0c 接入和移动性管理功能 xff0c 执行注册 连接 可达性 移动性管理 为UE和SMF提供会话管理消息传输通道 xff0c 为用户接入时提供认
  • windows 下wsl 启动不了

    win10 升级到了dev 版本 xff0c 安装了16 10 0版本的visual studio 2019 后 xff0c 重启启动 xff0c 发现wsl 启动不了了 xff0c docker desktop 不能用了 尝试了下面的一波
  • 算法---判断101-200之间有多少个素数,并输出所有素数。

    算法 判断101 200之间有多少个素数 xff0c 并输出所有素数 题目 xff1a 判断101 200之间有多少个素数 xff0c 并输出所有素数 程序分析 首先明白什么是素数 xff0c 只能被1和本身整除的数 xff0c 用循环遍历
  • Tesseract-OCR 字符识别---样本训练

    转自 xff1a http blog csdn net feihu521a article details 8433077 Tesseract是一个开源的OCR xff08 Optical Character Recognition xff
  • centos fluxbox 安装,个性化配置

    centos fluxbox 安装 个性化配置 张映 发表于 2010 12 01 分类目录 xff1a linux 我用的第一个linux系统是archlinux xff0c 当时装的桌面是fluxbox xff0c 界面简单 xff0c
  • 算数移位(<<, >>)与逻辑移位

    lt lt gt gt 是算数移位 xff0c 也就是 1 xff0c 如果右移 xff0c 则 最高位补1 左移 xff0c 则右边补0 但是uint32 t的10 00 00 00 右边移动后 xff0c 是什么 xff1f 补0呗 x
  • OCR数据集

    1 SynthText in the Wild dataset 数据集下载链接 xff1a http www robots ox ac uk vgg data scenetext 数据集介绍 xff1a 一个综合生成的数据集 xff0c 其
  • Linux(centos)下搭建confluence

    1 下载文件 2 将下载的文件放到 opt路径下 通过上图可以看出confluence安装到了 opt atlassian confluence和 var atlassian application data confluence目录下 x
  • gitlab中文社区

    1 获取gitlab中文社区项目 中文社区版项目 xff1a https gitlab com xhang gitlab 2 克隆中文仓库 git clone https gitlab com xhang gitlab git 3 查看gi
  • 阿里云 oss python3 例子

    阿里云的oss SDK又是不支持python3 xff0c 头疼头疼 本想改一改它的SDK xff0c 让它支持python2 43 python3 xff0c 无奈里面大量的代码使用不带括号的print xff0c 工作量恐怖 幸好oss
  • 【C语言课程设计】利用结构体数组实现文件内容的增删改查

    啦啦啦 xff0c 又到了学习时间 xff0c 现在给大家介绍结构体数关于文件内容的增删改查 我们先把相关代码写好 xff0c 再使用EGE xff0c 慢慢来不着急 一 文件的打开方式 假设文件名叫list txt 首先我们要建立一个文件
  • Mysql中limit的用法详解

    在我们使用查询语句的时候 xff0c 经常要返回前几条或者中间某几行数据 xff0c 这个时候怎么办呢 xff1f 不用担心 xff0c mysql已经为我们提供了这样一个功能 SELECT FROM table LIMIT offset
  • 在Windows10上安装CodeSoft 2015,系统蓝屏,解决办法

    最近公司电脑更新换代 xff0c 替换成Windows 10系统 xff0c 在安装CodeSoft 2015 标签软件时 xff0c 电脑蓝屏重启 xff0c 软件安装失败 xff0c 且无法卸载 xff0c 之后只能重装系统 在经过系统
  • Win10 WSL下 Ubuntu20.04 安装与卸载 Mysql 8.0

    一 查询并卸载旧的mysql xff08 未安装可以直接跳过 xff09 查看MySQL的依赖项 dpkg list grep mysql 关闭mysql服务 service mysql stop 自动卸载mysql xff08 包括ser
  • ios性能优化实践

    本文将从原理出发 xff0c 解释卡顿发生的原理 xff0c 然后会讲解项目中行之有效的几个优化点 xff0c 最后会展望一下接下来将要尝试的方向 下面进入正题 屏幕显示的原理 基本原理 屏幕显示原理 我们知道 xff0c 远古时代的CRT