iOS圖片緩存庫基準對比

1. 引言

過去的幾年裏,iOS 應用在視覺方面愈來愈吸引人。圖像展現是其中很關鍵的部分,由於大部分圖像展現都須要下載而且渲染。大部分開發者都要使用圖像填充表格視圖(table views)或者集合視圖(collection views)。下載圖片消耗一些資源(如蜂窩數據、電池以及 CPU 等)。爲了減小資源消耗,一些緩存模型也應運而生。html

爲了得到良好的用戶體驗,當咱們緩存和加載圖像時,瞭解 iOS 底層如何處理是很重要的。此外,大多數使用了圖片緩存的開源庫也是個不錯解決方案。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 簡介git

  • 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 的圖像進行拷貝。

拷貝圖像包含如下過程:github

  • 給文件 io 和解壓縮分配緩衝區
  • 讀取磁盤數據到內存
  • 解壓圖像數據(生成原位圖) - 高 CPU 消耗
  • CoreAnimation 使用解壓數據並渲染

字節位沒有正確對齊的圖像將被 CoreAnimation 拷貝,以修復字節位對齊並使之能被渲染。這一點在 Apple 文檔裏沒有說明,可是使用 Instruments 代表 CA::Render::copy_image 會執行此操做,即便 Core Aniation 即便沒有拷貝圖像。web

從 iOS7 開始,第三方應用不能使用JPEG硬件解碼器。這意味着咱們只能使用慢不少的軟解碼器。這一點在 FastImageCache 團隊的 GitHub 主頁以及 Nick Lockwood 的推文上都有指出。vim

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 的表現更好。app

看一看上面羅列的觀點,本身實現圖像緩存不只複雜,耗時並且痛苦。這也是爲何我傾向於使用開源的圖像緩存解決方案,大家大部分已經據說過 SDWebImage 或 new FastImageCache。框架

爲了讓你知道哪一個開源庫最適合你,我作了測試而且分析它們如何知足上述要求。異步

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


圖片描述

相關文章
相關標籤/搜索