iOS UI掉帧和卡顿优化解决方案记录

UI卡顿原理

在 VSync 信号到来后,系统图形服务会通过 CADisplayLink 等机制通知 App,App 主线程开始在 CPU 中计算显示内容,比如视图的创建、布局计算、图片解码、文本绘制等。随后 CPU 会将计算好的内容提交到 GPU 去,由 GPU 进行变换、合成、渲染。随后 GPU 会把渲染结果提交到帧缓冲区去,等待下一次 VSync 信号到来时显示到屏幕上。由于垂直同步的机制,如果在一个 VSync 时间内,CPU 或者 GPU 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是界面卡顿的原因。

从上面的图中可以看到,CPU 和 GPU 不论哪个阻碍了显示流程,都会造成掉帧现象。所以开发时,也需要分别对 CPU 和 GPU 压力进行评估和优化。

卡顿原因分析

1、 通过工具检查

  • 通过Xcode 检查视图层级关系是否有异常 ------ 未发现视图层级设置不合理的问题
  • 通过instruments 的Animation Hitch工具检测:------ 碰到问题,录制完了无数据(用iphone7录制有数据的,iphne14录制无数据?),iphone7检测结果发现掉帧确实很频繁

  • 用模拟器检查是否有离屏渲染 ------- 确实有离屏渲染问题

2、看代码分析

  • 复用问题
    • cell上的lottie动画组件没有复用;
    • cell上有大量的removeFromSuperView导致列表滑动过程中频繁的视图添加和删除,同时本来只需要更新显示内容的控件也没有得到复用;
  • GPU离屏渲染的问题
    • 阴影、圆角设置的方式 ------ 确有圆角设置导致的离屏渲染
    • 光栅化设置 ------ 无
    • 设置了组透明度为 YES,并且透明度不为 1 的layer ------ 无
    • 使用了高斯模糊等 ------ 无
  • 渲染耗时
    • 图片体积是否还有压缩的空间 ------ 商品图片和店铺头像确实有进一步裁减压缩的空间
    • 滑动过程中停止lottie动画
    • 渐变图层换成图片 ------ 减少GPU渲染耗时
  • 检查是否有线程不合理问题
    • 主线程耗时任务,比如调用UIGraphicsGetCurrentContext等接口在 CPU 上进行绘制计算 ------ 无
    • 主线程等待繁忙的子线程或低优先级的后台线程任务而导致阻塞,或者在滑动过程中主线程有耗时方法 ------ 往下滑动加载下一页数据时,确实有耗时的操作放在主线程
    • 主线程等待系统资源,比如使用Data(contentsOf:)进行 IO 读取等 ------ 无
  • 内存占用过度:列表一直往下刷新内存会爆掉,可能存在内存泄漏,需要进一步看看
    • 可以用内存工具检测看看有没有异常
    • 看代码里有没有循环引用等引起内存问题的代码
    • 缓存是否不合理

优化步骤和测试点

第一阶段 ------ 10.5.60

优化点:

  • 对列表Cell的顶部的播放组件重构优化
    • 优化代码结构;
    • 避免在刷新数据的时候添加和删除界面元素;
    • 优化左上角lottie动画的加载方式,避免每次刷新数据都更新Lottie动画,达到复用的目的;

第二阶段 ------ 10.5.66

优化点

  • 双列Cell底部组件反复的添加和删除逻辑优化;
  • 解决离屏渲染问题;
  • 商品图片和介绍图预加载并裁减来减少体积(图片从100~200Byte,减少到几个Byte);
  • 解决滑动过程中停止播放器定时器生效导致播放器一直在播放引起的掉帧;
  • 渐变背景图层换成图片;
  • 加载下一页数据代码中主线程耗时方法,尽量放到非主线程的异步线程;
  • 滑动过程中,停止lottie动画;
  • 优化获取size的逻辑:内部控件固定的size计算好后缓存下来;
  • 减少frame的设置刷新;
  • 播放器代码从UI组件抽取出来;
  • 解决一个历史遗留bug;
  • 显示封面图时增加渐显动画

优化总结

优化项总结
掉帧和卡顿相关优化
  • 修改Cell上UI控件反复的添加和删除逻辑;
    • cell是复用的,已经添加在cell的控件,如果刷新数据时需要隐藏的使用hidden而不是重复remove/add(这样修改测试重点要看下会不会因为cell的复用导致数据显示没有更新过来);
    • lottie的url获取和创建优化:url是从config server获取的,之前是每次刷新数据都获取,改成创建控件的时候获取一次;创建之前也是每次刷新数据的收获都重新创建,改成只创建一次;
  • 解决离屏渲染问题;
  • 商品图片和介绍图预加载并裁减来减少体积(图片从100~200Byte,减少到几个Byte);
  • 解决滑动过程中停止播放器定时器生效导致播放器一直在播放引起的掉帧;(这是个程序Bug,是造成滑动列表过程中掉帧很重要很隐蔽也是个很干扰排查的原因!)
  • 渐变背景图层换成图片;
  • 排查加载下一页数据代码中主线程耗时方法,尽量放到非主线程的异步线程;
  • 滑动过程中,停止lottie动画;
  • 优化获取size的逻辑:内部控件固定的size计算好后缓存下来;
  • 减少frame的设置刷新;

体验优化
  • 显示封面图时增加渐显动画

其他优化
  • 播放器代码从UI组件抽取出来;
  • 相同模版合并;

改善结果

改善前:无论是下拉加载数据,还是数据加载完毕往上滑动列表,都有明显掉帧和卡顿现象

改善后:往上滑动或者数据加载完毕再往下滑动,完全不会掉帧和卡顿

遗留问题

往下加载数据偶尔会出现掉帧现象。

分析可能的原因是往下滑动加载下一页数据时,接口数据回调回来后调用框架的CSCollectionViewDataManager类的addCardInstances方法在主线程耗时27ms左右导致掉帧。

心得体会

  • 因为引起掉帧的因素是多方面的,一开始无从查起;可以先把最明显的一些优化点先改好,再慢慢用排除法去定位哪些模块引起问题的,从而再进一步针对的排查;
  • 重构完一个单元,进行一次自测;
  • 从明显的关键问题入手分析改造;
  • 采用排除法排除有问题的地方能快速定位问题,但是要注意排除干扰因素;比如排除抓包干扰,抓包的时候界面会变的更加卡顿掉帧,所以记得关掉antbox或者删除掉抓包相关的依赖库;
  • 排查过程中,确保单一场景。比如列表往下滑和往上滑其实是不同的场景(往下拉时会触发加载更多数据等一系列的动作),应该分别去分析两个场景下引起掉帧的原因。

其他杂记

排查法检查卡顿点过程记录

单元模块排除法找出引起掉帧的主要因素

场景:

1、往下拉

2、往上(回)拉

前提:注释播放器play和stop逻辑;注释起播加速;

步骤

1、注释topview所有组件,只留下bottomview

测试滑动效果:

往下滑动时,仍然会掉帧

网上滑动,不掉帧

结论: 

1、可能商品图片加载引起掉帧

2、加载新数据引起掉帧

进一步测试:注释商品图片加载代码

测试滑动效果:

往下滑动加载新数据,还是会有些许掉帧 —— 加载新数据有可能存在阻塞主线程的方法

往上滑动不掉帧

2、 放出_introView(图片和渐变背景先隐藏)

测试滑动效果:

往下滑动,加载新数据有些掉帧;

往上滑动,基本不掉帧;再次往下滑动,也基本不掉帧了;

3、 放出tagview(lottie先隐藏)

测试滑动效果:同上

4、 放出tagview的lottie

说明:

1、 lottie的url从config server取的,初始化控件的时候取一次

2、 lottie的创建(调用框架的方法,异步获取, 并开启了缓存(降级场景只能取到静态图?))

测试滑动效果:同上

5、 放出_introView的渐变背景

测试滑动效果:同上

6、 释放封面图预加载代码

测试滑动效果:同上; 感觉往下加载新数据时,卡顿更严重一些,等分析加载新数据卡顿原因时再进一步分析;

7、 释放goodsview图片

测试滑动效果:同上;

8、 释放_introView图片加载

测试滑动效果:

往下加载新数据会掉帧;

往上滑动也出现了偶尔掉帧;

9、重新隐藏_introView图片加载再测试一遍

测试滑动效果:往回拉时基本不掉帧。

得出结论:同时只显示一张图片时,往回拉基本不掉帧。增加一张图片往回拉出现掉帧。

10、只加载封面图,隐藏其他图片加载

测试滑动效果:同2

11、释放_introView图片加载

测试滑动效果:掉帧不明显

12、释放goodsview图片加载

测试滑动效果:往回掉帧

13、goods和intro的图片都预加载

Goods预加载前224b -> 预加载 14b

Intro预加载前104b -> 预加载 8b

接下来,逐步分析加载新数据掉帧原因

方法:从日志台打印的关键日志入手,找到往回拉和往下拉日志差别;找到了didTriggerPaging方法;从这个方法入手终于揪出了两个主线程耗时方法

1、addCardInstances 耗时20多ms

2、preHandleCardInstances 平均耗时4~6ms

遗留的问题

  • 记得看下预览的cell是否有离屏渲染问题
  • 预加载图片两次,调查一下原因(打了两次log);
  • 加载出错时,重新加载逻辑貌似现在没有;
  • 启播加速放到异步线程?— 发现不影响
  • reloadWithThemeConfigVO应该没有必要每加载一次都去拉取,可以优化
  • addCardInstances耗时是否真的影响列表渲染?
  • 播放器的播放会掉帧,注释掉播放器,完全不掉帧 —— 播放器没有停止播放,会引起掉帧
  • 往下加载数据:注释掉预加载图片的代码还是会掉帧,说明预加载图片不是引起掉帧的原因?
  • MLPLiveRecommendView里也有个image,但是这样的数据很少,没必要放到预加载图片

结论一二

解决播放器引起的掉帧问题(滑动过程中没有停止播放)

发现的问题

已解决:

  • cell上的lottie动画组件没有复用
  • cell上有大量的removeFromSuperView可能导致卡顿,代码可以优化(是为了避免数据重复,在prepareForReuse里使用,可以换成在这里刷新数据???)
  • 圆角设置的方式导致离屏渲染

未解决:

  • 滑动结束,一堆的cell调用了stop
  • feeds接口请求参数exclContentIds随着分页数增加,数组越来越大,是必要传的吗
  • 一直刷新,内存爆掉闪退(可能不是这个模块的问题)

重构风险点
  • 1、preview类型数据没有自测过,记得mock数据自测
  • 2、有recommend的数据也没自测过
  • 3、为了本地跑真机在工程文件里去掉了一个purchase的勾选,并提交了
  • 4、开关mock数据测试一下开关是否生效
  • 5、测试一下开关是在什么场景下生效(每次进入直播广场?)
  • 6、偶现刷着刷着闪退

待解决的问题
  • 1、找到检测卡顿的工具 —— instruments的animation hitch 用iphone7(iOS14)可以录制

新学到的知识点
  • 同一父视图重复调用addSubviews添加同一个View并不会产生多层级
  • 测试性能时不要链接抓包工具(可删除APMockData库减少干扰);最好断开debug链接;
  • 三板斧:可监控(日志、埋点)、可灰度(发布阶段)、可回滚(开关控制)
  • mPaas框架
  • ARC下block不需要copy,因为属性是strong,系统会将block自动拷贝到堆区,就不会有block捕获的变量值不确定的问题了
  • alpha属性

1、 =0时,不会从响应链中移除

2、更改默认是有动画的

3、 hidden隐藏性能更高

4、会影响自己和subview的透明度

  • opaque: 给绘图系统提供一个性能优化开关

1、UIView当有背景颜色时:并且背景颜色有透明度(透明度不为1时),将 opaque设置为YES性能较高。

2、UIVIew有背景颜色时:并且背景颜色的透明度为1,opaque的值不影 响性能。

3、UIVIew没有背景颜色时:opaque的值不影响性能。

  • CALayer的opacity

耗时点
  • 卡顿检测工具使用测试
  • 代码熟悉过程,Cell模版注入原理(多亏师兄的指点,缩短了理解时间)
  • 每修改完一个组件,需要花大量时间mock数据模拟各种场景和参数自测
  • 采用排除法测试是哪个组件导致卡顿时,注册掉一个加载图片的组件,发现流畅很多;但是分析代码不知道什么原因导致卡顿,然后一顿排除测试:
    怀疑是imageview的背景色设置MLP_C_FFFFFF_NODARK导致的
    怀疑是图片资源的问题,更换了资源确实卡顿好很多
    但是,最后发现断开antbox连接后卡顿好很多(可删除APMockData库减少干扰)
  • 编译瓶颈
    • Xcode调试成为瓶颈(模拟器看离屏渲染有时跑不起来;修改一次代码,编译要好几分钟)
    • 发现注释代码后,编译时间变长;
    • 取消注释的代码,编译时间也很长;
    • 什么代码也没改,重新编译时间也很长;看来编译时间可能跟网络环境有关(同样的电脑,同样的工程,不一样的网络环境)

后记

参考文档

Instruments性能检测
Instruments性能检测 - 简书

Animation Hitch:
iOS 底层原理39:Instruments系列(三)Animation Hitches - 简书
iOS渲染流程和卡顿分析工具-Animation Hitches - 掘金
iOS 性能检测新方式​——AnimationHitches - 知乎

iOS卡顿检测:
iOS卡顿检测 - 简书

UICollectionView复用:
UICollectionView缓存机制探究 - 简书
2016笔记——UICollectionView复用 - 简书

dispatch_sync_on_main_queue:
如何安全使用dispatch_sync - 简书

圆角设置:
iOS 设置圆角、指定位置圆角及 iOS 11圆角设置 - 简书

离屏渲染:
iOS 离屏渲染探究 - 掘金
iOS圆角的离屏渲染,你真的弄明白了吗 - 掘金

iOS 渲染原理解析:

iOS 渲染原理解析

iOS 离屏渲染原理及优化(很有参考价值):

[转] iOS离屏渲染原理及优化 · Tenloy's Blog

设置圆角性能测评:

iOS设置圆角的4种方法实例(附性能评测)

卡顿原理和解决方案:iOS 深入理解列表卡顿原理和滑动优化方案_苹果手机上的列表滑动问题-CSDN博客

图片的渲染过程与优化(非常有用):iosiOS图片的渲染过程与性能优化 - 简书

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/325302.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

教你用通义千问只要五步让千年的兵马俑跳上现代的科目三?

教你用五步让千年的兵马俑跳上现代的舞蹈科目三? 上面这个“科目三”的视频,只用了一张我上月去西安拍的兵马俑照片生成的。 使用通义千问,只要5步就能它舞动起来,跳上现在流行的“科目三”舞蹈。 全民舞王 第1步 打开通义千问…

设计师们必备的神秘利器!这款设计工具不容忽视!

「即时设计」与Figma类似,它是一种云设计工具,可以与多人实时合作,从设计到评估、交付、团队合作和版本管理。 作为一种国内工具,起初我们对它不是很乐观,但不得不说,经过深入使用,无论是迭代速…

【量化交易实战记】小明的破晓时刻——2023下半年新能源汽车板块的成功掘金之旅

在2023年的炎炎夏日,小明在不断的观察分析市场的过程中,突然敏锐地察觉到了新能源汽车市场的风云变幻。他日复一日地研读行业报告、追踪政策动向、分析公司财报,以及密切关注全球市场动态。那段时间里,新能源汽车行业仿佛迎来了一…

C++ 有需求 需要对数字向下取整 int和 double 混淆 已解决

在项目使用中。 原本以为 直接 ceil(13/ 2) 3 但是实际是错误的。 需要 是 ceil(5.0 / 2) double 才能向上取整。结果有大佬 直接使用两种办法 能解决问题。 由于传入的参数和返回的参数都是double&#xff0c;所以需要手动转化 #include <bits/stdc.h> using name…

Docker部署的gitlab升级指南(15.11.X容器里升级PostgreSQL到13.8)

一、确定当前版本 #进入当前版本容器产看gitlab版本 docker exec -it gitlab cat /opt/gitlab/embedded/service/gitlab-rails/VERSION#显示版本如下 14.4.0二、备份数据&#xff0c;防止升级发生意外 #执行备份命令 docker exec -ti gitlab gitlab-rake gitlab:backup:creat…

Mybatis基础---------增删查改

增删改 1、新建工具类用来获取会话对象 import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.apache.ibatis.io.Resources;import java.io.IOExcept…

Spring环境搭配

概述 Spring 是一个开源框架&#xff0c;Spring 是于2003 年兴起的一个轻量级的Java 开发框架&#xff0c;由 RodJohnson 在其著作 Expert One-On-One J2EE Development and Design 中阐述的部分理念和原型衍生而来。它是 为了解决企业应用开发的复杂性而创建的。框架的主要优势…

【PDF密码】PDF密码,如何强制取消?

想要给PDF文件设置一个密码防止他人对文件进行编辑&#xff0c;那么我们可以对PDF文件设置限制编辑&#xff0c;设置方法很简单&#xff0c;我们在PDF编辑器中点击文件 – 属性 – 安全&#xff0c;在权限下拉框中选中【密码保护】 然后在密码保护界面中&#xff0c;我们勾选【…

一台电脑如何通过另一台联网电脑访问网络

电脑A没有连接网络&#xff0c;电脑B已经连接wifi。 电脑A如何通过访问电脑B从而连接网络&#xff1f; 1. 将这2台电脑用网线直连 2. 电脑B打开【网络和Internet设置】 3. 右键点击WLAN&#xff0c;选择属性&#xff0c;进入共享tab页面&#xff0c;勾选【允许其他网络用户通过…

统计学-R语言-4.7

文章目录 前言描述水平的统计量平均数分位数中位数四分位数 众数描述差异的统计量&#xff08;数据离散程度&#xff09;极差四分位差方差和标准差变异系数标准分数 描述分布形状的统计量偏度与偏度系数峰度与峰度系数 数据的综合描述综合描述的R函数综合描述的实例 总结 前言 …

Linux安装Rdkafka PHP 扩展(Kafka使用教程)

以是centos为例 #可以查看php版本 php -v#查看php安装的扩展库 php -m 1、首先&#xff0c;确保你已经安装了 PHP 和相关的开发工具。你可以使用以下命令来安装它们&#xff1a; sudo yum install php-devel 中间会问你是否ok&#xff0c;输入y回车&#xff0c;出现complete…

【总结】浅谈深度学习算法与硬件协同优化

写在前面 本文总结了笔者本科期间关于深度神经网络算法与硬件协同优化的思路和常用方法&#xff0c;希望能够给入门此方向的同学带来一定的启发。笔者学疏才浅&#xff0c;如有问题欢迎私信或评论区讨论交流&#xff01; 一、背景与意义 深度神经网络(Deep Neural Network, …

从理论到实践:数字孪生技术的全面应用探讨

数字孪生是一种将实际物体或系统的数字模型与其实时运行状态相结合的概念。这一概念的核心在于创建一个虚拟的、与真实世界相对应的数字副本&#xff0c;以便监测、分析和优化实体系统的性能。 简单理解&#xff0c;数字孪生就是在一个设备或系统的基础上&#xff0c;创造一个…

快速入门Torch读取自定义图像数据集

真有用读取自定义数据集 学习新技术当然首先要看官网了就这&#xff1f;&#xff1f;&#xff1f;官方提供了许多内置好的数据集&#xff0c;但是我需要自定义啊&#xff01;&#xff01;&#xff01;我是谁&#xff1f;我在哪&#xff1f;我在干什么&#xff1f;完全不知道如何…

FFMPEG命令生成各国国旗

文章目录 亚洲篇中国~待补充朝鲜~待补充韩国~待补充蒙古~待补充日本越南~待补充老挝 欧洲篇挪威~待补充瑞典~待补充芬兰~待补充冰岛~待补充丹麦~待补充爱沙尼亚拉脱维亚立陶宛白俄罗斯~待补充乌克兰摩尔多瓦~待补充俄罗斯德国 亚洲篇 中国~待补充 朝鲜~待补充 韩国~待补充 …

Ubuntu 20.04扩容磁盘命令:Ubuntu 20.04扩容系统主分区教程(PV VG LV)

前置知识&#xff1a; 磁盘 最基础的存在&#xff0c;物理磁盘 pv 物理卷&#xff08;同一磁盘 可以划分多个物理卷&#xff09; vg 卷组 &#xff08;一个到多个pv可组成一个卷组&#xff09; lv 逻辑卷 &#xff08;卷组可以划分为多个逻辑卷&#xff09;Ubuntu20.4扩容磁…

使用Python编写一个渗透测试探测工具

本篇将会涉及&#xff1a; 资源探测一个有用的字典资源第一个暴力探测器 资源探测 资源探测在渗透测试中还是属于资源的映射和信息的收集阶段。 主要有以下三个类型&#xff1a; 字典攻击暴力破解模糊测试 字典攻击&#xff0c;在破解密码或密钥的时候&#xff0c;通过自定…

手把手教你学会接口自动化系列十四-如何用python操作excel的sheet自动化测试之前的准备工作

接上篇,我们都知道我们已经将所有的用例都用excel管理起来了,这个时候,我们该如何使用python操作excel使我们这篇文章要探究的问题了。 首先,使用python操作excel,可以使用openpyxl这样一个模块 我们实践起来吧。 1,我们可以通过下面的命令在pycharm中安装openyxl模块…

《Qt开发》MDI应用程序

实现多个子窗体的自定义布局&#xff08;自定义子窗体尺寸和位置&#xff09;、平铺布局&#xff08;titleSubWindows&#xff09;和分页模式&#xff08;QMdi::TabbedView&#xff09;。 运行效果图 初始布局&#xff08;自定义布局&#xff09; 平铺布局 多页模式 实现过程…