Texture 性能优化

这是 Texture 文档系列翻译,其中结合了自己的理解和工作中的使用体会。如果哪里有误,希望指出。

  1. Texture 核心概念

  2. Texture 布局 Layout

  3. Texture 便捷方法

  4. Texture 性能优化

  5. Texture 容器 Node Containers

  6. Texture 基本控件 Node

  7. Texture 中 Node 的生命周期

Layer Backing

通过使用 layer 而非 view 可以显著提高 app 性能,推荐在不需要触摸事件的 node 中开启 layer-backing。

UIKit中,手动将基于视图的代码转换为 layer 很费力,并且在需要启用触摸处理或其他特定于视图的功能时,需要手动将所有内容转换回去。

在 Texture 中,将 node 的视图及所有 subtree 转换为 layer 非常简单:

rootNode.isLayerBacked = YES;

需要转换回 view 时,只需删除这一行。

子树栅格化 Subtree Rasterization

将整个视图层级合成为一个图层可以提高性能,但在UIKit中会影响可维护性和基于层级结构的推理。

在 node 中,开启合成非常简单:

[rootNode enableSubtreeRasterization];

上述代码使从 rootNode 开始整个 node 层次结构合成为一层。

同步并发 Synchronous Concurrency

ASViewControllerASCellNode都有一个布尔类型neverShowPlaceholders属性。通过将此属性设置为YES,cell 或视图控制器的 view 绘制完成前主线程将被堵塞。

开启该属性后,并不能充分利用 Texture 的性能。通常 node 已被预加载,在进入屏幕时即将完成,因此堵塞时间很短。即使rangeTuningParameters设置为0,Texture 性能也优于UIKit。因为即使主线程堵塞,subnode 也在并发执行。

查看NSSpain 2015 talk video了解其具体行为:

node.neverShowPlaceholders = YES;

通常,cell 绘制完成前进入了屏幕会显示占位符,直到绘制完成。将neverShowPlaceholders设置为YES使 Texture 更像UIKit,即滑动时会像UIKit一样掉帧,尽管 Texture 速度会快些。

TextureSynchronousConcurrency.jpg

圆角 Corner Rounding

对于圆角处理,很多开发人员都会选择CALayercornerRadius属性。但cornerRadius会严重影响性能,应在没有其他选择时才使用。这一部分将涵盖:

  • 为何不应使用CALayercornerRadius
  • 其他更为高效实现圆角方式,以及何时使用。
  • 选择圆角处理的流程图。
  • Texture 实现圆角的方法。

CALayer 的 cornerRadius 耗费性能

使用CALayercornerRadius会触发离屏渲染(offscreen rendering),以在每帧上执行裁剪操作。滚动时每秒需要在60帧上执行裁剪操作,即使内容没有发生任何变化。这意味着 GPU 必须在每帧之间切换上下文,包括合成整个帧和裁剪。

这些对性能的消耗不会显示在 Time Profiler 中,因为其影响的是 CoreAnimation Render Server,会造成掉帧。

圆角策略

当选择圆角策略时,只需考虑以下三点:

  1. 圆角下(movement underneath the corner)是否有滑动。
  2. 是否有穿过圆角滑动(movement through the corner)。
  3. 四个圆角是否处于同一个 node 上,有没有与其他 node 相交。

圆角下滑动是指圆角后面的任何运动。例如,当一个圆角 cell 在背景上滚动时,背景就是在圆角下滑动。

为了描述穿过圆角滑动,可以想象一个小的圆角滚动视图,其中包含大很多的图片。当在滚动视图内缩放、平移图片时,照片将在滚动视图的各个角中穿过。

Texturecorner-rounding-movement.png

上图中,蓝色表示圆角下有滑动;橙色表示穿过圆角滑动。

圆角对象内部有移动,而无需移过角。下图显示了以绿色标志的内容,该内容从边缘开始插入,其边距等于圆半径大小。当内容滚动时,它不会在角落滚动。

Texturecorner-rounding-scrolling.png

使用上述方法来调整设计以消除圆角移动的影响,以便不影响性能的情况下使用cornerRadius

最后需要注意是否四个角处于同一个 node 中,或 subnode 与圆角相交。

Texturecorner-rounding-overlap.png

预合成角 Precomposited Corners

预合成角指使用贝塞尔曲线绘制角的曲线,以在 CGContext / UIGraphicsContext 中剪切内容。在这种情况下,拐角将成为图像本身的一部分,并被同步到CALayer中,有两种类型的预合成角:

最佳方案为使用预合成的不透明角(precomposited opaque corner),这是最高效的方法,可实现零 alpha 混合(尽管这比避免触发屏外渲染的重要性小很多),但其不够灵活。如果圆角的对象需要移动,则后面的背景将需要为纯色。使用 Texture 或图片背景会很棘手,推荐使用预合成的 alpha 角。

第二种涉及贝塞尔曲线的角是预合成 alpha 角(precomposited alpha corner),此方法非常灵活,是最常用的方法之一。其会增加在这个内容上进行 alpha 混合的成本,并且 alpha 通道不透明会增加25%内存消耗,但这些消耗对于现代设备来说很小,与cornerRadius触发的离屏渲染不在同一数量级。

预合成角要求角必须在同一 node,且不与 subnode 相交。不满足任一条件,则需使用裁剪圆角。

当使用了shouldRasterizeDescendants后,Texture 会自动对cornerRadius进行预合成。在开启栅格化前,请务必仔细考虑,具体可以查看子树栅格化 Subtree Rasterization

Texture 的UIImage+ASConveniences.h提供了简单、纯色圆角实现方法,支持 alpha 和不透明创建平面颜色,圆角可以调整大小,非常适合于图像 node 和ASButtonNode的背景占位符。

剪裁圆角 Clip Corner

Clip corner 通过向四个角放置四个不透明内容实现圆角。这种方法灵活、性能高。四个单独 layer 对于 CPU 来说性能消耗很少。

Textureclip-corners.png

Clip Corner 适用于以下两种情况:

  • 四个角在不同 node,或与 subnode 相交。
  • 圆角只在 node 顶部。

是否可以使用CALayer的cornerRadius

虽然很少、但也存在必须使用cornerRadius的情况。例如,在实现动画时,圆角下滑动、穿过圆角的内容是动态的,但通常可以通过改变设计利用前面的解决方案。

屏幕没有任何滑动时,使用cornerRadius对性能的影响小很多。只要屏幕有滑动,即使不涉及圆角部分,也会因使用cornerRadius对性能产生影响。例如,导航栏中有一个圆形元素,其下方有滚动视图,即使它们不重叠,也会产生影响。滑动屏幕上任何内容,均会对性能产生影响,即使用户没有与其交互。此外,任何类型的屏幕刷新,都将因cornerRadius而产生额外开销。

Rasterization 和 Layerbacking

虽然使用CALayershouldRasterize可以提高cornerRadius的性能,但这是一个未充分理解的选项,通常很危险。在无需重新栅格化时(即没有滑动、点击更改颜色,也不在移动的表视图上),可以使用shouldRasterize属性。通常,不建议使用shouldRasterize,其可能会导致更为严重的性能下降。对于本身性能不佳、坚持使用cornerRadius属性的app来说,其能够带来一定的性能提升。但如果正从零构建一款 app,强烈建议选择前面更好的圆角策略。

CALayershouldRasterize与 Texture 的node.shouldRasterizeDescendents无关,当开启shouldRasterizeDescendents后,将阻止 subnode 创建 view 和 layer。

圆角策略选取

使用下面流程图选取合适圆角策略:

Texturecorner-rounding-flowchart-v2.png

Texture 实现圆角的方式

在 Texture 中,有以下几种实现圆角的方式。

使用 cornerRadius
CGFloat cornerRadius = 20.0;
    
_photoImageNode.cornerRoundingType = ASCornerRoundingTypeDefaultSlowCALayer;
_photoImageNode.cornerRadius = cornerRadius;
使用 precomposition
CGFloat cornerRadius = 20.0;
    
_photoImageNode.cornerRoundingType = ASCornerRoundingTypePrecomposited;
_photoImageNode.cornerRadius = cornerRadius;

使用 clipping

CGFloat cornerRadius = 20.0;

_photoImageNode.cornerRoundingType = ASCornerRoundingTypeClipping;
_photoImageNode.backgroundColor = [UIColor whiteColor];
_photoImageNode.cornerRadius = cornerRadius;
使用 willDisplayNodeContentWithRenderingContext 设置裁剪路径
CGFloat cornerRadius = 20.0;
    
// Use the screen scale for corner radius to respect content scale
CGFloat screenScale = UIScreen.mainScreen.scale;
_photoImageNode.willDisplayNodeContentWithRenderingContext = ^(CGContextRef context, id drawParameters) {
    CGRect bounds = CGContextGetClipBoundingBox(context);
    CGFloat radius = cornerRadius * screenScale; 
    UIImage *overlay = [UIImage as_resizableRoundedImageWithCornerRadius:radius
                                                             cornerColor:[UIColor clearColor]
                                                               fillColor:[UIColor clearColor]];
    [overlay drawInRect:bounds];
    [[UIBezierPath bezierPathWithRoundedRect:bounds cornerRadius:radius] addClip];
};
使用 ASImageNode 获取圆形图像并添加边框

非常适合于获取圆形头像。

CGFloat cornerRadius = 20.0;

_photoImageNode.imageModificationBlock = ASImageNodeRoundBorderModificationBlock(5.0, [UIColor orangeColor]);

上一篇:Texture 便捷方法

下一篇:Texture 容器 Node Containers

欢迎更多指正:https://github.com/pro648/tips

本文地址:https://github.com/pro648/tips/blob/master/sources/Texture%20%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96.md

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

推荐阅读更多精彩内容