Instagram/IGListKit 實踐談

發表於2016-12-16

簡單介紹

IGListKit是Instagram推出的新的UICollectionView框架,使用資料驅動,旨在創造一個更快更靈活的列表控制元件。
github地址:https://github.com/Instagram/IGListKit
這個全新的控制元件一出來,我就趕快投入實踐了一把。

先談一談我對這個控制元件的結論:這個框架設計的非常好,完美符合高內聚、低耦合。IGListKit 是一個很典型的使用 Objective-C 開發的,但卻是個偏向使用 Swift 語言開發者的一個 UI 元件庫。
使用過程也面臨了一些疑惑,先談一下使用收穫:

11712028-81c9910e9b51abfc
  1. 它的優勢在於flexible,比起原來的UICollectionView,在使用上更加靈活,在資料驅動上做的更好。
  2. 這個框架在fast上體現的還不夠,但不妨礙我們自己進行下一步優化。

先看看IGListKit的結構

12712028-1bbefcabe3005a04

在原來的UICollectionViewController裡的寫法,我們一定都會實現UICollectionDataSource和UICollectionViewDelegate。
不過在IGListKit的實戰過程中,你會發現似乎不用在ViewController中實現相關協議,取而代之的是SectionController來實現對應的方法:

這裡直接取了官方的Demo裡的其中一個SectionController作為例子。其實UICollectionDataSourceUICollectionViewDelegate都交給了Adapter這個介面卡中。我們來看一下IGAdapter.m檔案中的原始碼:
當我們為介面卡繫結collectionView時,呼叫如下方法

其中self是指介面卡物件。
接著介面卡作為實現資料來源協議的物件,我們來看一下它是怎麼聯絡SectionController群的。

可以看到adapter通過遍歷自己的sectionController的map來達到UICollectionView的資料來源在cellForItem如何選擇對應的sectionController。
坦白說,這樣做,給人一種全新的思路,而且以後就算自己實現其實也並不複雜,可以參考其設計。

WorkRange能做的事

什麼是WorkRange?還是用Github的官方介紹說的更快,更清楚。

13712028-408eae202f33d764

大體就是說,我們可以指定左右的Working區間,幹一些準備工作。
官網寫的不多,只說了我們可以幹事,具體幹啥事,在我的個人實踐中,我對它使用的理解是這樣的。

14712028-3e47a6a8bccb28cf
更新資料來源及預排版在ViewController進行,為Item設定layout屬性。這樣在SectionController中可以無需計算直接使用排版資料。

而將預下載或者預渲染工作放在workRange中。

15712028-cc4c4069276ec5ee

Display Delegate

我還沒來得及用到Display Delegate,但我覺得它非常適合在顯示文字的控制元件上使用非同步繪製
我們先來看一看它的呼叫順序

  1. func cellForItem(at index: Int) -> UICollectionViewCell
  2. func listAdapterwillDisplay
  3. func listAdapterdidEndDisplaying

可以發現cellForItem在willDisplay前面,於是我會選擇在cellForItem執行非同步繪製。
在listAdapterdidEndDisplaying暫停非同步繪製,最大程度上防止滑動速度過快,導致白白浪費去執行繪製任務。

和想象不一樣的資料驅動

當初看到github中官方給的圖是這樣的:

16712028-da92b9680abe5b13
我以為IGListKit裡的資料驅動是類似雙向繫結的結構,更新時不用手動顯式的呼叫Update,可實際修改資料來源模型,還是要顯式呼叫
adapter.performUpdates(animated: true, completion: nil)
而這句程式碼對應的就是

為什麼稱為Never Call呢?

再來看一下Diff演算法

簡單來說這個演算法就是計算tableView或者collectionView前後資料變化增刪改移關係的一個演算法,時間複雜度是O(n),算是IGListKit的特色特點之一。
其實這個演算法單獨拿出來不只可以計算collectionView模型,稍加改造,也適用於其他模型或者檔案的變化
使用的是Paul Heckel 的A technique for isolating differences between files 的演算法,這份paper是收費。
不過這並不妨礙我們直接看原始碼,我們可以看一下IGListDiff.mm檔案,該演算法使用C++來編寫。
主要是通過hashtable和新舊的兩個陣列結構:

17712028-71cfbf10af55361d

用簡單的例子來說,這裡我模擬的是從假設原來的 1,2,4,1的舊資料模型到新的1,2,3,5的資料模型的變化過程,假想成Swift中程式碼,應該是這樣的:

首先oldIndexs是一個棧的結構,過程是先遍歷新陣列,將陣列裡模型的id對應的hash值作為key,找到對應的Num成員物件(實際程式碼中為entry,可以理解為一種抽象)的oldIndexs棧存入NSNotFound。
再遍歷舊陣列,拿例子來說,就是將陣列裡模型的id 對應的hash值作為key,找到對應的Num成員物件裡的oldIndexs棧增加舊陣列的下標值。
如果是新增加的,那麼在hashtable中key對應的value存入的Num成員物件就是notfound。
這樣演算法如圖使用的資料結構(已簡化,實際稍複雜些),可以繫結新舊陣列的成員的對應關係,包括成員間的移動增加刪除修改關係,對於像TableView或者CollectionView非常適合不過。

相關文章