App页面性能优化 -- Core Animation篇

写在前面

  1. 什么时候需要考虑页面性能问题
  2. 如何进行页面性能评估
  3. 如何具体实施性能优化

PS: 任何提前优化都是魔鬼

图层混合

很多时候,界面都是会出现多个UI控件叠加的情况,如果有透明或者半透明的控件,那么GPU会去计算这些这些layer最终的显示的颜色,也就是我们肉眼所看到的效果。例如一个上层Veiw颜色是绿色RGB(0,255,0),下层又放了一个View颜色是红色RGB(0,0,255),透明度是50%,那么最终显示到我们眼前的颜色是蓝色RGB(0,127.5,127.5)。这个计算过程会消耗一定的GPU资源损耗性能。如果我们把上层的绿色View改为不透明, 那么GPU就不用耗费资源计算,直接显示绿色。在实际开发过程中应该尽量避免图层混合。

调试步骤:

  1. Command + i,进入Instruments,选择Core Animation,选择设备及调试进程(App)
  2. 勾选调试选项为Color Blended Layers
  3. 开始录制并在真机上查看页面,图层混合区域为红色,无混合区域为绿色
Core Animation界面截图
App首页猜你喜欢截图
App店铺案例截图

分析结论:
App中图片多不包含透明通道,故图片的图层混合基本上表现优于Label和Button。处理类似图层问题,需要我们自编码时添加以下代码:

label.backgroundColor = [UIColor whiteColor];
label.layer.masksToBounds = YES;

还有两个值得注意的地方,如果label的内容是中文,label实际渲染区域要大于label的size,最外层多了一个sublayer,如果不设置第二行label的边缘外层会出现图层混合的红色,因此需要在label内容是中文的情况下保证label的masksToBounds属性为YES是不会发生离屏渲染,下文会讲离屏渲染。UIImageView控件比较特殊,不仅需要自身这个容器是不透明的,并且imageView包含的内容图片也必须是不透明的,如果你自己的图片出现了图层混合红色,先检查是不是自己的代码有问题,如果确认代码没问题,就是图片自身的问题。

光栅化

光栅化是将一个layer预先渲染成位图(bitmap),再加入到缓存中,成功被缓存的layer会标注为绿色,没有成功缓存的会标注为红色,正确使用光栅化可以得到一定程度的性能提升。如需开启光栅化,需要添加如下代码:

view.layer.shouldRasterize = YES;

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

非适用情况:
如果内容会经常变动,这个时候不要开启,否则会造成性能的浪费。例如我们在使用tableViewCell中,一般不要用光栅化,因为tableViewCell的绘制非常频繁,内容在不断的变化,如果使用了光栅化,会造成大量的离屏渲染降低性能。
如果你在一个界面中使用了光栅化,刚进去这个页面的所有使用了光栅化的控件layer都会是红色,因为还没有缓存成功,如果上下滑动你会发现,layer变成了绿色。但是如果你滑动幅度较大会发现,新出现的控件会是红色然后变成绿色,因为刚开始这些控件的layer还没有缓存。

调试步骤:

  1. Command + i,进入Instruments,选择Core Animation,选择设备及调试进程(App)
  2. 勾选调试选项为Color Hits Green and Misses Red
  3. 开始录制并在真机上查看页面,缓存中(缓存未命中)区域为红色,缓存命中区域为绿色
Core Animation界面截图
App社区截图

分析结论:
可以发现,app中并没有去手动设置光栅化,所以调试时也并没有发现界面元素图层缓存命中状态,只有系统状态栏那里做了光栅化的处理。在列表滑动时可以在很短的时间内发现状态栏光栅化缓存命中状态的切换。这是因为,系统给光栅化缓存分配了一个固定的大小,因此不能过度使用,如果超出了缓存也会造成离屏渲染;缓存的时间为100ms,因此如果在100ms内没有使用缓存的对象,则会从缓存中清除。

图片颜色拷贝

苹果官方对于Color Copied Images的解释大概这个意思,如果GPU不支持当前图片的颜色格式,那么就会将图片交给CPU预先进行格式转化,并且这张图片标记为蓝色。那么GPU支持什么格式呢?苹果的GPU只解析32bit的颜色格式,如果使用Color Copied Images去调试发现是蓝色,这个时候就需要联系设计人员了。

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

调试步骤:

  1. Command + i,进入Instruments,选择Core Animation,选择设备及调试进程(App)
  2. 勾选调试选项为Color Copied Images
  3. 开始录制并在真机上查看页面,需要CPU进行格式处理的被标记为蓝色
Core Animation界面截图
App附近截图

分析结论:
有类似的问题直接找UI美眉吧~

不标准的表面颜色格式

苹果官方文档未对该选项做任何说明,调试过程中发现该选项只对UILabel生效,普通label为灰色,使用到了Attribute String的label为紫色。

调试步骤:

  1. Command + i,进入Instruments,选择Core Animation,选择设备及调试进程(App)
  2. 勾选调试选项为Color Non-Standard Surface Formats
  3. 开始录制并在真机上查看页面,需要CPU进行格式处理的被标记为蓝色
Core Animation界面截图
App附近截图

分析结论:

图片对齐

这个选项可以帮助我们查看图片大小是否正确显示。如果image size和imageView size不匹配,image会出现黄色。要尽可能的减少黄色的出现,因为image size与imageView size不匹配,会消耗资源拉伸/压缩图片。

调试步骤:

  1. Command + i,进入Instruments,选择Core Animation,选择设备及调试进程(App)
  2. 勾选调试选项为Color Misaligned Images
  3. 开始录制并在真机上查看页面,需要CPU进行格式处理的被标记为蓝色
Core Animation界面截图
App搜索截图

分析结论:
有类似的问题根据应用场景自行处理,在某些情况下我们需要系统来为我们做图片拉伸/压缩的工作,例如打标的背景图片就是做了拉伸,但对于服务图片来说,我们可能需要做图片的裁剪。

离屏渲染

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

drawRect:方法
layer.shadow
layer.allowsGroupOpacity or layer.allowsEdgeAntialiasing
layer.shouldRasterize
layer.mask
layer.masksToBounds and layer.cornerRadius

这里有需要注意的是第三条layer.shouldRasterize,其实就是光栅化,光栅化会触发离屏渲染,因此光栅化慎用。设置圆角(layer方式)会触发离屏渲染,如果在某个页面大量使用了圆角,会非常消耗性能造成FPS急剧下降,设置圆角(layer方式)触发离屏渲染要同时满足下面两个条件:

layer.masksToBounds = YES;
layer.cornerRadius = 5.f;

调试步骤:

  1. Command + i,进入Instruments,选择Core Animation,选择设备及调试进程(App)
  2. 勾选调试选项为Color Offscreen-Rendered Yellow
  3. 开始录制并在真机上查看页面,需要CPU进行格式处理的被标记为蓝色
Core Animation界面截图
App附近截图

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

  1. 阴影绘制shadow:使用ShadowPath来替代shadowOffset等属性的设置
    imageViewLayer.shadowPath = CGPathCreateWithRect(imageRect, NULL);
  2. 利用GraphicsContex生成一张带圆角的图片或者view,或者用边角图片覆盖在需要圆角的视图上。

【参考文章】

Instruments User Guide

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容