原文鏈接 - iOS 開發一定要嘗試的 Texture(ASDK)(排版正常, 包含視頻)
前言
本篇所涉及的性能問題我都將根據滑動的流暢性來評判, 包括掉幀情況和一些實際體驗
ASDK 已經改名為 Texture, 我習慣稱作 ASDK
編譯環境: MacOS 10.13.3, Xcode 9.2
參與測試機型: iPhone 6 10.3.3, iPhone 7 11.2.1, iPhone X 11.2.5, 默認 iPhone 6
TableView / TableNode 包含的 TableViewCell / CellNode: 默認復雜程度一般, 包含 1~2 張圖片和 2~4 條文本展示, 圖片有圓角
列表滑動卡頓的原因及優化
大牛們把原因都說的很清楚了, 導致的結果就是 16ms 不足以渲染一幀, 產生掉幀卡頓
下面是嘗試過的一些優化:
圓角
- 使用一張圓角圖片覆蓋, 經典文章 Corner Rounding, HYBImageCliped 也是這么做的
- 異步裁剪圖片: 通過 UIGraphics 對圖片進行裁剪, 可能造成內存暴漲
行高緩存
老生長談了, 除 UITableView-FDTemplateLayoutCell 之外, QMUI 中也有提供一套緩存行高方案
數據預加工
具體是在 JSON 轉 Model 后把文本轉為富文本, 處理一些弱邏輯等, 之后賦值就可以直接展示了
咳咳, 這個感覺不到什么效果
圖形預加工
例如處理圖片遮罩或固定的圖標, 一般是直接使用多層視圖實現
我曾嘗試把三張小圖繪制到一張大圖上再進行展示, 于是乎, 異步復用問題除外, 內存炸了, 最終還是老老實實用多個視圖實現
為什么要使用 ASDK
圖形異步渲染
通常我們認為 UIKit 是不能渲染于非主線程的, 一旦你這么做, 就可能會導致崩潰, 無法正常顯示等問題, 而 ASDK 為什么可以呢, 因為 ASDisplayNode 是線程安全的, Node 創建時, 不會立即在其內部新建 UIView 和 CALayer, 直到主線程第一次訪問時才會生成對應的對象, 除此之外, 還通過圖層預合成和基于 Runloop 的異步并發, 使其擁有更好的性能 ASAsyncTransactionGroup
這個特點帶來的相關實際體驗就是: 安心的進行異步繪圖, 如圓角裁剪, 增加遮罩等, 這在 UIKit 中是足以毀滅人生的, 內存暴漲, 異步復用, 性能極差
不過低性能設備下還是會出現明顯空白
視頻鏈接
預加載數據和對象
首先來一張 Gif 體驗一下, 實際上使用 ASDK 開發完成后對比也是如此, 有種網速變快了的錯覺
ASDK 中的 ASRangeController, ASTableView, ASCollectionView 相對于 UIKit 原生控件的特點是可用于監控視圖的可見區域, 維護工作區域, 在合適的時機觸發網絡請求以及繪制, 單元格的異步布局
這里推薦閱讀: 預加載與智能預加載(iOS)
異于原生控件的復用機制
單一的 Cell
意思是某個 List 展示的樣式只有一種, TableView 只需要注冊一個 Cell
這種情況下, 如果常規的一些優化得當, 滾動的流暢性還是可以接受的(與 ASDK 差距微小, 但仍然肉眼可分辨)
此時的差距主要體現在列表某項數據第一次展示, 以及 TableView 在分頁加載時產生的等待較長, 當然, 這兩點也是可以繼續優化和解決的
相反的, 也就是來回滑動已經展示過的數據, 兩者的差距就非常小了, 大概是 59.7 - 59.9 和 59.9 的區別 (我瞎扯的)
綜上, 優化得當的情況下, 單一的 Cell 情況下 UIKit 與 ASDK 的差距不明顯
視頻鏈接
多種 Cell
表示某 List 中有多種不同的樣式, TableView 必須要通過注冊 N 個 Cell 來實現
這種情況下, 假設有 5 種 Cell, 屏幕可同時展示 6 條 Cell, 此時若第一屏幕剛好展示的就包含全部 5 種 Cell , 那么后續的滑動情況將與單一的 Cell表現一致, 若第一屏幕展示的內容只包含一種, 其他 4 種沒有在屏幕上出現過, 那么當某一種首次出現在屏幕上時, 便會出現明顯的卡頓; 我嘗試過解決這個問題, 提前創建所有的 Cell 實例對象, 緩存和復用相同的子視圖, 異步預繪制為一張圖片并緩存(坑), 都收效漸微
因 ASDK 支持預渲染, 與處理單種 Cell 沒有區別, 依舊 59.9
復用的差別
TableView 的復用機制我是既愛又恨的, 方便之處在于直接與數據綁定后, 可以方便的更新和修改, 只需保證 setModel 簡潔就 OK, 只是當業務綁定較多時就比較麻煩了
下面重點說說 TableNode, TableNode 的復用機制就是沒有復用, 只有緩存, 每個 CellNode 都是全新的, 因此會有一些特殊的地方:
CellNode 與數據源沒有綁定關系: 創建后就算把數據源刪除, TableNode 依然可以正常展示
數據直接決定要創建一個怎樣的 CellNode: 這一點很重要, TableViewCell 的展示大致為: 添加空假數據子視圖 -> 數據填充 -> 刷新, 涉及布局或圖文時會更復雜
CellNode 只有一步: 添加真數據的子視圖; 因此可以直接根據業務邏輯對控件和布局做出處理, 而不用一次或多次刷新
Demo: 此處需求為每組一個大圖 + N個小圖, 每組 3 或 5 個
解決思路: TableView 的方式是創建 5 個, 根據數量顯隱下面兩個, 或者兩種 Cell, 把 3 和 5 的情況分別對應, 除此之外, 最重要的是: 祈禱數據正常, 每組數據個數僅為 3 或 5
此時若使用 TableNode 就靈活多了, 可以根據需要(數據個數), 加入需要的子視圖, 我的思路是把頂部的大圖固定, 剩下的兩個為一行進行添加, 就算總數為偶數也是沒有任何額外消耗的, 具體參見 ASDKDemo
視頻鏈接
Flex 布局
值得學習的理由
ASDK 使用的是 Flex 布局, 且面向對象
偷一張圖
具體對比: iOS 上的 FlexBox 布局
簡單來說, 缺點只有一個, 就是學習曲線相對 Frame AutoLayout 更陡峭, 而優點是 性能與 Frame 相當, 上手后比 AutoLayout 還簡單, 如果你已經開始嘗試, 請堅持下去
不同的方式和思想
AutoLayout
使用 AutoLayout 時我心里想的無外乎:
- 我要把你放在左上角: Left Top
- 把你放在它右邊: LeftTo(它)
- 放中間: Center
- 至少/至多離它多遠: less / greater
缺點是視圖之間的依賴性太強, 可讀性維護性較差(更差的是 Frame), 例如排列數個距離不等控件, 就會很厭煩, 然后 cv 重復代碼; 處理多個多行文本垂直排列時很惡心, 想要處理好最終需要去計算文字行高, 外加入自定行間距; ...
Flex
例如 Demo 中的
<img src="https://img.didee.cn/imgs/201...; width="300"/>
我做的事情是:
- 聲明大圖的比例: Ratio(9.0/16.0); 那么這個聲明存儲為
postImageRatioSpec
- 聲明大標題的內邊距: Inset(8, 8, 8, 8);
titleInsetSpec
- 聲明
titleInsetSpec
的位置是垂直方向下的最尾部;titleRelativeSpec
- 聲明
titleRelativeSpec
是覆蓋到postImageRatioSpec
上:titleOverlaySpec
此時大圖和文字布局完成
接下來是用戶欄:
- 聲明用戶頭像和名稱水平排列, 水平方向從左也就是從頭部開始, 距離 4, 對齊方式為居中, 此時的居中為垂直方向;
leftStackSpec
- 同理, 聲明兩個圖標為水平, 尾部起始, 距離4, 居中;
rightStackSpec
- 接下來, 聲明
leftStackSpec
和rightStackSpec
水平排列, 等間距排列填充(實際是為 left 和 rightStack 進行填充), 距離 8, 對齊方式填充(無實際作用, 由于子視圖同為 Stack 且都是水平方向);userStackSpec
最后:
- 聲明
titleOverlaySpec
和userStackSpec
垂直排列, 自上而下, 對齊方式填充(同理于userStackSpec
, 此處影響的是userStackSpec
);videoStackSpec
- 聲明
videoStackSpec
的內邊距: Inset(16, 16, 0, 16);videoInsetSpec
特別注意 userStackSpec
和 videoStackSpec
, StackSpec 多層疊加后, 父子間是存在影響的, 我在使用中也感覺比較奇怪, 具體需要自行嘗試體會..
具體實現代碼: VideoCellNode.m , UserNode.m
相關鏈接 (不分先后)
文章
iOS性能優化探討
AsyncDisplayKit 系列教程 —— 為什么要使用 AsyncDisplayKit
新大陸:AsyncDisplayKit
iOS 保持界面流暢的技巧
Texture-Resources (EN 推薦)
Getting-Started 入門教程之一
Texture 布局篇
視頻
AsyncDisplayKit State of the Code (WWDC 2016)
Demo
包含無限滾動和不定子視圖的 Demo ASDKDemo
官方示例 Texture Examples
線上項目 AppStore 耐飛視頻