iOS SDWebImage 學習

官方SDWebImage的架構圖

官方SDWebImage的架構圖

SDWebImage庫的做用:

經過對UIImageView的類別擴展來實現異步加載替換圖片的工做。
主要用到的對象:html

  1. UIImageView (WebCache)類別,入口封裝,實現讀取圖片完成後的回調
  2. SDWebImageManager,對圖片進行管理的中轉站,記錄哪些圖片正在讀取
    (1)向下層讀取Cache(調用SDImageCache),或者向網絡讀取對象(調用SDWebImageDownloader)。 (2)實現SDImageCache和SDWebImageDownloader的回調
  3. SDImageCache
    (1)根據URL的MD5摘要對圖片進行存儲和讀取(實現存在內存中或者存在硬盤上兩種實現)
    (2)實現圖片和內存清理工做
  4. SDWebImageDownloader,根據URL向網絡讀取數據(實現部分讀取和所有讀取後再通知回調兩種方式)

SDWebImage 緩存流程

官方SDWebImage的流程圖

以最爲經常使用的UIImageView爲例:git

  1. UIImageView+WebCache:  setImageWithURL:placeholderImage:options: 先顯示 placeholderImage ,同時由SDWebImageManager 根據 URL 來在本地查找圖片。
  2. SDWebImageManager: downloadWithURL:delegate:options:userInfo: SDWebImageManager是將UIImageView+WebCache同SDImageCache連接起來的類, SDImageCache: queryDiskCacheForKey:delegate:userInfo:用來根據CacheKey查找圖片是否已經在緩存中
  3. 若是內存中已經有圖片緩存, SDWebImageManager會回調SDImageCacheDelegate : imageCache:didFindImage:forKey:userInfo:
  4. 而 UIImageView+WebCache 則回調SDWebImageManagerDelegate:  webImageManager:didFinishWithImage:來顯示圖片。
  5. 若是內存中沒有圖片緩存,那麼生成 NSInvocationOperation 添加到隊列,從硬盤查找圖片是否已被下載緩存。
  6. 根據 URLKey 在硬盤緩存目錄下嘗試讀取圖片文件。這一步是在 NSOperation 進行的操做,因此回主線程進行結果回調 notifyDelegate:。
  7. 若是上一操做從硬盤讀取到了圖片,將圖片添加到內存緩存中(若是空閒內存太小,會先清空內存緩存)。SDImageCacheDelegate 回調 imageCache:didFindImage:forKey:userInfo:。進而回調展現圖片。
  8. 若是從硬盤緩存目錄讀取不到圖片,說明全部緩存都不存在該圖片,須要下載圖片,回調 imageCache:didNotFindImageForKey:userInfo:。
  9. 共享或從新生成一個下載器 SDWebImageDownloader 開始下載圖片。
  10. 圖片下載由 NSURLSession 來作,實現相關 delegate 來判斷圖片下載中、下載完成和下載失敗。
  11. connection:didReceiveData: 中利用 ImageIO 作了按圖片下載進度加載效果。
  12. connectionDidFinishLoading: 數據下載完成後交給 SDWebImageDecoder 作圖片解碼處理。
  13. 圖片解碼處理在一個 NSOperationQueue 完成,不會拖慢主線程 UI。若是有須要對下載的圖片進行二次處理,最好也在這裏完成,效率會好不少。
  14. 在主線程 notifyDelegateOnMainThreadWithInfo: 宣告解碼完成,imageDecoder:didFinishDecodingImage:userInfo: 回調給 SDWebImageDownloader。
  15. imageDownloader:didFinishWithImage: 回調給 SDWebImageManager 告知圖片下載完成。
  16. 通知全部的 downloadDelegates 下載完成,回調給須要的地方展現圖片。
  17. 將圖片保存到 SDImageCache 中,內存緩存和硬盤緩存同時保存。
  18. 寫文件到硬盤在單獨 NSInvocationOperation 中完成,避免拖慢主線程。
  19. 若是是在iOS上運行,SDImageCache 在初始化的時候會註冊notification 到UIApplicationDidReceiveMemoryWarningNotification 以及 UIApplicationWillTerminateNotification,在內存警告的時候清理內存圖片緩存,應用結束的時候清理過時圖片。
  20. SDWebImagePrefetcher 能夠預先下載圖片,方便後續使用。

 

SDWebImage 使用

  • 查看緩存大小github

    - (NSString *)readSDWebImageCache {
        NSUInteger size = [SDImageCache sharedImageCache].getSize;
        // 1k = 1024, 1m = 1024k
        if (size < 1024) { // 小於1k
            return [NSString stringWithFormat:@"%ldB",(long)size];
        }else if (size < 1024 * 1024) { // 小於1m
            CGFloat aFloat = size/1024;
            return [NSString stringWithFormat:@"%.0fK",aFloat];
        }else if (size < 1024 * 1024 * 1024) { // 小於1G
            CGFloat aFloat = size/(1024 * 1024);
            return [NSString stringWithFormat:@"%.1fM",aFloat];
        }else {
            CGFloat aFloat = size/(1024*1024*1024);
            return [NSString stringWithFormat:@"%.1fG",aFloat];
        }
    }
    複製代碼
  • 清除緩存web

    - (void)clearDisk {
        NSLog(@"SDWebImageCache---%@", [self readSDWebImageCache]);
        [[SDImageCache sharedImageCache] clearDiskOnCompletion:nil];
        [[SDImageCache sharedImageCache] clearMemory]; //可不寫
        NSLog(@"SDWebImageCache2---%@", [self readSDWebImageCache]);
    }
    複製代碼

 

SDWebImage 緩存

  • 清理緩存圖片的策略:

    特別是最大緩存空間大小的設置。若是全部緩存文件的總大小超過這一大小,則會按照文件最後修改時間的逆序,以每次一半的遞歸來移除那些過早的文件,直到緩存的實際大小小於咱們設置的最大使用空間。
    注意:它默認只支持超過7天的圖片清除。不對圖片緩存大小進行控制。固然它已經作了這種機制,只是maxCacheSize爲默認值0,因此不生效。
    1. 遍歷緩存目錄使用下面函數
    NSArray *resourceKeys = @[NSURLIsDirectoryKey,   NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];
    
    NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL
                                                includingPropertiesForKeys:resourceKeys                                                              options:NSDirectoryEnumerationSkipsHiddenFiles
                                                              errorHandler:NULL];
    複製代碼
    1. 歸檔過時緩存
    for (NSURL *fileURL in fileEnumerator) {
         ......
         // 根據文件路徑最後修改時間來獲取內容
         NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];
     // 判斷是否過緩存期
         if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate])         {
             [urlsToDelete addObject:fileURL];
             continue;
         }
    
         // 這裏同時對未過時的文件根據文件大小進行歸檔,便之後續重置緩存.
         NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
         currentCacheSize += [totalAllocatedSize unsignedIntegerValue];
         [cacheFiles setObject:resourceValues forKey:fileURL];
     }
    複製代碼
    1. 刪除過時緩存
    for (NSURL *fileURL in urlsToDelete) {
         [_fileManager removeItemAtURL:fileURL error:nil];
     } 
    複製代碼
    1. 重置緩存大小
    // 依據文件修改時間,對未過時的文件進行升序排序.
     NSArray *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
                                                     usingComparator:^NSComparisonResult(id obj1, id obj2) {
                                                         return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];
                                                     }];
     
     // 根據設定的緩存大小,對當前緩存進行調整,刪除那些快過時的文件,使當前總的文件大小小與設定的緩存大小。
     for (NSURL *fileURL in sortedFiles) {
         if ([_fileManager removeItemAtURL:fileURL error:nil]) {
             NSDictionary *resourceValues = cacheFiles[fileURL];
             NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
             currentCacheSize -= [totalAllocatedSize unsignedIntegerValue];
             
             if (currentCacheSize < desiredCacheSize) {
                 break;
             }
         }
     }
    複製代碼
  • app事件註冊使用經典的觀察者模式,當觀察到內存警告、程序被終止、程序進入後臺這些事件時,程序將自動調用相應的方法處理

    當收到系統內存告警通知時,對內存緩存進行處理
    [[NSNotificationCenter defaultCenter] addObserver:self
                                               selector:@selector(clearMemory)
                                                   name:UIApplicationDidReceiveMemoryWarningNotification
                                                 object:nil];
                                                 
                                                 
    複製代碼
    當進程終止時,對緩存文件進行處理
    [[NSNotificationCenter defaultCenter] addObserver:self
                                               selector:@selector(cleanDisk)
                                                   name:UIApplicationWillTerminateNotification
                                                 object:nil];
    複製代碼
    當進入後臺運行時,對緩存文件進行處理
    [[NSNotificationCenter defaultCenter] addObserver:self
                                               selector:@selector(backgroundCleanDisk)
                                                   name:UIApplicationDidEnterBackgroundNotification
                                                 object:nil];
    複製代碼

 

SDWebImage 源碼解析

SDImageDownloader負責管理全部的下載任務,具體的下載任務由SDImageDownloaderOperation類負責。

  • SDWebImageDownloaderOperation
    SDWebImageDownloaderOperation源碼解析
    開發者就能夠不使用SDWebImage提供的下載任務類,而能夠自定義相關類,只須要遵照協議便可,SDWebImageDownloaderOperation類也遵照了該協議,該類繼承自 NSOperation 主要是爲了將任務加進併發隊列裏實現多線程下載多張圖片,真正實現下載操做的是 NSURLSessionTask 類的子類,這裏就能夠看出 SDWebImage 使用 NSURLSession 實現下載圖片的功能
  • SDImageDownloader
    SDImageDownloader源碼解析
    SDWebImage主要使用了自定義NSOperation子類,並在這個自定義NSOperation子類中經過一個可用的NSURLSession來建立一個執行服務器交互數據的NSURLSessionDataTask的下載任務,並由其全權負責下載工做,接着使用NSOperationQueue實現多線程的多圖片下載。

 

其餘

  • 常見SDWebImageOptions
    SDWebImageRetryFailed, 下載失敗後會自動從新下載
    SDWebImageLowPriority, 當正在與UI進行交互時,自動暫停內部的一些下載功能
    SDWebImageRetryFailed | SDWebImageLowPriority,同時存在上邊兩種
    SDWebImageCacheMemoryOnly, 取消磁盤緩存只有內存緩存
    SDWebImageProgressiveDownload,默認狀況,圖像會在下載完成後一次性顯示
  • 默認存儲法都是是內存緩存和磁盤緩存結合的方式。若是你只須要內存緩存,那麼在帶options選項的方法options這裏選擇SDWebImageCacheMemoryOnly就能夠了
  • 對於圖片的緩存實際應用的是NSURLCache自帶的cache機制。NSURLCache每次都要把緩存的raw data 再轉化爲UIImage
  • SDWebImage提供了以下三個category來進行緩存
    MKAnnotationView(WebCache)
    UIButton(WebCache)
    UIImageView(WebCache)
  • 好比在下載某個圖片的過程當中要響應一個事件,就覆蓋這個方法:
    [[SDWebImageManager sharedManager].imageDownloader downloadImageWithURL:urlPath options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
       NSLog(@"下載進度---%f", (float)receivedSize/expectedSize);
    } completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
       NSLog(@"下載完成---%@", [NSThread currentThread]);
    }];
    複製代碼
  • 圖片下載速度不一致,用戶快速滾動的時候,會由於cell重用致使圖片混亂
    解決辦法:MVC,使用模型保持下載的圖像,再次刷新表格。
  • 將圖像保存到模型裏的優缺點
    優勢:不用重複下載,利用MVC刷新表格,不會形成數據混亂,加載速度比較快
    缺點:內存。全部下載好的圖像,都會記錄在模型裏。若是數據比較多(2000)形成內存警告
  • 圖片格式簡介
    PNG:無損壓縮,壓縮比較低,PNG圖片通常會比JPG大。(GPU解壓縮的消耗很是小,解壓縮的速度比較快,比較清晰,蘋果推薦使用)
    JPG:有損壓縮!壓縮比很是高!照相機使用(GPU解壓縮的消耗很是大)
    GIF:動圖
    BMP:位圖,沒有任何壓縮,幾乎不用
  • SDWebImage本身的編解碼技術
    在展現一張圖片的時候常使用imageNamed:這樣的類方法去獲取並展現這張圖片,可是圖片是以二進制的格式保存在磁盤或內存中的,若是要展現一張圖片須要根據圖片的不一樣格式去解碼爲正確的位圖交由系統控件來展現,而解碼的操做默認是放在主線程執行,凡是放在主線程執行的任務都務必須要考慮清楚,若是有大量圖片要展現,就會在主線程中執行大量的解碼任務,勢必會阻塞主線程形成卡頓,因此SDWebImage本身實現相關的編解碼操做,並在子線程中處理,就不會影響主線程的相關操做

更多實用詳見 Demo VenderExplore文件夾下

相關文章
相關標籤/搜索