美文网首页
界面优化

界面优化

作者: 佛祖ohmygod | 来源:发表于2021-04-06 13:40 被阅读0次

    界面优化

    图片显示到屏幕上是CPU与GPU的协作完成

    • CPU: 计算视图frame,图片解码,需要绘制纹理图片通过数据总线交给GPU
    • GPU: 纹理混合,顶点变换与计算,像素点的填充计算,渲染到帧缓冲区。
    • 时钟信号:垂直同步信号V-Sync / 水平同步信号H-Sync。
    • iOS设备双缓冲机制:显示系统通常会引入两个帧缓冲区,双缓冲机制


      image.png

    二.图片加载的工作流程

    1. 假设我们使用 +imageWithContentsOfFile: 方法从磁盘中加载一张图片,这个时候的图片并没有解压缩;
    2. 然后将生成的 UIImage 赋值给 UIImageView
    3. 接着一个隐式的 CATransaction 捕获到了 UIImageView 图层树的变化;
    4. 在主线程的下一个 runloop 到来时,Core Animation 提交了这个隐式的 transaction ,这个过程可能会对图片进行 copy 操作,而受图片是否字节对齐等因素的影响,这个 copy 操作可能会涉及以下部分或全部步骤:
    • 分配内存缓冲区用于管理文件 IO 和解压缩操作;
    • 将文件数据从磁盘读到内存中;
    • 将压缩的图片数据解码成未压缩的位图形式,这是一个非常耗时的 CPU 操作;
    • 最后 Core Animation 中CALayer使用未压缩的位图数据渲染 UIImageView 的图层。
    • CPU计算好图片的Frame,对图片解压之后.就会交给GPU来做图片渲染
    1. 渲染流程:
    • GPU获取获取图片的坐标
    • 将坐标交给顶点着色器(顶点计算)
    • 将图片光栅化(获取图片对应屏幕上的像素点)
    • 片元着色器计算(计算每个像素点的最终显示的颜色值)
    • 从帧缓存区中渲染到屏幕上
      我们提到了图片的解压缩是一个非常耗时的 CPU 操作,并且它默认是在主线程中执行的。那么当需要加载的图片比较多时,就会对我们应用的响应性造成严重的影响,尤其是在快速滑动的列表上,这个问题会表现得更加突出。
      卡顿监控
      FPS监控:为了保持流程的UI交互,App的刷新拼搏应该保持在60fps左右,其原因是因为iOS设备默认的刷新频率是60次/秒,而1次刷新(即VSync信号发出)的间隔是 1000ms/60 = 16.67ms,所以如果在16.67ms内没有准备好下一帧数据,就会产生卡顿

    主线程卡顿监控:通过子线程监测主线程的RunLoop,判断两个状态(kCFRunLoopBeforeSources 和 kCFRunLoopAfterWaiting)之间的耗时是否达到一定阈值

    界面优化
    CPU层面的优化
    1、尽量用轻量级的对象代替重量级的对象,可以对性能有所优化,例如 不需要相应触摸事件的控件,用CALayer代替UIView
    2、尽量减少对UIView和CALayer的属性修改

    • CALayer内部并没有属性,当调用属性方法时,其内部是通过运行时resolveInstanceMethod为对象临时添加一个方法,并将对应属性值保存在内部的一个Dictionary中,同时还会通知delegate、创建动画等,非常耗时
    • UIView相关的显示属性,例如frame、bounds、transform等,实际上都是从CALayer映射来的,对其进行调整时,消耗的资源比一般属性要大
      3、当有大量对象释放时,也是非常耗时的,尽量挪到后台线程去释放
      4、尽量提前计算视图布局,即预排版,例如cell的行高
      5、Autolayout在简单页面情况下们可以很好的提升开发效率,但是对于复杂视图而言,会产生严重的性能问题,随着视图数量的增长,Autolayout带来的CPU消耗是呈指数上升的。所以尽量使用代码布局。如果不想手动调整frame等,也可以借助三方库,例如Masonry(OC)、SnapKit(Swift)、ComponentKit、AsyncDisplayKit等
      6、文本处理的优化:当一个界面有大量文本时,其行高的计算、绘制也是非常耗时的
      1)如果对文本没有特殊要求,可以使用UILabel内部的实现方式,且需要放到子线程中进行,避免阻塞主线程
      计算文本宽高:[NSAttributedString boundingRectWithSize:options:context:]
      文本绘制:[NSAttributedString drawWithRect:options:context:]
      2)自定义文本控件,利用TextKit 或最底层的 CoreText 对文本异步绘制。并且CoreText 对象创建好后,能直接获取文本的宽高等信息,避免了多次计算(调整和绘制都需要计算一次)。CoreText直接使用了CoreGraphics占用内存小,效率高
      7、图片处理(解码 + 绘制)
      1)当使用UIImage 或 CGImageSource 的方法创建图片时,图片的数据不会立即解码,而是在设置时解码(即图片设置到UIImageView/CALayer.contents中,然后在CALayer提交至GPU渲染前,CGImage中的数据才进行解码)。这一步是无可避免的,且是发生在主线程中的。想要绕开这个机制,常见的做法是在子线程中先将图片绘制到CGBitmapContext,然后从Bitmap 直接创建图片,例如SDWebImage三方框架中对图片编解码的处理。这就是Image的预解码
      当使用CG开头的方法绘制图像到画布中,然后从画布中创建图片时,可以将图像的绘制在子线程中进行
      8、图片优化
      1)尽量使用PNG图片,不使用JPGE图片
      2)通过子线程预解码,主线程渲染,即通过Bitmap创建图片,在子线程赋值image
      3)优化图片大小,尽量避免动态缩放
      4)尽量将多张图合为一张进行显示
      9、尽量避免使用透明view,因为使用透明view,会导致在GPU中计算像素时,会将透明view下层图层的像素也计算进来,即颜色混合处理,可以参考六、OpenGL 渲染技巧:深度测试、多边形偏移、 混合这篇文章中提及的混合
      10、按需加载,例如在TableView中滑动时不加载图片,使用默认占位图,而是在滑动停止时加载
      11、少使用addView 给cell动态添加view
      GPU层面优化
      1、尽量减少在短时间内大量图片的显示,尽可能将多张图片合为一张显示,主要是因为当有大量图片进行显示时,无论是CPU的计算还是GPU的渲染,都是非常耗时的,很可能出现掉帧的情况
      2、尽量避免图片的尺寸超过4096×4096,因为当图片超过这个尺寸时,会先由CPU进行预处理,然后再提交给GPU处理,导致额外CPU资源消耗
      3、尽量减少视图数量和层次,主要是因为视图过多且重叠时,GPU会将其混合,混合的过程也是非常耗时的
      4、尽量避免离屏渲染,可以查看这篇文章四、深入剖析【离屏渲染】原理
      5、异步渲染,例如可以将cell中的所有控件、视图合成一张图片进行显示。可以参考Graver三方框架

    相关文章

      网友评论

          本文标题:界面优化

          本文链接:https://www.haomeiwen.com/subject/tzmskltx.html