欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新
页面位置 : > > > 内容正文

iOS开发底层探索界面优化示例详解,

来源: 开发者 投稿于  被查看 13162 次 评论:66

iOS开发底层探索界面优化示例详解,


目录
  • 1、卡顿原理
    • 1.1、界面显示原理
    • 1.2、界面撕裂
    • 1.3、界面卡顿
    • 小结
  • 2、卡顿检测
    • 2.1、CADisplayLink
    • 2.2、RunLoop检测
    • 2.3、微信matrix
    • 2.4、滴滴DoraemonKit
  • 3、优化方法
    • 3.1、预排版
    • 3.2、预编码 / 解码
    • 3.3、按需加载
    • 3.4、异步渲染
      • 3.4.1、CALayer
      • 3.4.2、异步渲染实现
  • 简单例子

    1、卡顿原理

    1.1、界面显示原理

    • CPU:Layout UI布局、文本计算、Display绘制、Prepare图片解码、Commit提交位图给 GPU
    • GPU:用于渲染,将结果放入 FrameBuffer
    • FrameBuffer:帧缓冲
    • Video Controller:根据Vsync(垂直同步)信号,逐行读取 FrameBuffer 中的数据,经过数模转换传递给 Monitor
    • Monitor:显示器,用于显示;对于显示模块来说,会按照手机刷新率以固定的频率:1 / 刷新率 向 FrameBuffer 索要数据,这个索要数据的命令就是 垂直同步信号Vsync(低刷60帧为16.67毫秒,高刷120帧为 8.33毫秒,下边举例主要以低刷16.67毫秒为主)

    1.2、界面撕裂

    显示端每16.67ms从 FrameBuffer(帧缓存区)读取一帧数据,如果遇到耗时操作交付不了,那么当前画面就还是旧一帧的画面,但显示过程中,下一帧数据准备完毕,导致部分显示的又是新数据,这样就会造成屏幕撕裂

    1.3、界面卡顿

    为了解决界面撕裂,苹果使用双缓冲机制 + 垂直同步信号,使用 2个FrameBuffer 存储 GPU 处理结果,显示端交替从这2个FrameBuffer中读取数据,一个被读取时另一个去缓存;但解决界面撕裂的问题也带来了新的问题:掉帧

    如果遇到画面带马赛克等情况,导致GPU渲染能力跟不上,会有2种掉帧情况;

    如图,FrameBuffer2 未渲染完第2帧,下一个16.67ms去 FrameBuffer1 中拿第3帧:

    • 掉帧情况1:第3帧渲染完毕,接下来需要第4帧,第2帧被丢弃
    • 掉帧情况2:第3帧未渲染完,再一个16.67ms去 FrameBuffer2 拿到第2帧,但第1帧多停留了16.67*2毫秒

    小结

    固定的时间间隔会收到垂直同步信号(Vsync),如果 CPU 和 GPU 还没有将下一帧数据放到对应的帧 FrameBuffer缓冲区,就会出现 掉帧

    2、卡顿检测

    2.1、CADisplayLink

    系统在每次发送 VSync 时,就会触发CADisplayLink,通过统计每秒发送 VSync 的数量来查看 App 的 FPS 是否稳定

    #import "ViewController.h"
    @interface ViewController ()
    @property (nonatomic, strong) CADisplayLink *link;
    @property (nonatomic, assign) NSTimeInterval lastTime;  // 每隔1秒记录一次时间
    @property (nonatomic, assign) NSUInteger count;         // 记录VSync1秒内发送的数量
    @end
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(linkAction:)];
        [_link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    }
    - (void)linkAction: (CADisplayLink *)link {
        if (_lastTime == 0) {
            _lastTime = link.timestamp;
            return;
        }
        _count++;
        NSTimeInterval delta = link.timestamp - _lastTime;
        if (delta < 1) return;
        _lastTime = link.timestamp;
        float fps = _count / delta;
        _count = 0;
        NSLog(@"
    

    用户评论