標籤:
原文:iOS image caching. Libraries benchmark (SDWebImage vs FastImageCache),譯者夜微眠(github地址),校對藍魂(部落格)、Cocoa(部落格)。
1.引言
過去的幾年裡,iOS應用在視覺方面越來越吸引人。映像展示是其中很關鍵的部分,因為大部分映像展示都需要下載並且渲染。大部分開發人員都要使用映像填充表格視圖(table views) 或者 集合視圖(collection views) 。下載圖片消耗一些資源(如蜂窩資料、電池以及CPU 等)。為了減少資源消耗,一些緩衝模型也應運而生。
為了獲得良好的使用者體驗,當我們緩衝和載入映像時,瞭解iOS底層如何處理是很重要的。此外,大多數使用了圖片緩衝的開源庫也是個不錯解決方案。
2.常用的解決途徑
非同步下載映像
處理映像(展開,去紅眼,去邊框)以便展示
寫入磁碟
需要時從磁碟讀取並展示
| 12345678910111213141516 |
// 假設我們有一個 NSURL *imageUrl and UIImageView *imageView, 我們需要通過NSURL下載圖片並在UIImageview上展示if ([self hasImageDataForURL:imageUrl] { NSData *data = [self imageDataForUrl:imageUrl]; UIImage *image = [UIImage imageWithData:imageData]; dispatch_async(dispatch_get_main_queue(), ^{ imageView.image = image; });} else { [self downloadImageFromURL:imageUrl withCompletion:^(NSData *imageData, …) { [self storeImageData:imageData …]; UIImage *image = [UIImage imageWithData:imageData]; dispatch_async(dispatch_get_main_queue(), ^{ imageView.image = image; }); }];} |
FPS 簡介
3.常用解決途徑的缺點
從磁碟載入映像或檔案時間消耗昂貴(磁碟讀取比記憶體讀取慢大概10 - 1000倍,如果是SSD硬碟 可能與記憶體讀取速度更接近(大概慢10倍)。參考這裡的比較:Introduction to RAM Disks。如果使用SSD,將獲得接近記憶體的速度(大概比記憶體訪問速度慢十倍),但目前還沒有手機和平板整合SSD模組。
建立UIImage執行個體將會在記憶體區產生一個圖片的壓縮版。但是壓縮後的映像太小且無法渲染,如果我們從磁碟載入映像,映像甚至都沒有載入到記憶體。解壓圖片同樣也很消耗資源。
設定imageView的image屬性,這種情況下將會建立一個CATransaction並加入主迴圈中。在下一次迴圈迭代中,CATransaction會對任何設定為layer contents的映像進行拷貝。
拷貝映像包含以下過程:
給檔案io 和 解壓縮 分配緩衝區
讀取磁碟資料到記憶體
解壓映像資料(產生原位元影像) - 高cpu消耗
CoreAnimation 使用解壓資料並渲染
位元組位沒有正確對齊的映像將被CoreAnimation拷貝,以修複位元組位對齊並使之能被渲染。這一點在Apple 文檔裡沒有說明,但是使用Instruments表明 CA::Render::copy_image會執行此操作,即使Core Aniation 即使沒有拷貝映像。
從iOS7 開始,第三方應用不能使用JPEG硬體解碼器。這意味著我們只能使用慢很多的軟解碼器。這一點在FastImageCache團隊的 GitHub首頁以及 Nick Lockwood的推文上都有指出。
4.一個強大的iOS映像緩衝需包含以下部分:
非同步下載映像,儘可能減少使用主線程隊列。
使用後台隊列解壓映像。這是個複雜的過程,請閱讀Avoiding Image Decompression Sickness(http://www.cocoanetics.com/2011/10/avoiding-image-decompression-sickness/)。
在記憶體和磁碟上緩衝映像。在磁碟上緩衝映像很重要,因為app可能因為記憶體不足而被強行關閉或者需要清理記憶體。這種情況下,重新從磁碟載入映像比下載會快很多。備忘:如果使用NSCache作為記憶體緩衝,當有記憶體警告時,NSCache會清空緩衝內容。NSCache相關細節請查看nshipster 上這篇文章:NSCache
儲存解壓過的圖片到硬碟以及記憶體中,以避免再次解壓。
使用GCD 和 blocks,這將使得代碼更加高效和簡單。如今GCD 和 blocks是非同步作業時必需的。
最好使用UIImageView的分類以便整合
最好在下載後以及存入到緩衝前能夠處理映像
iOS映像最佳化
更多的成像相關以及SDK架構(CoreGraphics,ImageIO,CoreAnimation,CoreImage)工作原理,CPU vs GPU 等,請閱讀@rsebbe的文章:Advanced Imaging on iOS
Core Data 是一個好的選擇嗎?
這有一篇文章--CoreData 對比File System,實現映像緩衝的基準測試。結果File System的表現更好(正如我們所預期的)
看一看上面羅列的觀點,自己實現映像緩衝不僅複雜,耗時而且痛苦。這也是為什麼我傾向於使用開源的映像緩衝解決方案。你們大部分已經聽說過SDWebImage或new FastImageCache。
為了讓你知道哪個開源庫最適合你,我做了測試並且分析它們如何滿足上述要求。
5.基準測試
測試庫:
SDWebImage - version 3.5.4
FastImageCache - version 1.2
AFNetworking - version 2.2.1
TMCache - version 1.2.0
Haneke - version 0.0.5
註:AFNetworking 加入對比是由於其自iOS7後在磁碟緩衝方面出色的表現(基於NSURLCache實現)
測試情境
對於每個庫,我都會使用全新的測試app,然後啟動app,等所有映像載入完後,慢慢滑動。然後以不同力度來回滑動(從慢到快)。接著關掉app強制應用從磁碟緩衝中載入映像,最後重複以上測試情境。
關於測試app工程
-相關demo可以在Github找到並擷取,名字是ImageCachingBenchmark。同時還有本次實驗的圖表、結果資料表以及更多。
-請注意,請注意Github上的工程和映像緩衝庫都需要做一些調整,以便能讓我們看到每一張緩衝的圖片都能夠被載入出來。由於我不想檢查Cocoapods源碼檔案(不是個好習慣),所以需要對Cocoapods clean後重新編譯工程代碼 。目前Github上的版本與我做測試的版本有些差別。
-如果你們想重新跑一下測試,你需要編寫相同completionBlock用於映像載入,所有庫得要跟預設的SDWebImage一樣返回SDImageCacheType。
最快與最慢的裝置對比結果
在Github工程上能看到完整的基準測試結果,由於這些表格很大,我只使用運行最快的裝置iPhone 5s和運行最慢的iPhone 4來測試。
匯總:
表格名詞解釋
- 非同步下載 = 庫支援非同步下載
-後台解壓 =通過後台隊列或線程執行映像解壓
-儲存解壓 = 儲存解壓後的映像版本
-記憶體/磁碟緩衝 = 支援記憶體/磁碟緩衝
-UIImageView分類 = 庫中含UIImageView 類別
-從記憶體/磁碟 = 從緩衝(記憶體/磁碟)中讀取的平均時間
6.結論
-從頭開始編寫iOS映像緩衝組件很困難
-SDWebImage 和 AFNetworking 是穩定的工程。由於有很多貢獻者,這樣保證代碼能夠及時得到維護。FastImageCache在維護方面更新很快。
-基於以上所有資料,我認為SDWebImage 在目前是一個很好的解決方案。即使有些工程使用AFNetworking 或 FastImageCache更好。但是這些都依賴於項目需求。
iOS圖片緩衝庫基準對比