美文网首页iOS
iOS性能调优实战之Instrument-Core Animat

iOS性能调优实战之Instrument-Core Animat

作者: charlotte2018 | 来源:发表于2017-07-08 15:42 被阅读94次

    由于app新版本增加好多动画,本身就是一个拍照为主的建模工具。所以对性能要求很高,所以性能调优尤为重要。Instrument有很多工具可以测试内存泄漏,测试动画帧率,所以用好它,能解决项目中卡顿问题,让app更健壮。(随便扯点先)

    • 先看下基本界面,点击Product->Profile 或者直接快捷键Command + i 打开Instrument
    BEBFE50F-80E7-4FE2-9A19-A41492C029C2.png

    Core Animation

    它的作用就是查看fps的值,稍后会说这个fps是什么意思。第二个作用就是查看图层的渲染,图层的渲染是影响fps的关键。如果你的tableview滑动有卡顿,首先你会看到fps比较小。然后就查找原因,原因就在图层渲染上。如果渲染问题解决了,那fps就接近最高了,最高的是60。

    FPS 屏幕每秒刷新的频率

    任何屏幕总有一个刷新率,比如iphone推荐的刷新率是60Hz,也就是说GPU每秒钟刷新屏幕60次,因此两次刷新之间的间隔为16.67ms。这段时间内屏幕内容保持不变,称为一帧(frame),fps表示frames per second,也就是每秒钟显示多少帧画面。对于静止不变的内容,我们不需要考虑它的刷新率,但在执行动画或滑动时,fps的值直接反映出滑动的流畅程度。

    注意用真机测试哦,点击上图的Core Animation,然后进入下图的界面,然后点左上角的⭕️按钮,就可以开始测试了。这就是我滑动tabelview的时候屏幕刷新的频次。0的时候就是我没有滑动。滑动的时候大约都是在50多(60最佳),说明滑动的还算顺。根据WWDC的说法,当FPS 低于45的时候,用户就会察觉到到滑动有卡顿。那就该分析下卡顿的原因。


    7492DEB4-2898-4589-8C51-D0D6B246A32D.png
    调试、优化,查看图层的渲染情况(关键问题)
    屏幕快照 2017-07-08 下午12.18.15.png

    我们可以用上图底部的Debug Options框子里的工具去调试。寻找掉帧的原因。接下来就一个个去用用它们。

    Color Blended Layers 图层混合

    三原色大家都知道吧,rgb,如果某一块区域上覆盖了多个view,最后的显示效果受到这些view的共同影响。举个例子,上层是蓝色(RGB=0,0,1),透明度为50%,下层是红色(RGB=1,0,0)。那么最终的显示效果是紫色(RGB=0.5,0,0.5)。这种颜色的混合(blending)需要消耗一定的GPU资源,因为实际上可能不止只有两层。如果只想显示最上层的蓝色,可以把它的透明度设置为100%,这样GPU会忽略下面所有的layer,从而节约了很多不必要的运算。

    当勾选了Color Blended Layers之后会看到手机出现这样的场景,红色代表出先了图层混合,绿色的没事,所以我们就努力的去消除红色就行了。

    IMG_6346.PNG

    首先创建一个View,应该给他个颜色,不给就是默认透明,透明就有可能发生图层混合,所以这个是一个点。

    UIImageView可以先给背景一个颜色,不仅它自身需要是不透明的,它的图片也不能含有alpha通道,如果有alpha通道就会发生图层混合。

    label(哈哈哈)成红色,是因为一没有给文字的label增加不透明的背景颜色,而是当UILabel内容为中文时,label的实际渲染区域要大于label的size,因为外围有了一圈的阴影,才会出现图层混合所以就把超出的部分切一下,代码如下:

       UILabel *textLabel = [[UILabel alloc]initWithFrame:CGRectMake(100, 20, 80, 20)];
        textLabel.backgroundColor = [UIColor whiteColor];
        textLabel.layer.masksToBounds = YES;
        textLabel.text = [NSString stringWithFormat:@"哈哈哈"];
        [self.contentView addSubview:textLabel];
    
    

    最后发现哈哈哈变绿了。

    Color Hits Green and Misses Red 光栅化缓存图层命中

    光栅化是将一个layer预先渲染成位图(bitmap),然后加入缓存中。如果对于阴影效果这样比较消耗资源的静态内容进行缓存,可以得到一定幅度的性能提升。

    label.layer.shouldRasterize = true
    label.layer.rasterizationScale = layer.contentsScale 
    
    

    它表示如果命中缓存则显示为绿色,否则显示为红色,显然绿色越多越好,红色越少越好。

    光栅化的核心在于缓存的思想。上下微小幅度滑动时,一直是绿色
    上下较大幅度滑动,新出现的label一开始是红色,随后变成绿色
    如果静止一秒钟,刚开始滑动时会变红。
    这是因为layer进行光栅化后渲染成位图放在缓存中。当屏幕出现滑动时,我们直接从缓存中读取而不必渲染,所以会看到绿色。当新的label出现时,缓存中没有个这个label的位图,所以会变成红色。第三点比较关键,缓存中的对象有效期只有100ms,即如果在0.1s内没有被使用就会自动从缓存中清理出去。这就是为什么停留一会儿再滑动就会看到红色。

    光栅化的缓存机制是一把双刃剑,先写入缓存再读取有可能消耗较多的时间。因此光栅化仅适用于较复杂的、静态的效果。通过Instrument的调试发现,如果使用光栅化经常出现未命中缓存的情况,如果没有特殊需要则可以关闭光栅化,而且光栅化会导致离屏渲染。

    Color Copied Image (拷贝的图片)

    这个选项主要检查我们有无使用不正确图片格式,由于手机显示都是基于像素的,所以当手机要显示一张图片的时候,系统会帮我们对图片进行转化。比如一个像素占用一个字节,故而RGBA则占用了4个字节,则1920 x 1080的图片占用了7.9M左右,但是平时jpg或者png的图片并没有那么大,因为它们对图片做了压缩,但是是可逆的。所以此时,如果图片的格式不正确,则系统将图片转化为像素的时间就有可能变长。而该选项就是检测图片的格式是否是系统所支持的,若是GPU不支持的色彩格式的图片则会标记为青色,则只能由CPU来进行处理。CPU被强制生成了一些图片,然后发送到渲染服务器,而不是简单的指向原始图片的的指针。我们不希望在滚动视图的时候,CPU实时来进行处理,因为有可能会阻塞主线程。一般不会出现这种情况。如果有则会将图片标记为蓝色。

    Color Misaligned Image (图片大小)

    这里会高亮那些被缩放或者拉伸以及没有正确对齐到像素边界的图片,即图片Size和imageView中的Size不匹配,会使图过程片缩放,而缩放会占用CPU,所以在写代码的时候保证图片的大小匹配好imageView,如果图片需要缩放则标记为黄色,如果没有像素对齐则标记为紫色。

    Color Offscreen- Rendered Yellow (离屏渲染)

    离屏渲染就是GPU渲染发生在屏幕外。

    先看看正常的渲染首先OpenGL提交一个命令到Command Buffer,随后GPU开始渲染,渲染结果放到Render Buffer中,这是正常的渲染流程。

    但是有一些复杂的效果无法直接渲染出结果,它需要分步渲染最后再组合起来,比如为相机图标添加一个蒙版(mask):在前两个渲染通道中,GPU分别得到了纹理(texture,也就是那个相机图标)和layer(蓝色的蒙版)的渲染结果。但这两个渲染结果没有直接放入Render Buffer中,也就表示这是离屏渲染。直到第三个渲染通道,才把两者组合起来放入Render Buffer中。离屏渲染意味着把渲染结果临时保存,等用到时再取出,因此相对于普通渲染更占用资源。

    Color Offscreen-Rendered Yellow”会把需要离屏渲染的地方标记为黄色,大部分情况下我们需要尽可能避免黄色的出现。离屏渲染可能会自动触发,也可以手动触发。以下情况可能会导致触发离屏渲染:

    • 为图层设置遮罩(layer.mask)
    • 将图层的layer.masksToBounds / view.clipsToBounds属性设置为true
    • 将图层layer.allowsGroupOpacity属性设置为YES和layer.opacity小于1.0
    • 为图层设置阴影(layer.shadow *)。
    • 为图层设置layer.shouldRasterize=true
    • 具有layer.cornerRadius,layer.edgeAntialiasingMask,layer.allowsEdgeAntialiasing的图层
    • 文本(任何种类,包括UILabel,CATextLayer,Core Text等)。
    • 使用CGContext在drawRect :方法中绘制大部分情况下会导致离屏渲染,甚至仅仅是一个空的实现。

    如果设置阴影,可以给阴影一个路径,

    imgView.layer.shadowPath = UIBezierPath(rect: imgView.bounds).CGPath
    

    这行代码制定了阴影路径,如果没有手动指定,Core Animation会去自动计算,这就会触发离屏渲染。如果人为指定了阴影路径,就可以免去计算,从而避免产生离屏渲染。

    设置cornerRadius本身并不会导致离屏渲染,但很多时候它还需要配合layer.masksToBounds = true使用。根据之前的总结,设置masksToBounds会导致离屏渲染。解决方案是尽可能在滑动时避免设置圆角,如果必须设置圆角,可以使用光栅化技术将圆角缓存起来或者是绘制一个圆角图片:

    // 设置圆角
    label.layer.masksToBounds = true
    label.layer.cornerRadius = 8
    label.layer.shouldRasterize = true
    label.layer.rasterizationScale = layer.contentsScale
    

    总结

    避免图层混合

    确保控件的opaque属性设置为true,确保backgroundColor和父视图颜色一致且不透明
    如无特殊需要,不要设置低于1的alpha值
    确保UIImage没有alpha通道

    避免临时转换

    确保图片大小和frame一致,不要在滑动时缩放图片
    确保图片颜色格式被GPU支持,避免劳烦CPU转换

    慎用离屏渲染

    绝大多数时候离屏渲染会影响性能
    重写drawRect方法,设置圆角、阴影、模糊效果,光栅化都会导致离屏渲染
    设置阴影效果是加上阴影路径
    滑动时若需要圆角效果,开启光栅化

    相关文章

      网友评论

        本文标题:iOS性能调优实战之Instrument-Core Animat

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