iOS圖片快取庫基準對比

NO IMAGE
1 Star2 Stars3 Stars4 Stars5 Stars 給文章打分!
Loading...

1. 引言

過去的幾年裡,iOS 應用在視覺方面越來越吸引人。影象展示是其中很關鍵的部分,因為大部分影象展示都需要下載並且渲染。大部分開發者都要使用影象填充表格檢視(table views)或者集合檢視(collection views)。下載圖片消耗一些資源(如蜂窩資料、電池以及 CPU 等)。為了減少資源消耗,一些快取模型也應運而生。

為了獲得良好的使用者體驗,當我們快取和載入影象時,瞭解 iOS 底層如何處理是很重要的。此外,大多數使用了圖片快取的開源庫也是個不錯解決方案。

2. 常用的解決途徑

非同步下載影象
處理影象(拉伸,去紅眼,去邊框)以便展示
寫入磁碟
需要時從磁碟讀取並展示

    // 假設我們有一個 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 簡介

UI 渲染理想情況 FPS=60
60FPS => 16.7ms 每幀,這就意味著如果任何主執行緒操作大於 16.7ms,動態 FPS 將會下降,因為 CPU 忙於處理其他事情,而不是渲染 UI。

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

在記憶體和磁碟上快取影象。在磁碟上快取影象很重要,因為 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 來測試。

1422426863858832.jpg
1422426934456714.jpg
1422427092474726.jpg
1422427214870488.jpg

彙總:

1422425019671031.jpg

表格名詞解釋:

非同步下載:庫支援非同步下載
後臺解壓:通過後臺佇列或執行緒執行影象解壓
儲存解壓:儲存解壓後的影象版本
記憶體/磁碟快取:支援記憶體/磁碟快取
UIImageView 分類:庫中含 UIImageView 類別
從記憶體/磁碟:從快取(記憶體/磁碟)中讀取的平均時間

6. 結論

從頭開始編寫 iOS 影象快取元件很困難

SDWebImage 和 AFNetworking 是穩定的工程。由於有很多貢獻者,這樣保證程式碼能夠及時得到維護,FastImageCache 在維護方面更新很快。

基於以上所有資料,我認為 SDWebImage 在目前是一個很好的解決方案。即使有些工程使用 AFNetworking 或 FastImageCache 更好,但是這些都依賴於專案需求。


原文:iOS image caching. Libraries benchmark (SDWebImage vs FastImageCache)
譯者:夜微眠
校對:藍魂Cocoa
首發 CocoaChina


圖片描述

相關文章

IOS開發 最新文章