美文网首页iOS开发
iOS-性能优化之CoreAnimation使用

iOS-性能优化之CoreAnimation使用

作者: 路飞_Luck | 来源:发表于2019-05-10 20:07 被阅读0次
    目录
    序言

    iOS为了提高滑动的流畅感,特意在滑动的时候将runloop模式切换到UITrackingRunLoopMode,在这个过程中专心做跟滑动相关的工作。但是还是会遇到滑动不流畅的情况。接下来我们就需要借助CoreAnimation来找出造成不流畅的元凶。

    一 CoreAnimation 的介绍

    分析图像动画性能主要用的是Core Animation这个组件,先简单介绍一下里面一些经常用到的选项:

    模拟器 -> Debug

    image.png
    • Color Blended layers

    标示混合的图层会为红色,不透明的图层为绿色,通常我们希望绿色的区域越多越好。

    • Color Hits Green and Misses Red (已经找不到了)

    假如我们设置viewlayer的shouldRasterize为YES,那些成功被缓存的layer会标注为绿色,反之为红色。

    • Color copied images

    标示那些被Core Animation拷贝的图片。这主要是因为该图片的色彩格式不能被GPU直接处理,需要在CPU这边做转换,假如在主线层做这个操作对性能会有一定的影响。

    • Color misaligned images

    被缩放的图片会被标记为黄色,像素不对齐则会标注为紫色。

    • Color offscreen-rendered yellow

    标示哪些layer需要做离屏渲染(offscreen-render)。

    二 遇到性能瓶颈的解决出路

    下面这张图摘自 WWDC2014 大会视频 Advanced Graphics and Animations for iOS Apps

    Performance Investigation Mindset.png

    解释说明如下:

    是否受到CPU或者GPU的限制?
    是否有不必要的CPU渲染?
    是否有太多的离屏渲染操作?
    是否有太多的图层混合操作?
    是否有奇怪的图片格式或者尺寸?
    是否涉及到昂贵的view或者效果?
    view的层次结构是否合理?
    

    当你碰到性能问题的时候,可以从这几方面入手

    三 离屏渲染
    3.1 CPU 的离屏渲染

    通常在以下操作时容易造成离屏渲染

    • drawRect

    如果没有自定义绘制的任务就不要在子类中写一个空的drawRect方法,因为只要实现了该方法,就会为视图分配一个寄宿图,这个寄宿图的像素尺寸等于视图大小乘以 contentsScale的值,造成资源浪费

    • 使用Core Graphics

    上面的情况使用的就是CPU离屏渲染,首先分配一块内存,然后进行渲染操作生成一份bitmap位图,整个渲染过程会在你的应用中同步的进行,接着再将位图打包发送到iOS里一个单独的进程--render server,理想情况下,render server将内容交给GPU直接显示到屏幕上。

    3.2 GPU 的离屏渲染

    使用GPU在当前屏幕缓冲区以外开辟一个新的缓冲区进行绘制,通常发生的情况有:

    • 设置 cornerRadius + mask
    • shadows
    • edge antialiasing
    • 设置layer.shouldRasterize = YES
    离屏渲染.png

    离屏渲染(offscreen-render)对性能到底有什么影响?

    通常大家说的离屏渲染指的是GPU这块(当然CPU这块也会有影响,也需要消耗一定的资源),比如修改了layer
    的阴影或者圆角,GPU需要做额外的渲染操作。通常GPU在做渲染的时候是很快的,但是涉及到offscreen-
    render的时候情况就可能有些不同,因为需要额外开辟一个新的缓冲区进行渲染,然后绘制到当前屏幕的过程
    需要做onscreen跟offscreen上下文之间的切换,这个过程的消耗会比较昂贵,涉及到OpenGL的pipeline跟
    barrier,而且offscreen-render在每一帧都会涉及到,因此处理不当肯定会对性能产生一定的影响,所以可以的
    话尽量减少offscreen-render的图层。
    

    查看哪些图层需要离屏渲染可以用Instruments的Core Animation工具进行检测,Color Offscreen-Rendered Yellow选项会将对应的图层标记为黄色。

    四 造成离屏渲染原因分析
    4.1 Blending

    假如最上层的view是不透明的,那直接使用这个view的对应颜色之就可以,但如果view是透明的,在计算像素的颜色值时就需要计算它下面图层,透明的视图越多,计算量就越大,因此也会对图形的性能产生一定的影响,所以可以的话也尽量减少透明图层的数目。

    4.2 圆角 cornerRadius + masksToBounds
    _iconImgView.layer.cornerRadius = 22;
    _iconImgView.layer.masksToBounds = YES;
    
    image.png

    我们可以通过使用 绘制一个圆形图片覆盖或者使用 Quartz2D来实现圆角。
    具体的可以参考文章 iOS-裁剪圆角方法汇总(五种)

    4.3 shadowPath

    设置阴影效果可以通过以下代码实现

    // 设置阴影
    CALayer *imageViewLayer = iconImgV.layer;
    imageViewLayer.shadowColor = [[UIColor blackColor] CGColor];
    imageViewLayer.shadowOpacity = 1.0; //此参数默认为0,即阴影不显示
    imageViewLayer.shadowRadius = 2.0; //给阴影加上圆角,对性能无明显影响
    imageViewLayer.shadowOffset = CGSizeMake(5, 5);
    

    但是当你滑动的时候发现会导致离屏渲染。

    一个简单的不需要离屏渲染的方法就是制定阴影的路径,也就是设置layer的shadowPath属性

    //设定路径:与视图的边界相同
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:iconImgV.bounds];
    imageViewLayer.shadowPath = path.CGPath;//路径默认为 nil
    
    4.4 Rasterization

    对于圆角这种类似导致的性能问题,最简单的就是在列表中不要使用圆角,假如要通过cornerRadius + masksToBounds方式裁剪圆角的话,一种最快提升性能的方式就是设置layer的shouldRasterize为YES。

    除了 GroupOpacityEdgeAntialiasing,其他效果触发的离屏渲染都会对性能产生严重影响,离屏渲染真的是一无是处吗?不,离屏渲染本来是个优化设计。如何物尽其用?答案是:Rasterization

    cell.layer.shouldRasterize = true
    cell.layer.rasterizationScale = cell.layer.contentsScale
    

    shouldRasterize = false时,离屏渲染的黄色特征仅限于上述自动触发离屏渲染的效果的部分,shouldRasterize = true后该部分和开启了该属性的 layer 整体(在这里就是 cell 整体)都有黄色特征,所以开启 Rasterization 是手动启动了离屏渲染。

    从前面来看,离屏渲染会给 GPU 带来沉重的负担,强制启动岂不是更糟?开启 Rasterization 后,GPU 只合成一次内容,然后复用合成的结果;合成的内容超过 100ms 没有使用会从缓存里移除,在更新内容时还会产生更多的离屏渲染。对于内容不发生变化的视图,原本拖后腿的离屏渲染就成为了助力;如果视图内容是动态变化的,使用这个方案有可能让性能变得更糟。

    Core Animation Instruments 有个Color Hits Green and Misses Red的选项,开启 Rasterization 后开启这个选项,屏幕上绿色的部分表示有渲染缓存可用,红色的部分表示无渲染缓存可用。

    使用该属性注意事项:

    • 适用于内容基本不变的图层

    假如图层的内容经常变化,比如cell里面有涉及到动画之类的,那么缓存的内容就无效了,GPU需要重新创建缓存区,导致离屏渲染,这又涉及到OpenGL的上下文环境切换,反而降低性能。

    • 不要过度使用
      缓存区的大小被设置为屏幕大小的2.5倍,假如过分使用同样会导致大量的离屏渲染。

    • 如果缓存的内容超过100ms没有被使用则会被回收。

    五 实战技巧
    5.1 裁剪圆角

    对于圆角可以使用一张中间圆形透明的图覆盖在上面,虽然这会引入blending操作,但是大部分情况下性能会比离屏渲染好。
    参考文章 iOS-裁剪圆角方法汇总(五种)

    5.2 view 层次结构

    让你的view层次结构平坦一些,因为OpenGL在渲染layer的时候,在碰到有子层级layer的时候可能需要停下来把两者合成到一个buffer里再接着渲染。(When the OpenGL renderer goes to draw each layer, it may have to stop for some subhierarchies and composite them into a single buffer).

    5.3 延迟加载图片

    有时候在边滚动边设置图片的时候可能会有一定的影响,因此可以在滚动的时候imageview不执行setimage的操作,滚动停止的时候才加载图片,由于滚动的时候NSRunloop是处于UITrackingRunLoopMode模式下,可以采用如下的方式,将设置图片放到NSDefaultRunLoopMode模式下才进行:

    // 延迟加载图片
    __block UIImage *downloadedImage = nil;
    [[SDWebImageDownloader sharedDownloader] downloadImageWithURL:[NSURL URLWithString:obj] options:SDWebImageDownloaderScaleDownLargeImages progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
        downloadedImage = image;
    }];
    [imgView performSelector:@selector(setImage:) withObject:downloadedImage afterDelay:0 inModes:@[NSDefaultRunLoopMode]];
    
    5.4 * 图片加载的极限优化方式:FastImageCache

    本文会持续更新,并且进行完善,配备相应的代码例子,敬请期待。


    本文参考
    iOS app性能优化的那些事(二)
    iOS离屏渲染优化


    • 如有错误,欢迎指正,多多点赞,打赏更佳,您的支持是我写作的动力。

    相关文章

      网友评论

        本文标题:iOS-性能优化之CoreAnimation使用

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