长路漫漫,唯剑作伴--coreanimation优化

来事啊 来事啊     2022-08-27     581

关键词:

一、简介

  • 当App发展到一定的规模,性能优化就成为必不可少的一点。但是很多人,又对性能优化很陌生,毕竟平常大多时间都在写业务逻辑,很少关注这个。最近在优化自己的项目,也收集了很多资料,这里先浅谈一下使用Instruments中CoreAnimation优化收获的经验以及总结,这是第一篇,后续会更新Timer Profiler,Leaks等其他优化工具的具体用法。

二、准备工作

  1. 在性能优化中一个最具参考价值的属性是FPS:全称Frames Per Second,其实就是屏幕刷新率,苹果的iphone推荐的刷新率是60Hz,也就是说GPU每秒钟刷新屏幕60次,这每刷新一次就是一帧frame,FPS也就是每秒钟刷新多少帧画面。静止不变的页面FPS值是0,这个值是没有参考意义的,只有当页面在执行动画或者滑动的时候,FPS值才具有参考价值,FPS值的大小体现了页面的流畅程度高低,当低于45的时候卡顿会比较明显。
  2. 注意点

    • 使用真机调试
    • 最好使用release包测试(release是发布版本,苹果会在release包中做很多优化工作,因此用release包测试出来的性能才是最真实的)。
  3. 步骤

    1. 启动程序点击XCode选择左上角-XCode->Open Developer Tool ->Instruments,打开Instruments再选择CoreAnimation:

      技术分享

    2. 打开CoreAnimation

      技术分享

四、调试、优化

  1. Color Blended Layers (图层混合)

    • 这个选项是检测哪里发生了图层混合,先介绍一下什么是图层混合?很多情况下,界面都是会出现多个UI控件叠加的情况,如果有透明或者半透明的控件,那么GPU会去计算这些这些layer最终的显示的颜色,也就是我们肉眼所看到的效果。例如一个上层Veiw颜色是绿色RGB(0,255,0),下层又放了一个View颜色是红色RGB(0,0,255),透明度是50%,那么最终显示到我们眼前的颜色是蓝色RGB(0,127.5,127.5)。这个计算过程会消耗一定的GPU资源损耗性能。如果我们把上层的绿色View改为不透明, 那么GPU就不用耗费资源计算,直接显示绿色。混合颜色计算公式:

      R(C)=alpha*R(B)+(1-alpha)*R(A)    R(x)、G(x)、B(x)分别指颜色x的RGB分量
    • 如果出现图层混合了,打开Color Blended Layers选项,那块区域会显示红色,所以我们调试的目的就是将红色区域消减的越少越好。那么如何减少红色区域的出现呢?只要设置控件不透明即可。思路:

      • 设置opaque 属性为true。
      • 给View设置一个不透明的颜色,没有特殊需要设置白色即可。
    • 如果你在lldb中po打印某个控件,你会发现打印出来的数据中,控件的opaque都是true,因为控件这个属性的默认值都是true,所以第一种方法可以直接忽略掉。使用第二种方法你会发现之前红色的都消除掉了。

    • 设置不透明之前

      技术分享

    • 设置不透明之后 

            技术分享

    • 具体方法

      label.backgroundColor = [UIColor whiteColor];
      label.layer.masksToBounds = YES;
    • 到这里你可能奇怪,设置label的背景色第一行不就够了么,为什么还有第二行?这是因为如果label的内容是中文,label实际渲染区域要大于label的size,最外层多了一个sublayer,如果不设置第二行label的边缘外层灰出现图层混合的红色,因此需要在label内容是中文的情况下加第二句。单独使用label.layer.masksToBounds = YES是不会发生离屏渲染,下文会讲离屏渲染。

      注意点:UIImageView控件比较特殊,不仅需要自身这个容器是不透明的,并且imageView包含的内容图片也必须是不透明的,如果你自己的图片出现了图层混合红色,先检查是不是自己的代码有问题,如果确认代码没问题,就是图片自身的问题,可以联系你们的UI眉眉~

  2. Color Hits Green and Misses Red(光栅化)

    • 这个选项主要是检测我们是是否正确使用layer的shouldRasterize属性,shouldRasterize = YES开启光栅化。什么是光栅化?光栅化是将一个layer预先渲染成位图(bitmap),再加入到缓存中,成功被缓存的layer会标注为绿色,没有成功缓存的会标注为红色,正确使用光栅化可以得到一定程度的性能提升。

    • 适用情况:一般在图像内容不变的情况下才使用光栅化,例如设置阴影耗费资源比较多的静态内容,如果使用光栅化对性能的提升有一定帮助。

    • 非适用情况:如果内容会经常变动,这个时候不要开启,否则会造成性能的浪费。例如我们在使用tableViewCell中,一般不要用光栅化,因为tableViewCell的绘制非常频繁,内容在不断的变化,如果使用了光栅化,会造成大量的离屏渲染降低性能。

    • 如果你在一个界面中使用了光栅化,刚进去这个页面的所有使用了光栅化的控件layer都会是红色,因为还没有缓存成功,如果上下滑动你会发现,layer变成了绿色。但是如果你滑动幅度较大会发现,新出现的控件会是红色然后变成绿色,因为刚开始这些控件的layer还没有缓存。

    • 注意点

      • 系统给光栅化缓存分配了一个固定的大小,因此不能过度使用,如果超出了缓存也会造成离屏渲染
      • 缓存的时间为100ms,因此如果在100ms内没有使用缓存的对象,则会从缓存中清除。

        技术分享

  3. Color Copied Images(图片颜色格式)

    • Shows images that are copied by Core Animation in blue苹果官方注释被拷贝给CPU进行转化的图片显示为绿色。那么这句话怎么理解呢?如果GPU不支持当前图片的颜色格式,那么就会将图片交给CPU预先进行格式转化,并且这张图片标记为蓝色。那么GPU支持什么格式呢?苹果的GPU只解析32bit的颜色格式,如果使用Color Copied Images去调试发现是蓝色,这个时候你也可以去找你们的UI眉眉了~

    • 知识扩展:32bit指的是图片颜色深度,用“位”来表示,用来表示显示颜色数量,例如一个图片支持256种颜色,那么就需要256个不同的值来表示不同的颜色,也就是从0到255,二进制表示就是从00000000到11111111,一共需要8位二进制数,所以颜色深度是8。通常32bit色彩中使用三个8bit分别表示R红G绿B蓝,还有一个8bit常用来表示透明度(Alpha)。

  4. Color Non-Standard Surface Formats (不标准的表面颜色格式)

    • 这个调试选项没有一篇博文讲过,都是直接略过,我也尝试很多途径去找这个选项到底是什么作用,然而苹果开发文档以及stackOverFlow都没有对这个有所解释。自己真机调试尝试了很多发现有个规律,就是打开这个选项,某些Label和Button的背景颜色都会出现银白色,但是不是必先现的,有些Label和Button依然正常颜色背景。其他ImageView等控件是不会出现银白色的背景颜色,猜想是不是和文本Text的设置有关系。如果您对这个有所了解,欢迎讨论。

      技术分享
  5. Color Immediately(颜色刷新频率)

    • 当执行颜色刷新的时候移除10ms的延迟,因为可能在特定情况下你不需要这些延迟,所以使用此选项加快颜色刷新的频率。不过一般这个调试选项我们是用不到的。

  6. Color Misaligned Images(图片大小)

    • 这个选项可以帮助我们查看图片大小是否正确显示。如果image size和imageView size不匹配,image会出现黄色。要尽可能的减少黄色的出现,因为image size与imageView size不匹配,会消耗资源压缩图片。下图中的image实际size(81,110),顶部image正常,底部image出现黄色因为放在了一个size x 2的imageView容器中。

      技术分享

  7. Color Offscreen-Rendered Yellow(离屏渲染)

    • 离屏渲染Off-Screen Rendering 指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。还有另外一种屏幕渲染方式-当前屏幕渲染On-Screen Rendering ,指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行。 离屏渲染会先在屏幕外创建新缓冲区,离屏渲染结束后,再从离屏切到当前屏幕, 把离屏的渲染结果显示到当前屏幕上,这个上下文切换的过程是非常消耗性能的,实际开发中尽可能避免离屏渲染。

    • 触发离屏渲染Offscreen rendering的行为

      • drawRect:方法
      • layer.shadow
      • layer.allowsGroupOpacity or layer.allowsEdgeAntialiasing
      • layer.shouldRasterize
      • layer.mask
      • layer.masksToBounds && layer.cornerRadius
    • 这里有需要注意的是第三条layer.shouldRasterize ,其实就是我们本文讲的第三个选项光栅化,光栅化会触发离屏渲染,因此光栅化慎用。

    • 第六条设置圆角会触发离屏渲染,如果在某个页面大量使用了圆角,会非常消耗性能造成FPS急剧下降,设置圆角触发离屏渲染要同时满足下面两个条件:

      layer.masksToBounds = YES;
      layer.cornerRadius = 5;
    • 下图是给一个label设置了圆角,触发离屏渲染:

      技术分享
    • 为了尽可能避免触发离屏渲染,我们可以换其他手段来实现必要的功能:

      • 阴影绘制shadow:使用ShadowPath来替代shadowOffset等属性的设置

        imageViewLayer.shadowPath = CGPathCreateWithRect(imageRect, NULL);
      • 利用GraphicsContex生成一张带圆角的图片或者view,这里不写具体实现过程,需要的可以度娘Copy,很多现成的代码。

  8. Color Compositing Fast-Path Blue (快速路径)

    • Places a blue overlay over content that is detached from the compositor.标记由硬件绘制的路径为蓝色,蓝色越多越好,可以对直接使用OpenGL绘制的图层进行高亮。没有对OpenGL有过多的研究,所以这里没办法给出demo,大家只需要记住蓝色越多越好就ok。

  9. Flash Updated Regions (重绘区域)

    • Colors regions on your iOS device in yellow when those regions are updated by the graphics processor.这个选项会对重绘的内容高亮成黄色,重绘就是指使用Core Graphics绘制,绘制会损耗一定的性能,因此重绘区域应该越小越好。下图是用真机进入原生地图开启Flash Updated Regions 调试的效果图,很可惜截屏不能截到黄色的区域,因此我用红框圈起来,一共两处,坐上角的是在不停的刷新页面,右下角是在不停的刷新当前位置,因此都是使用Core Graphics重绘刷新的一种场景,并且你可以发现黄色区域很小,区域越小性能越好。

    • 技术分享

五、总结

  • 好了,花了几天的精力终于写完了~对Core Animation的全部调试选项也都讲解了,如果你能在项目中合理使用,对App的性能提升一定不可小觑。当然这是针对试图的调试方式,功能代码的设计是否合理也是影响性能很重要的一方面。

 

长路漫漫,唯剑作伴--基础

一、结构体结构体定义structstu{char*name;//姓名intnum;//学号intage;//年龄chargroup;//所在学习小组floatscore;//成绩}结构体变量structstustu1,stu2;structstu{char*name;//姓名intnum;//学号intage;//年龄chargroup;//所在学习小组floatscore;//成绩}stu1,s 查看详情

长路漫漫,唯剑作伴--胡乱来一套

//第一种方法NSDate*start=[NSDatedate];//2.根据url地址下载图片数据到本地(二进制数据)NSData*data=[NSDatadataWithContentsOfURL:url];NSDate*end=[NSDatedate];NSLog(@"第二步操作花费的时间为%f",[endtimeIntervalSinceDate:start]);//第二种方法CFTime 查看详情

长路漫漫,唯剑作伴--mvvm

开篇MVCModel-View-Controller是一个用来组织代码的权威范式。Apple甚至是这么说的。在MVC下,所有的对象被归类为一个model,一个view,或一个controller。Model持有数据,View显示与用户交互的界面,而ViewController调解Model和View之间的交互... 查看详情

长路漫漫,唯剑作伴--automaticreferencecounting

一、简介ARC,自动引用计数,是指iOS的内存管理使用引用计数的技术。在OC中采用AutomaticReferenceCounting的机制,让编译器进行内存管理。在新一代的AppleLLVM编译器中设置ARC为有效状态,就不用再次键入retain、release代码,这在降低... 查看详情

长路漫漫,唯剑作伴--问题答案

一、使用atomic一定是线程安全的吗?回答不是对于atomic的属性,系统生成的getter/setter会保证get、set操作的完整性,不受其他线程影响。比如,线程A的getter方法运行到一半,线程B调用了setter:那么线程A的getter还是能得到一个完... 查看详情

长路漫漫,唯剑作伴--lifecircle

一、load和initialize load在load中使用其他类是不安全的,因为其它类不一定加载完毕;load方法不遵循继承规则,也就是说,如果某个类没有实现load方法,那么不管这个类的所有超类是否实现了这个方法,这个类都不会调用load... 查看详情

长路漫漫,唯剑作伴--loadviewviewdidload及viewdidunload的关系

一、loadView什么时候被调用?每次访问UIViewController的view(比如controller.view、self.view)而且view为nil,loadView方法就会被调用。有什么作用?loadView方法是用来负责创建UIViewController的view默认实现是怎样的?它会先去查找与UIViewController... 查看详情

亚索语录

...12、想杀我?你可以试试。13、剑之故事,以血为墨。14、长路漫漫,唯剑作伴。15、吾之荣耀,离别已久。16、死亡如风,常伴吾身。17、我不会怀着耻辱而死!18、 查看详情

函数知识(长路漫漫,任重道远)

  (第一周学习计算机基础,多年的思维习惯正在努力慢慢改变,知识点主要困于循环与函数,还得多加练习,既然是自己选择的当然得站到最后!)函数:具有独立功能,能通过名称重复使用的代码。参数:完成一件事需要... 查看详情

你逛过凌晨4点的校园吗?--前端人的漫漫长路(代码片段)

大家好哇,我是小丞同学,在文章开头再简单的介绍下自己吧~我是一名前端爱好者,想要成为一名高级前端开发工程师💖大二@GDUT🏘Base:广州时间一晃而过,弹指之间,2021年已经接近尾声,过... 查看详情

亚索语录

...12、想杀我?你可以试试。13、剑之故事,以血为墨。14、长路漫漫,唯剑作伴。15、吾之荣耀,离别已久。16、死亡如风,常伴吾身。17、我不会怀着耻辱而死!18、御剑于心,且听风吟。19、无罪之人,方可安睡。20、这个故事还... 查看详情

长路漫漫,其修远兮,这一年我与csdn的故事|2021年终总结

...;这一年来经历了许多,也感悟了很多。回顾2021,长路漫漫,其修远兮,在2022也将不忘初心,砥砺前行。一.2021,我选择了“CSDN”其实人生基本就两件事,先选题,再解题。最好的人生应该是再每... 查看详情

长路漫漫,其修远兮,这一年我与csdn的故事|2021年终总结

...;这一年来经历了许多,也感悟了很多。回顾2021,长路漫漫,其修远兮,在2022也将不忘初心,砥砺前行。一.2021,我选择了“CSDN”其实人生基本就两件事,先选题,再解题。最好的人生应该是再每... 查看详情

你逛过凌晨4点的校园吗?--前端人的漫漫长路(代码片段)

大家好哇,我是小丞同学,在文章开头再简单的介绍下自己吧~我是一名前端爱好者,想要成为一名高级前端开发工程师💖大二@GDUT🏘Base:广州时间一晃而过,弹指之间,2021年已经接近尾声,过... 查看详情

你逛过凌晨4点的校园吗?--前端人的漫漫长路(代码片段)

大家好哇,我是小丞同学,在文章开头再简单的介绍下自己吧~我是一名前端爱好者,想要成为一名高级前端开发工程师💖大二@GDUT🏘Base:广州时间一晃而过,弹指之间,2021年已经接近尾声,过... 查看详情

你逛过凌晨4点的校园吗?--前端人的漫漫长路(代码片段)

大家好哇,我是小丞同学,在文章开头再简单的介绍下自己吧~我是一名前端爱好者,想要成为一名高级前端开发工程师💖大二@GDUT🏘Base:广州时间一晃而过,弹指之间,2021年已经接近尾声,过... 查看详情

你逛过凌晨4点的校园吗?--前端人的漫漫长路(代码片段)

大家好哇,我是小丞同学,在文章开头再简单的介绍下自己吧~我是一名前端爱好者,想要成为一名高级前端开发工程师💖大二@GDUT🏘Base:广州时间一晃而过,弹指之间,2021年已经接近尾声,过... 查看详情

集训0620

...时维护的未遍历的链接口最少。初步观察发现,如果一条长路一条短路,先走短路比先走长路优,因为长路上并接很多链接口的可能性很大。 查看详情