App的UIView抽象,AsyncDisplayKit能為開發(fā)者提供一種App圖形性能優(yōu)化的新思路。
1.引子
??Facebook的Paper團隊想必大家都耳熟能詳,豐富的交互動畫以及多種手勢操作的運用,讓Paper應用獨樹一幟,那些眼前一亮的動畫是由底層的Pop動畫庫提供支持的,同時為了達到復雜動畫下應用流暢,必須做好深度優(yōu)化,然而系統(tǒng)提供的UIView沒有辦法達到希望的效果,AsyncDisplayKit適時的推出讓App的流暢度和響應性都可以達到一個新的高度,也讓Paper這類應用的完整用戶體驗在不同硬件級別的蘋果設備上得到較好的還原。下文中AsyncDisplayKit由ASDK代替。
ASDK主要運用場景:
復雜多元素視圖界面。
低性能硬件設備。
2.ASDK使用入門
- 官方文檔
- Raywenderlich AsyncDisplaykit Tutorial 1這篇深入分析了ASDK強大之處!
- Raywenderlich AsyncDisplayKit Tutorial 2
?? ASDK的基本單元是Node,Node是UIView以及對應Layer的抽象層,與UIView的最大區(qū)別在于,Node是線程安全的,并可以設置對應的Node內(nèi)部層次后在后臺線程中運行,而UIView只能將大量的耗時操作全都放在主線程上進行。
??ASDisplayNode對view和Layer擁有很好的抽象,你可以很方便的訪問CALayer屬性,下圖中展示了主要的ASDK與UIView中一一映射。
| AsyncDisplayKit | UIView |
| ------------- |:-------------:| -----:|
| ASDisplayNode | UIView subclass |
| ASControlNode | UIControl |
| ASImageNode | UIImageView |
| ASTextNode(TextKit)| UITextView|
| ASTableView| UITableView|
| ASCollectionView| UICollectionView|
??最新的ASDK中開始自己實現(xiàn)了一套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(以下為部分翻譯)
??創(chuàng)建一個Node并不會創(chuàng)建它的view-layer部分,這是能夠以很低成本的創(chuàng)建nodes并且在后臺線程中的原因,當你要使用一個Node的UIView和CALayer屬性時,你可以通過一個代理對象如_ASPendingState,這樣可以預先匹配了UIView和CALayer的默認值。
??Nodes由ASDisplayLayer和ASDisplayView構成,這些都是輕量級的創(chuàng)建,并添加到其他各自的層次結(jié)構,并提供集成點,允許節(jié)點充當全面的views或layers。
??當核心動畫的要求要_ASDisplayLayer繪制自己,請求會轉(zhuǎn)發(fā)到其節(jié)點。除非異步顯示已被禁用,實際繪制調(diào)用不會立即發(fā)生或在主線程中發(fā)生。相反,一個顯示Block將被添加到一個后臺隊列。這些block并行執(zhí)行,但可以啟用ASDISPLAYNODE_DELAY_DISPLAY在ASDisplayNode ( AsyncDisplay ),以連載的渲染系統(tǒng)進行調(diào)試。
??在ASTableView方面Cell重用很關鍵,但是ASTableView內(nèi)部做的很好,有效的做了資源利用,就是單個Cell內(nèi)部Node釋放與重用問題,特別是Layer樹中Layer的釋放問題。資源的釋放與重用是性能提升的一種有效途徑。
??不能在ASNode內(nèi)部直接創(chuàng)建一個UIView實例,這樣會直接Crash。
??容器Node內(nèi)部的層級結(jié)構,通過設置node.shouldRasterizeDescendants = true可以實現(xiàn),那些子Node可以通過containerNode.addSubnode(suclassNode),添加Node的順序很關鍵,最先添加的Node會被后續(xù)的阻擋。
??除了繪制操作會損耗性能意外,在復雜屏幕上布局計算也非常昂貴,ASDK可以做到布局和渲染都不在主線程上,太贊了。
??然并卵,對于如何明確使用ASDK,首先你要對標準UIKit的性能瓶頸有所足夠了解。
4.ASDK的一些問題
- autoLayout支持
??最初ASDK沒有支持AutoLayout,然而在后續(xù)版本中提供了對應的支持。下面展示了對應的兩種不同形式的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的實現(xiàn)效果并不好,所以Paper團隊放棄了TextNode這塊對iOS6的支持,其他的AS*還是可以在iOS6環(huán)境下運行的,這里要吐槽一下,F(xiàn)acebook從去年底起選擇不在支持iOS6,選擇性放棄了小于5%的iOS6用戶,同時天貓iPadApp在適配多iOS版本中還是遇到了很多坑,適配Reactive-Native中時iOS6中的JavascriptCore.framework產(chǎn)生風險,總之坑很多。
5.iOS性能優(yōu)化展望
iOS圖形加速優(yōu)化,首先需要找到性能薄弱點,通過Instrument的Allocations,Time Profiler,Leaks配合可以觀察出運行的App具體性能薄弱點在哪,是CPU損耗過多還是GPU的損耗占更多,這樣才能得出接下來優(yōu)化方向,否則就南轅北轍了。
下面是三個具體優(yōu)化的鏈接: