從 Auto Layout 的布局算法談性能

這是使用 ASDK 性能調(diào)優(yōu)系列的第二篇文章,前一篇文章中講到了如何提升 iOS 應(yīng)用的渲染性能犀呼,你可以點(diǎn)擊 這里 了解這部分的內(nèi)容枣察。

在上一篇文章中,我們提到了 iOS 界面的渲染過程以及如何對(duì)渲染過程進(jìn)行優(yōu)化晰奖。ASDK 的做法是將渲染繪制的工作拋到后臺(tái)線程進(jìn)行,并在每次 Runloop 結(jié)束時(shí)腥泥,將繪制結(jié)果交給 CALayer 進(jìn)行展示匾南。

而這篇文章就要從 iOS 中影響性能的另一大殺手,也就是萬惡之源 Auto Layout(自動(dòng)布局)來分析如何對(duì) iOS 應(yīng)用的性能進(jìn)行優(yōu)化以及 Auto Layout 到底為什么會(huì)影響性能蛔外?

box-layout

把 Auto Layout 批判一番

由于在 2012 年蘋果發(fā)布了 4.0 寸的 iPhone5蛆楞,在 iOS 平臺(tái)上出現(xiàn)了不同尺寸的移動(dòng)設(shè)備,使得原有的 frame 布局方式無法很好地適配不同尺寸的屏幕夹厌,所以豹爹,為了解決這一問題 Auto Layout 就誕生了。

Auto Layout 的誕生并沒有如同蘋果的其它框架一樣收到開發(fā)者的好評(píng)矛纹,它自誕生的第一天起就飽受 iOS 開發(fā)者的批評(píng)臂聋,其蹩腳、冗長(zhǎng)的語(yǔ)法使得它在剛剛面世就被無數(shù)開發(fā)者吐槽崖技,寫了幾個(gè)屏幕的代碼都不能完成一個(gè)簡(jiǎn)單的布局逻住,哪怕是 VFL(Visual Format Language)也拯救不了它。

真正使 Auto Layout 大規(guī)模投入使用的應(yīng)該還是 Masonry迎献,它使用了鏈?zhǔn)降恼Z(yǔ)法對(duì) Auto Layout 進(jìn)行了很好的封裝瞎访,使得 Auto Layout 更加簡(jiǎn)單易用;時(shí)至今日吁恍,開發(fā)者也在日常使用中發(fā)現(xiàn)了 Masonry 的各種問題扒秸,于是出現(xiàn)了各種各樣的布局框架,不過這都是后話了冀瓦。

masonry

Auto Layout 的原理和 Cassowary

Auto Layout 的原理其實(shí)非常簡(jiǎn)單伴奥,在這里通過一個(gè)例子先簡(jiǎn)單的解釋一下:

view-demonstrate

iOS 中視圖所需要的布局信息只有兩個(gè),分別是 origin/centersize翼闽,在這里我們以 origin & size 為例拾徙,也就是 frame 時(shí)代下布局的需要的兩個(gè)信息;這兩個(gè)信息由四部分組成:

  • x & y
  • width & height

以左上角的 (0, 0) 為坐標(biāo)的原點(diǎn)感局,找到坐標(biāo) (x, y)尼啡,然后繪制一個(gè)大小為 (width, height) 的矩形,這樣就完成了一個(gè)最簡(jiǎn)單的布局询微。而 Auto Layout 的布局方式與上面所說的 frame 有些不同崖瞭,frame 表示與父視圖之間的絕對(duì)距離,但是 Auto Layout 中大部分的約束都是描述性的撑毛,表示視圖間相對(duì)距離书聚,以上圖為例:

A.left = Superview.left + 50
A.top  = Superview.top + 30
A.width  = 100
A.height = 100

B.left = (A.left + A.width)/(A.right) + 30
B.top  = A.top
B.width  = A.width
B.height = A.height

雖然上面的約束很好的表示了各個(gè)視圖之間的關(guān)系,但是 Auto Layout 實(shí)際上并沒有改變?cè)械?Hard-Coded 形式的布局方式,只是將原有沒有太多意義的 (x, y) 值雌续,變成了描述性的代碼斩个。

我們?nèi)匀恍枰啦季中畔⑺枰乃牟糠?xy西雀、width 以及 height萨驶。換句話說,我們要求解上述的八元一次方程組艇肴,將每個(gè)視圖所需要的信息解出來腔呜;Cocoa 會(huì)在運(yùn)行時(shí)求解上述的方程組,最終使用 frame 來繪制視圖再悼。

layout-phase

Cassowary 算法

在上世紀(jì) 90 年代核畴,一個(gè)名叫 Cassowary 的布局算法解決了用戶界面的布局問題,它通過將布局問題抽象成線性等式和不等式約束來進(jìn)行求解冲九。

Auto Layout 其實(shí)就是對(duì) Cassowary 算法的一種實(shí)現(xiàn)谤草,但是這里并不會(huì)對(duì)它展開介紹,有興趣的讀者可以在文章最后的 Reference 中了解一下 Cassowary 算法相關(guān)的文章莺奸。

Auto Layout 的原理就是對(duì)線性方程組或者不等式的求解丑孩。

Auto Layout 的性能

在使用 Auto Layout 進(jìn)行布局時(shí),可以指定一系列的約束灭贷,比如視圖的高度温学、寬度等等。而每一個(gè)約束其實(shí)都是一個(gè)簡(jiǎn)單的線性等式或不等式甚疟,整個(gè)界面上的所有約束在一起就明確地(沒有沖突)定義了整個(gè)系統(tǒng)的布局仗岖。

在涉及沖突發(fā)生時(shí),Auto Layout 會(huì)嘗試 break 一些優(yōu)先級(jí)低的約束览妖,盡量滿足最多并且優(yōu)先級(jí)最高的約束轧拄。

因?yàn)椴季窒到y(tǒng)在最后仍然需要通過 frame 來進(jìn)行,所以 Auto Layout 雖然為開發(fā)者在描述布局時(shí)帶來了一些好處讽膏,不過它相比原有的布局系統(tǒng)加入了從約束計(jì)算 frame 的過程檩电,而在這里,我們需要了解 Auto Layout 的布局性能如何府树。

performance-loss

因?yàn)槭褂?Cassowary 算法解決約束問題就是對(duì)線性等式或不等式求解俐末,所以其時(shí)間復(fù)雜度就是多項(xiàng)式時(shí)間的,不難推測(cè)出挺尾,在處理極其復(fù)雜的 UI 界面時(shí),會(huì)造成性能上的巨大損失站绪。

在這里我們會(huì)對(duì) Auto Layout 的性能進(jìn)行測(cè)試遭铺,為了更明顯的展示 Auto Layout 的性能,我們通過 frame 的性能建立一條基準(zhǔn)線以消除對(duì)象的創(chuàng)建和銷毀、視圖的渲染魂挂、視圖層級(jí)的改變帶來的影響甫题。

你可以在 這里 找到這次對(duì) Layout 性能測(cè)量使用的代碼。

代碼分別使用 Auto Layout 和 frame 對(duì) N 個(gè)視圖進(jìn)行布局涂召,測(cè)算其運(yùn)行時(shí)間坠非。

使用 AutoLayout 時(shí),每個(gè)視圖會(huì)隨機(jī)選擇兩個(gè)視圖對(duì)它的 topleft 進(jìn)行約束果正,隨機(jī)生成一個(gè)數(shù)字作為 offset炎码;同時(shí),還會(huì)用幾個(gè)優(yōu)先級(jí)高的約束保證視圖的布局不會(huì)超出整個(gè) keyWindow秋泳。

而下圖就是對(duì) 100~1000 個(gè)視圖布局所需要的時(shí)間的折線圖潦闲。

這里的數(shù)據(jù)是在 OS X EL Captain,Macbook Air (13-inch Mid 2013)上的 iPhone 6s Plus 模擬器上采集的迫皱, Xcode 版本為 7.3.1歉闰。在其他設(shè)備上可能不會(huì)獲得一致的信息,由于筆者的 iPhone 升級(jí)到了 iOS 10卓起,所以沒有辦法真機(jī)測(cè)試和敬,最后的結(jié)果可能會(huì)有一定的偏差。

performance-chart-100-1000

從圖中可以看到戏阅,使用 Auto Layout 進(jìn)行布局的時(shí)間會(huì)是只使用 frame16 倍左右昼弟,雖然這里的測(cè)試結(jié)果可能受外界條件影響差異比較大,不過 Auto Layout 的性能相比 frame 確實(shí)差很多饲握,如果去掉設(shè)置 frame 的過程消耗的時(shí)間私杜,Auto Layout 過程進(jìn)行的計(jì)算量也是非常巨大的。

在上一篇文章中救欧,我們?cè)?jīng)提到衰粹,想要讓 iOS 應(yīng)用的視圖保持 60 FPS 的刷新頻率,我們必須在 1/60 = 16.67 ms 之內(nèi)完成包括布局笆怠、繪制以及渲染等操作铝耻。

也就是說如果當(dāng)前界面上的視圖大于 100 的話,使用 Auto Layout 是很難達(dá)到絕對(duì)流暢的要求的蹬刷;而在使用 frame 時(shí)瓢捉,同一個(gè)界面下哪怕有 500 個(gè)視圖,也是可以在 16.67 ms 之內(nèi)完成布局的办成。不過在一般情況下泡态,在 iOS 的整個(gè) UIWindow 中也不會(huì)一次性出現(xiàn)如此多的視圖。

我們更關(guān)心的是迂卢,在日常開發(fā)中難免會(huì)使用 Auto Layout 進(jìn)行布局某弦,既然有 16.67 ms 這個(gè)限制桐汤,那么在界面上出現(xiàn)了多少個(gè)視圖時(shí),我才需要考慮其它的布局方式呢靶壮?在這里怔毛,我們將需要布局的視圖數(shù)量減少一個(gè)量級(jí),重新繪制一個(gè)圖表:

performance-layout-10-90

從圖中可以看出腾降,當(dāng)對(duì) 30 個(gè)左右視圖使用 Auto Layout 進(jìn)行布局時(shí)拣度,所需要的時(shí)間就會(huì)在 16.67 ms 左右,當(dāng)然這里不排除一些其它因素的影響螃壤;到目前為止抗果,會(huì)得出一個(gè)大致的結(jié)論,使用 Auto Layout 對(duì)復(fù)雜的 UI 界面進(jìn)行布局時(shí)(大于 30 個(gè)視圖)就會(huì)對(duì)性能有嚴(yán)重的影響(同時(shí)與設(shè)備有關(guān)映穗,文章中不會(huì)考慮設(shè)備性能的差異性)窖张。

上述對(duì) Auto Layout 的使用還是比較簡(jiǎn)單的,而在日常使用中蚁滋,使用嵌套的視圖層級(jí)又非常正常宿接。

在筆者對(duì)嵌套視圖層級(jí)中使用 Auto Layout 進(jìn)行布局時(shí),當(dāng)視圖的數(shù)量超過了 500 時(shí)辕录,模擬器直接就 crash 了睦霎,所以這里沒有超過 500 個(gè)視圖的數(shù)據(jù)域蜗。

我們對(duì)嵌套視圖數(shù)量在 100~500 之間布局時(shí)間進(jìn)行測(cè)量韵丑,并與 Auto Layout 進(jìn)行比較:

performance-nested-autolayout-frame

在視圖數(shù)量大于 200 之后,隨著視圖數(shù)量的增加垦写,使用 Auto Layout 對(duì)嵌套視圖進(jìn)行布局的時(shí)間相比非嵌套的布局成倍增長(zhǎng)蚣旱。

雖然說 Auto Layout 為開發(fā)者在多尺寸布局上提供了遍歷碑幅,而且支持跨越視圖層級(jí)的約束,但是由于其實(shí)現(xiàn)原理導(dǎo)致其時(shí)間復(fù)雜度為多項(xiàng)式時(shí)間塞绿,其性能損耗是僅使用 frame 的十幾倍沟涨,所以在處理龐大的 UI 界面時(shí)表現(xiàn)差強(qiáng)人意。

在三年以前异吻,有一篇關(guān)于 Auto Layout 性能分析的文章裹赴,可以點(diǎn)擊這里了解這篇文章的內(nèi)容 Auto Layout Performance on iOS

ASDK 的布局引擎

Auto Layout 不止在復(fù)雜 UI 界面布局的表現(xiàn)不佳诀浪,它還會(huì)強(qiáng)制視圖在主線程上布局棋返;所以在 ASDK 中提供了另一種可以在后臺(tái)線程中運(yùn)行的布局引擎,它的結(jié)構(gòu)大致是這樣的:

layout-hierarchy

ASLayoutSpec 與下面的所有的 Spec 類都是繼承關(guān)系雷猪,在視圖需要布局時(shí)睛竣,會(huì)調(diào)用 ASLayoutSpec 或者它的子類的 - measureWithSizeRange: 方法返回一個(gè)用于布局的對(duì)象 ASLayout

ASLayoutable 是 ASDK 中一個(gè)協(xié)議求摇,遵循該協(xié)議的類實(shí)現(xiàn)了一系列的布局方法射沟。

當(dāng)我們使用 ASDK 布局時(shí)嫉你,需要做下面四件事情中的一件:

  • 提供 layoutSpecBlock
  • 覆寫 - layoutSpecThatFits: 方法
  • 覆寫 - calculateSizeThatFits: 方法
  • 覆寫 - calculateLayoutThatFits: 方法

只有做上面四件事情中的其中一件才能對(duì) ASDK 中的視圖或者說結(jié)點(diǎn)進(jìn)行布局。

方法 - calculateSizeThatFits: 提供了手動(dòng)布局的方式躏惋,通過在該方法內(nèi)對(duì) frame 進(jìn)行計(jì)算,返回一個(gè)當(dāng)前視圖的 CGSize嚷辅。

- layoutSpecThatFits:layoutSpecBlock 其實(shí)沒什么不同簿姨,只是前者通過覆寫方法返回 ASLayoutSpec;后者通過 block 的形式提供一種不需要子類化就可以完成布局的方法簸搞,兩者可以看做是完全等價(jià)的扁位。

- calculateLayoutThatFits: 方法有一些不同,它把上面的兩種布局方式:手動(dòng)布局和 Spec 布局封裝成了一個(gè)接口趁俊,這樣域仇,無論是 CGSize 還是 ASLayoutSpec 最后都會(huì)以 ASLayout 的形式返回給方法調(diào)用者。

手動(dòng)布局

這里簡(jiǎn)單介紹一下手動(dòng)布局使用的 -[ASDisplayNode calculatedSizeThatFits:] 方法寺擂,這個(gè)方法與 UIView 中的 -[UIView sizeThatFits:] 非常相似暇务,其區(qū)別只是在 ASDK 中,所有的計(jì)算出的大小都會(huì)通過緩存來提升性能怔软。

- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize {
  return _preferredFrameSize;
}

子類可以在這個(gè)方法中進(jìn)行計(jì)算垦细,通過覆寫這個(gè)方法返回一個(gè)合適的大小,不過一般情況下都不會(huì)使用手動(dòng)布局的方式挡逼。

使用 ASLayoutSpec 布局

在 ASDK 中括改,更加常用的是使用 ASLayoutSpec 布局,在上面提到的 ASLayout 是一個(gè)保存布局信息的媒介家坎,而真正計(jì)算視圖布局的代碼都在 ASLayoutSpec 中嘱能;所有 ASDK 中的布局(手動(dòng) / Spec)都是由 -[ASLayoutable measureWithSizeRange:] 方法觸發(fā)的,在這里我們以 ASDisplayNode 的調(diào)用棧為例看一下方法的執(zhí)行過程:

-[ASDisplayNode measureWithSizeRange:]
    -[ASDisplayNode shouldMeasureWithSizeRange:]
    -[ASDisplayNode calculateLayoutThatFits:]
        -[ASDisplayNode layoutSpecThatFits:]
        -[ASLayoutSpec measureWithSizeRange:]
        +[ASLayout layoutWithLayoutableObject:constrainedSizeRange:size:sublayouts:]
        -[ASLayout filteredNodeLayoutTree]

ASDK 的文檔中推薦在子類中覆寫 - layoutSpecThatFits: 方法虱疏,返回一個(gè)用于布局的 ASLayoutSpec 對(duì)象惹骂,然后使用 ASLayoutSpec 中的 - measureWithSizeRange: 方法對(duì)它指定的視圖進(jìn)行布局,不過通過覆寫 ASDK 的布局引擎 一節(jié)中的其它方法也都是可以的订框。

如果我們使用 ASStackLayoutSpec 對(duì)視圖進(jìn)行布局的話析苫,方法調(diào)用棧大概是這樣的:

-[ASDisplayNode measureWithSizeRange:]
    -[ASDisplayNode shouldMeasureWithSizeRange:]
    -[ASDisplayNode calculateLayoutThatFits:]
        -[ASDisplayNode layoutSpecThatFits:]
        -[ASStackLayoutSpec measureWithSizeRange:]
            ASStackUnpositionedLayout::compute
            ASStackPositionedLayout::compute            ASStackBaselinePositionedLayout::compute        +[ASLayout layoutWithLayoutableObject:constrainedSizeRange:size:sublayouts:]
        -[ASLayout filteredNodeLayoutTree]

這里只是執(zhí)行了 ASStackLayoutSpec 對(duì)應(yīng)的 - measureWithSizeRange: 方法,對(duì)其中的視圖進(jìn)行布局穿扳。在 - measureWithSizeRange: 中調(diào)用了一些 C++ 方法 ASStackUnpositionedLayout衩侥、ASStackPositionedLayout 以及 ASStackBaselinePositionedLayoutcompute 方法,這些方法完成了對(duì) ASStackLayoutSpec 中視圖的布局矛物。

相比于 Auto Layout茫死,ASDK 實(shí)現(xiàn)了一種完全不同的布局方式;比較類似與前端開發(fā)中的 Flexbox 模型履羞,而 ASDK 其實(shí)就實(shí)現(xiàn)了 Flexbox 的一個(gè)子集峦萎。

在 ASDK 1.0 時(shí)代屡久,很多開發(fā)者都表示希望 ASDK 中加入 ComponentKit 的布局引擎;而現(xiàn)在爱榔,ASDK 布局引擎的大部分代碼都是從 ComponentKit 中移植過來的(ComponentKit 是另一個(gè) Facebook 團(tuán)隊(duì)開發(fā)的用于布局的框架)被环。

ASLayout

ASLayout 表示當(dāng)前的結(jié)點(diǎn)在布局樹中的大小和位置;當(dāng)然详幽,它還有一些其它的奇怪的屬性:

@interface ASLayout : NSObject

@property (nonatomic, weak, readonly) id<ASLayoutable> layoutableObject;
@property (nonatomic, readonly) CGSize size;
@property (nonatomic, readwrite) CGPoint position;
@property (nonatomic, readonly) NSArray<ASLayout *> *sublayouts;
@property (nonatomic, readonly) CGRect frame;

...

@end

代碼中的 layoutableObject 表示當(dāng)前的對(duì)象筛欢,sublayouts 表示當(dāng)前視圖的子布局 ASLayout 數(shù)組。

整個(gè)類的實(shí)現(xiàn)都沒有什么值得多說的唇聘,除了大量的構(gòu)造方法版姑,唯一一個(gè)做了一些事情的就是 -[ASLayout filteredNodeLayoutTree] 方法了:

- (ASLayout *)filteredNodeLayoutTree {
  NSMutableArray *flattenedSublayouts = [NSMutableArray array];
  struct Context {
    ASLayout *layout;
    CGPoint absolutePosition;
  };
  std::queue<Context> queue;
  queue.push({self, CGPointMake(0, 0)});
  while (!queue.empty()) {
    Context context = queue.front();
    queue.pop();

    if (self != context.layout && context.layout.type == ASLayoutableTypeDisplayNode) {
      ASLayout *layout = [ASLayout layoutWithLayout:context.layout position:context.absolutePosition];
      layout.flattened = YES;
      [flattenedSublayouts addObject:layout];
    }
    
    for (ASLayout *sublayout in context.layout.sublayouts) {
      if (sublayout.isFlattened == NO) queue.push({sublayout, context.absolutePosition + sublayout.position});
  }

  return [ASLayout layoutWithLayoutableObject:_layoutableObject
                         constrainedSizeRange:_constrainedSizeRange
                                         size:_size
                                   sublayouts:flattenedSublayouts];
}

而這個(gè)方法也只是將 sublayouts 中的內(nèi)容展平,然后實(shí)例化一個(gè)新的 ASLayout 對(duì)象迟郎。

ASLayoutSpec

ASLayoutSpec 的作用更像是一個(gè)抽象類剥险,在真正使用 ASDK 的布局引擎時(shí),都不會(huì)直接使用這個(gè)類宪肖,而是會(huì)用類似 ASStackLayoutSpec表制、ASRelativeLayoutSpecASOverlayLayoutSpec 以及 ASRatioLayoutSpec 等子類控乾。

筆者不打算一行一行代碼深入講解其內(nèi)容夫凸,簡(jiǎn)單介紹一下最重要的 ASStackLayoutSpec

stack

ASStackLayoutSpecFlexbox 中獲得了非常多的靈感阱持,比如說 justifyContent夭拌、alignItems 等屬性,它和蘋果的 UIStackView 比較類似衷咽,不過底層并沒有使用 Auto Layout 進(jìn)行計(jì)算鸽扁。如果沒有接觸過 ASStackLayoutSpec 的開發(fā)者,可以通過這個(gè)小游戲 Foggy-ASDK-Layout 快速學(xué)習(xí) ASStackLayoutSpec 的使用镶骗。

關(guān)于緩存以及異步并發(fā)

因?yàn)橛?jì)算視圖的 CGRect 進(jìn)行布局是一種非常昂貴的操作桶现,所以 ASDK 在這里面加入了緩存機(jī)制,在每次執(zhí)行 - measureWithSizeRange: 方法時(shí)鼎姊,都會(huì)通過 -shouldMeasureWithSizeRange: 判斷是否需要重新計(jì)算布局:

- (BOOL)shouldMeasureWithSizeRange:(ASSizeRange)constrainedSize {
  return [self _hasDirtyLayout] || !ASSizeRangeEqualToSizeRange(constrainedSize, _calculatedLayout.constrainedSizeRange);
}

- (BOOL)_hasDirtyLayout {
  return _calculatedLayout == nil || _calculatedLayout.isDirty;
}

在一般情況下骡和,只有當(dāng)前結(jié)點(diǎn)被標(biāo)記為 dirty 或者這一次布局傳入的 constrainedSize 不同時(shí),才需要進(jìn)行重新計(jì)算相寇。在不需要重新計(jì)算布局的情況下慰于,只需要直接返回 _calculatedLayout 布局對(duì)象就可以了。

因?yàn)?ASDK 實(shí)現(xiàn)的布局引擎其實(shí)只是對(duì) frame 的計(jì)算唤衫,所以無論是在主線程還是后臺(tái)的異步并發(fā)進(jìn)程中都是可以執(zhí)行的婆赠,也就是說,你可以在任意線程中調(diào)用 - measureWithSizeRange: 方法佳励,ASDK 中的一些 ViewController 比如:ASDataViewController 就會(huì)在后臺(tái)并發(fā)進(jìn)程中執(zhí)行該方法:

- (NSArray<ASCellNode *> *)_layoutNodesFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts {
  ...

  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  dispatch_apply(nodeCount, queue, ^(size_t i) {
    ASIndexedNodeContext *context = contexts[i];
    ASCellNode *node = [context allocateNode];
    if (node == nil) node = [[ASCellNode alloc] init];

    CGRect frame = CGRectZero;
    frame.size = [node measureWithSizeRange:context.constrainedSize].size;
    node.frame = frame;

    [ASDataController _didLayoutNode];
  });

  ...

  return nodes;
}

上述代碼做了比較大的修改休里,將原有一些方法調(diào)用放到了當(dāng)前方法中蛆挫,并省略了大量的代碼。

關(guān)于性能的對(duì)比

由于 ASDK 的布局引擎的問題妙黍,其性能比較難以測(cè)試悴侵,在這里只對(duì) ASDK 使用 ASStackLayoutSpec布局計(jì)算時(shí)間進(jìn)行了測(cè)試,不包括視圖的渲染以及其它時(shí)間:

async-node-calculate

測(cè)試結(jié)果表明 ASStackLayoutSpec 花費(fèi)的布局時(shí)間與結(jié)點(diǎn)的數(shù)量成正比拭嫁,哪怕計(jì)算 100 個(gè)視圖的布局也只需要 8.89 ms畜挨,雖然這里沒有包括視圖的渲染時(shí)間,不過與 Auto Layout 相比性能還是有比較大的提升噩凹。

總結(jié)

其實(shí) ASDK 的布局引擎大部分都是對(duì) ComponentKit 的封裝,不過由于擺脫了 Auto Layout 這一套低效但是通用的布局方式毡咏,ASDK 的布局計(jì)算不僅在后臺(tái)并發(fā)線程中進(jìn)行驮宴、而且通過引入 Flexbox 提升了布局的性能,但是 ASDK 的使用相對(duì)比較復(fù)雜呕缭,如果只想對(duì)布局性能進(jìn)行優(yōu)化堵泽,更推薦單獨(dú)使用 ComponentKit 框架。

References

Github Repo:iOS-Source-Code-Analyze

Follow: Draveness · GitHub

Source: http://draveness.me/layout-performance

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末恢总,一起剝皮案震驚了整個(gè)濱河市迎罗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌片仿,老刑警劉巖纹安,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異砂豌,居然都是意外死亡厢岂,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門阳距,熙熙樓的掌柜王于貴愁眉苦臉地迎上來塔粒,“玉大人,你說我怎么就攤上這事筐摘∽洳纾” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵咖熟,是天一觀的道長(zhǎng)圃酵。 經(jīng)常有香客問我,道長(zhǎng)馍管,這世上最難降的妖魔是什么辜昵? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮咽斧,結(jié)果婚禮上堪置,老公的妹妹穿的比我還像新娘躬存。我一直安慰自己,他們只是感情好舀锨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布岭洲。 她就那樣靜靜地躺著,像睡著了一般坎匿。 火紅的嫁衣襯著肌膚如雪盾剩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天替蔬,我揣著相機(jī)與錄音告私,去河邊找鬼。 笑死承桥,一個(gè)胖子當(dāng)著我的面吹牛驻粟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播凶异,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蜀撑,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了剩彬?” 一聲冷哼從身側(cè)響起酷麦,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎喉恋,沒想到半個(gè)月后沃饶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡轻黑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年绍坝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片苔悦。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡轩褐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出玖详,到底是詐尸還是另有隱情把介,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布蟋座,位于F島的核電站拗踢,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏向臀。R本人自食惡果不足惜巢墅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧君纫,春花似錦驯遇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至会喝,卻和暖如春陡叠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背肢执。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工枉阵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人预茄。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓兴溜,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親反璃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容