(ASDK已更名Texture)node
說到視圖性能,不能不提到UITableView,對於它的滾動性能的討論和優化從未中止。在咱們的探索過程當中,嘗試過如下一些措施:後端
還有一些諸如圓角、opaque等普通UIView可能遇到的性能瓶頸已經在第一篇中討論了一些,這裏再也不贅述。緩存
然而咱們會想,cell的異步佈局、圖片和文字渲染是否還能夠優化,預加載是否能夠更完善更智能?仍然有許多問題等待被解決。性能優化
咱們先看一下通常UITableView加載Cell的過程:session
這些操做都在cell將要進入window的一剎那發生,不難理解,在短短的16ms裏(60fps)是很難完成這些任務的,尤爲是當用戶快速滾動的時候,大量任務堆積在runloop,狀況變得雪上加霜。多線程
若是將滾動中的CPU佔用狀況用圖表顯示出來,大概是這樣的(WWDC16 session 219):
app
這其中每當一個cell將要進入屏幕,一個尖峯就會產生。而在其餘相對空閒的時候,cpu的負載至關的低。框架
天然咱們會想到,若是把任務平均分配到一個時間段內,而不是集中在某一個點,是否就能夠避免這樣的狀況發生?若是咱們可以預測一個cell很快將要進入屏幕,而此時cpu空閒,是否能夠未雨綢繆,提早作一些佈局和渲染的工做?那樣一來,在cell真正須要顯示的時候,因爲佈局和渲染結果backing store已是現成的了,只須要將它送給真正負責顯示的view就能夠,也就能夠避免產生劇烈的性能波動。異步
首先的好消息是,做爲ASDK的一員,ASTableNode以及其cellNode已經具有了異步佈局和異步渲染的能力,即便沒有作額外優化,僅僅利用ASDK通用的異步機制將耗時操做延後,相對於通常UITableView已經有了顯著的提高。雖然性能鋸齒仍然存在,可是將其轉移到了後臺線程之後,用戶感覺到的卡頓就已經不會那麼明顯了。ide
然而這些彷佛還不夠,在進入屏幕以後纔開始渲染,會有短暫的白屏現象(等待渲染完成)再顯示內容。既然渲染工做能夠在顯示以後再進行,那麼相似的,也能夠在顯示以前的一段時間,把佈局和渲染的工做預先完成。
要達到這些目的,首先介紹一些相關的類:
對於每個cell而言,本來須要在同一時間點進行的全部初始化/加載/佈局/渲染等工做,如今被均勻分配到了不一樣的狀態進行預處理。隨着用戶滾動列表,根據cell離屏幕的距離不一樣,設置相應的interfaceState並觸發不一樣階段的工做,達到均勻分配的目的。同時,因爲不須要在主線程上進行,多個cell的工做能夠經過共享後臺線程來大幅提升並行效率。
ASRangeController,一樣與ASTableNode一一對應,而且能夠根據設備性能自定義佈局、加載、渲染的工做indexPath區間,在滾動時動態高效地調整各cell的interfaceState來層層觸發不一樣顯示階段的工做,對於流暢滾動起到了相當重要的做用。
ASScrollDirection,定義了列表滾動的方向(上下左右)。在ASRangeController調整各階段的工做區間時,通常在用戶滾動的方向上須要多加載一些,而滾出屏幕的cell在必定時間內回到屏幕的機率較低,所以其分配到的資源也就相應少一些。
在ASDK1.x的時代,因爲彼時尚未ASRangeController的存在,cell的渲染只會在進入屏幕之後進行,也就是說,雖然性能可以達到60fps,可是滾動較快時,渲染跟不上,『白屏』現象就出現了。到了2.x有了ASRangeController以後,雖然在滾動極快的狀況下仍然會由於資源不足而產生白屏現象,可是在通常狀況下,由於資源分配更加合理,這個問題獲得了顯著的改善。
當同時layout多個node時,如何均勻分配工做到各個線程,同時單次不佔用過多cpu時間?
ASDK是這麼作的:
獲取當前設備上cpu的數量,並乘以每一個cpu的工做量,如4 * 5 = 20,即同一批最多對20個node進行佈局。(儘管沒有找到嚴格的文檔來講明這樣的計算方式會帶來最高的效率,可是應該要比不分批次處理更優,使佔用的cpu時間片可控)
調用dispatch_apply,對同一批次20個node進行並行佈局計算
每一批處理20個,直到全部都處理完
不光ASDisplayNode自己會根據不一樣的state進行相應的工做,它同時也提供了一系列的方法供子類override,如didEnterPreloadState/didExitVisibleState等等。在實際應用中,因爲子類一般會持有一些本身管理的資源(如圖片),須要控制在顯示/離開屏幕之際進行資源的分配/回收。因爲每一個時間點分的比較細,只要將工做均勻、合理地分配到相應的方法中,就能夠實現很是精確高效的資源調度。
ASRangeController常常須要管理屏幕外的node(可能同時有好幾屏的內容同時進行佈局計算和顯示),經過預處理來減輕未來的工做量,是一個典型的『空間換時間』的辦法,對於內存的壓力天然就會上升。
爲此,ASRangeController提供了一些參數,讓開發者能夠自行決定每一個stage所囊括的範圍,達到控制內存的:
ASLayoutRangeModeFull,此時用到的資源較多,同時用戶體驗也是最好的
ASLayoutRangeMinimum,比上一種類型節省一些資源
ASLayoutRangeModeVisibleOnly,在app退到後臺的時候自動設置,將屏幕外的node所佔用的資源釋放,下降app在系統中被殺的機率
ASLayoutRangeModeLowMemory,比上一種更省內存,對於app退到後臺,而且目前沒有在屏幕上(可能在navigation stack裏)的rangeController適用,最大程度釋放資源
咱們能夠經過調用setTuningParameters的方式,對於每一種mode中的每一種layoutRangeType作出精細調整。同時,ASDK會自動根據此時node在屏幕上的狀況,自動在以上幾種mode中來回切換,並根據指定的參數範圍來加載/釋放資源,達到資源和性能的平衡。
ASDK中還自帶了一個顯示rangeController工做狀況的小工具:
即刻在熱門頁面有4個tab,相應的右下角的debug view顯示的分別是4個頁面不一樣的working range。箭頭表示當前的滾動方向,因爲咱們配置了在滾動方向上加載的距離比反方向要更長,所以能夠看到在滾動方向上的色塊會更長一些。
即刻iOS在最初時也曾苦苦找尋列表性能優化方案。在閱讀了Ray Wenderlich的一篇文章以後,驚豔其出色的性能,開始接觸ASDK。最開始嘗試在消息頁面使用,後來在長期實踐中,發現其確實可以解決tableView的行高計算和性能問題,才漸漸在其餘頁面使用。
就如同在本系列第一篇提到的,對於先進的框架雖然不能重度依賴,咱們也願意敢於擁抱其先進之處。站在ASDK的角度看UITableView,就能有更大的空間來從新審視列表滾動的本質,將性能和資源分配效率提高到一個新的高度;即便未來脫離ASDK,仍然有一些想法值得咱們進一步思考。
想體驗即刻的朋友能夠下載看一下,最近又增長了很多新玩法 :)
PS: 咱們在招Android、爬蟲、後端的職位,連接:jike.ruguoapp.com/careers