AsyncDisplayKit随想

App的UIView抽象,AsyncDisplayKit能为开发者提供一种App图形性能优化的新思路。

1.引子

  Facebook的Paper团队想必大家都耳熟能详,丰富的交互动画以及多种手势操作的运用,让Paper应用独树一帜,那些眼前一亮的动画是由底层的Pop动画库提供支持的,同时为了达到复杂动画下应用流畅,必须做好深度优化,然而系统提供的UIView没有办法达到希望的效果,AsyncDisplayKit适时的推出让App的流畅度和响应性都可以达到一个新的高度,也让Paper这类应用的完整用户体验在不同硬件级别的苹果设备上得到较好的还原。下文中AsyncDisplayKit由ASDK代替。

ASDK主要运用场景:

  1. 复杂多元素视图界面。

  2. 低性能硬件设备。

2.ASDK使用入门

   ASDK的基本单元是Node,Node是UIView以及对应Layer的抽象层,与UIView的最大区别在于,Node是线程安全的,并可以设置对应的Node内部层次后在后台线程中运行,而UIView只能将大量的耗时操作全都放在主线程上进行。
  ASDisplayNode对view和Layer拥有很好的抽象,你可以很方便的访问CALayer属性,下图中展示了主要的ASDK与UIView中一一映射。

| AsyncDisplayKit | UIView |
| ------------- |:-------------:| -----:|
| ASDisplayNode | UIView subclass |
| ASControlNode | UIControl |
| ASImageNode | UIImageView |
| ASTextNode(TextKit)| UITextView|
| ASTableView| UITableView|
| ASCollectionView| UICollectionView|

  最新的ASDK中开始自己实现了一套AutoLayout,有别于Apple官方的AutoLayout,还是需要适应,不过注释写的比较清楚,可以接受,但无形中增加了学习成本以及日后的维护成本。
  在控制View的frame的时候,ASDisplayNode采用了另外一套形式,分别在以下两个方法中。

// perform expensive sizing operations on a background thread
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
{
}

// main-thread layout
- (void)layout
{
}

3.ASDK原理浅析

  AsyncDisplayKit 的Node所处的位置可以让其在后台线程中有进行高消耗运算,最后将尽量小损耗的运算放在主线程上操作。


Node Architecture(以下为部分翻译)
  创建一个Node并不会创建它的view-layer部分,这是能够以很低成本的创建nodes并且在后台线程中的原因,当你要使用一个Node的UIView和CALayer属性时,你可以通过一个代理对象如_ASPendingState,这样可以预先匹配了UIView和CALayer的默认值。

  Nodes由ASDisplayLayer和ASDisplayView构成,这些都是轻量级的创建,并添加到其他各自的层次结构,并提供集成点,允许节点充当全面的views或layers。

  当核心动画的要求要_ASDisplayLayer绘制自己,请求会转发到其节点。除非异步显示已被禁用,实际绘制调用不会立即发生或在主线程中发生。相反,一个显示Block将被添加到一个后台队列。这些block并行执行,但可以启用ASDISPLAYNODE_DELAY_DISPLAY在ASDisplayNode ( AsyncDisplay ),以连载的渲染系统进行调试。

  在ASTableView方面Cell重用很关键,但是ASTableView内部做的很好,有效的做了资源利用,就是单个Cell内部Node释放与重用问题,特别是Layer树中Layer的释放问题。资源的释放与重用是性能提升的一种有效途径。

  不能在ASNode内部直接创建一个UIView实例,这样会直接Crash。

  容器Node内部的层级结构,通过设置node.shouldRasterizeDescendants = true可以实现,那些子Node可以通过containerNode.addSubnode(suclassNode),添加Node的顺序很关键,最先添加的Node会被后续的阻挡。

  除了绘制操作会损耗性能意外,在复杂屏幕上布局计算也非常昂贵,ASDK可以做到布局和渲染都不在主线程上,太赞了。

  然并卵,对于如何明确使用ASDK,首先你要对标准UIKit的性能瓶颈有所足够了解。

4.ASDK的一些问题

  • autoLayout支持
      最初ASDK没有支持AutoLayout,然而在后续版本中提供了对应的支持。下面展示了对应的两种不同形式的frame的设置形式。
  • no-autoLayout:
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
{
  CGSize imageSize = CGSizeMake(kImageSize, kImageSize);
  CGSize textSize = [_textNode measure:CGSizeMake(constrainedSize.width - kImageSize - 2 * kOuterPadding - kInnerPadding,
                                                  constrainedSize.height)];

  // ensure there's room for the text
  CGFloat requiredHeight = MAX(textSize.height, imageSize.height);
  return CGSizeMake(constrainedSize.width, requiredHeight + 2 * kOuterPadding);
}
  • ASDK的AutoLayout抽象:
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
  ASRatioLayoutSpec *imagePlaceholder = [ASRatioLayoutSpec newWithRatio:1.0 child:_imageNode];
  imagePlaceholder.flexBasis = ASRelativeDimensionMakeWithPoints(kImageSize);

  _textNode.flexShrink = YES;

  return
  [ASInsetLayoutSpec
   newWithInsets:UIEdgeInsetsMake(kOuterPadding, kOuterPadding, kOuterPadding, kOuterPadding)
   child:
   [ASStackLayoutSpec
    newWithStyle:{
      .direction = ASStackLayoutDirectionVertical,
      .spacing = kInnerPadding
    }
    children:@[imagePlaceholder, _textNode]]];
}
  • iOS6 支持问题:
    ASTextNode中运用到了一些TextKit的特性,而这些特性CoreText的实现效果并不好,所以Paper团队放弃了TextNode这块对iOS6的支持,其他的AS*还是可以在iOS6环境下运行的,这里要吐槽一下,Facebook从去年底起选择不在支持iOS6,选择性放弃了小于5%的iOS6用户,同时天猫iPadApp在适配多iOS版本中还是遇到了很多坑,适配Reactive-Native中时iOS6中的JavascriptCore.framework产生风险,总之坑很多。

5.iOS性能优化展望

iOS图形加速优化,首先需要找到性能薄弱点,通过Instrument的Allocations,Time Profiler,Leaks配合可以观察出运行的App具体性能薄弱点在哪,是CPU损耗过多还是GPU的损耗占更多,这样才能得出接下来优化方向,否则就南辕北辙了。

下面是三个具体优化的链接:

  1. TableView的优化10Tips
  2. iOS App性能优化
  3. iOS app performance tips tricks
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容