iOS SDWebImage 學(xué)習(xí)

官方SDWebImage的架構(gòu)圖

SDWebImage庫(kù)的作用:

通過(guò)對(duì)UIImageView的類(lèi)別擴(kuò)展來(lái)實(shí)現(xiàn)異步加載替換圖片的工作大莫。
主要用到的對(duì)象:

  1. UIImageView (WebCache)類(lèi)別擂啥,入口封裝侨把,實(shí)現(xiàn)讀取圖片完成后的回調(diào)
  2. SDWebImageManager痹扇,對(duì)圖片進(jìn)行管理的中轉(zhuǎn)站,記錄哪些圖片正在讀取
    (1)向下層讀取Cache(調(diào)用SDImageCache)噩凹,或者向網(wǎng)絡(luò)讀取對(duì)象(調(diào)用SDWebImageDownloader)临扮。 (2)實(shí)現(xiàn)SDImageCache和SDWebImageDownloader的回調(diào)
  3. SDImageCache
    (1)根據(jù)URL的MD5摘要對(duì)圖片進(jìn)行存儲(chǔ)和讀取(實(shí)現(xiàn)存在內(nèi)存中或者存在硬盤(pán)上兩種實(shí)現(xiàn))
    (2)實(shí)現(xiàn)圖片和內(nèi)存清理工作
  4. SDWebImageDownloader剃允,根據(jù)URL向網(wǎng)絡(luò)讀取數(shù)據(jù)(實(shí)現(xiàn)部分讀取和全部讀取后再通知回調(diào)兩種方式)

SDWebImage 緩存流程

以最為常用的UIImageView為例:

  1. UIImageView+WebCache: setImageWithURL:placeholderImage:options: 先顯示 placeholderImage 沛简,同時(shí)由SDWebImageManager 根據(jù) URL 來(lái)在本地查找圖片齐鲤。
  2. SDWebImageManager: downloadWithURL:delegate:options:userInfo: SDWebImageManager是將UIImageView+WebCache同SDImageCache鏈接起來(lái)的類(lèi), SDImageCache: queryDiskCacheForKey:delegate:userInfo:用來(lái)根據(jù)CacheKey查找圖片是否已經(jīng)在緩存中
  3. 如果內(nèi)存中已經(jīng)有圖片緩存椒楣, SDWebImageManager會(huì)回調(diào)SDImageCacheDelegate : imageCache:didFindImage:forKey:userInfo:
  4. 而 UIImageView+WebCache 則回調(diào)SDWebImageManagerDelegate: webImageManager:didFinishWithImage:來(lái)顯示圖片给郊。
  5. 如果內(nèi)存中沒(méi)有圖片緩存,那么生成 NSInvocationOperation 添加到隊(duì)列捧灰,從硬盤(pán)查找圖片是否已被下載緩存淆九。
  6. 根據(jù) URLKey 在硬盤(pán)緩存目錄下嘗試讀取圖片文件。這一步是在 NSOperation 進(jìn)行的操作毛俏,所以回主線(xiàn)程進(jìn)行結(jié)果回調(diào) notifyDelegate:炭庙。
  7. 如果上一操作從硬盤(pán)讀取到了圖片,將圖片添加到內(nèi)存緩存中(如果空閑內(nèi)存過(guò)小煌寇,會(huì)先清空內(nèi)存緩存)焕蹄。SDImageCacheDelegate 回調(diào) imageCache:didFindImage:forKey:userInfo:。進(jìn)而回調(diào)展示圖片阀溶。
  8. 如果從硬盤(pán)緩存目錄讀取不到圖片腻脏,說(shuō)明所有緩存都不存在該圖片,需要下載圖片银锻,回調(diào) imageCache:didNotFindImageForKey:userInfo:永品。
  9. 共享或重新生成一個(gè)下載器 SDWebImageDownloader 開(kāi)始下載圖片。
  10. 圖片下載由 NSURLSession 來(lái)做击纬,實(shí)現(xiàn)相關(guān) delegate 來(lái)判斷圖片下載中鼎姐、下載完成和下載失敗。
  11. connection:didReceiveData: 中利用 ImageIO 做了按圖片下載進(jìn)度加載效果更振。
  12. connectionDidFinishLoading: 數(shù)據(jù)下載完成后交給 SDWebImageDecoder 做圖片解碼處理症见。
  13. 圖片解碼處理在一個(gè) NSOperationQueue 完成,不會(huì)拖慢主線(xiàn)程 UI殃饿。如果有需要對(duì)下載的圖片進(jìn)行二次處理,最好也在這里完成芋肠,效率會(huì)好很多乎芳。
  14. 在主線(xiàn)程 notifyDelegateOnMainThreadWithInfo: 宣告解碼完成,imageDecoder:didFinishDecodingImage:userInfo: 回調(diào)給 SDWebImageDownloader帖池。
  15. imageDownloader:didFinishWithImage: 回調(diào)給 SDWebImageManager 告知圖片下載完成奈惑。
  16. 通知所有的 downloadDelegates 下載完成,回調(diào)給需要的地方展示圖片睡汹。
  17. 將圖片保存到 SDImageCache 中肴甸,內(nèi)存緩存和硬盤(pán)緩存同時(shí)保存。
  18. 寫(xiě)文件到硬盤(pán)在單獨(dú) NSInvocationOperation 中完成囚巴,避免拖慢主線(xiàn)程原在。
  19. 如果是在iOS上運(yùn)行友扰,SDImageCache 在初始化的時(shí)候會(huì)注冊(cè)notification 到UIApplicationDidReceiveMemoryWarningNotification 以及 UIApplicationWillTerminateNotification,在內(nèi)存警告的時(shí)候清理內(nèi)存圖片緩存,應(yīng)用結(jié)束的時(shí)候清理過(guò)期圖片庶柿。
  20. SDWebImagePrefetcher 可以預(yù)先下載圖片村怪,方便后續(xù)使用。

SDWebImage 使用

  • 查看緩存大小

    - (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];
        }
    }
    復(fù)制代碼
    
  • 清除緩存

    - (void)clearDisk {
        NSLog(@"SDWebImageCache---%@", [self readSDWebImageCache]);
        [[SDImageCache sharedImageCache] clearDiskOnCompletion:nil];
        [[SDImageCache sharedImageCache] clearMemory]; //可不寫(xiě)
        NSLog(@"SDWebImageCache2---%@", [self readSDWebImageCache]);
    }
    復(fù)制代碼
    

SDWebImage 緩存

  • 清理緩存圖片的策略:

    特別是最大緩存空間大小的設(shè)置浮庐。如果所有緩存文件的總大小超過(guò)這一大小甚负,則會(huì)按照文件最后修改時(shí)間的逆序,以每次一半的遞歸來(lái)移除那些過(guò)早的文件审残,直到緩存的實(shí)際大小小于我們?cè)O(shè)置的最大使用空間梭域。
    注意:它默認(rèn)只支持超過(guò)7天的圖片清除。不對(duì)圖片緩存大小進(jìn)行控制搅轿。當(dāng)然它已經(jīng)做了這種機(jī)制病涨,只是maxCacheSize為默認(rèn)值0,所以不生效介时。

    1. 遍歷緩存目錄使用下面函數(shù)
    NSArray *resourceKeys = @[NSURLIsDirectoryKey,   NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];
    
    NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL
                                                includingPropertiesForKeys:resourceKeys                                                              options:NSDirectoryEnumerationSkipsHiddenFiles
                                                              errorHandler:NULL];
    復(fù)制代碼
    
    1. 歸檔過(guò)期緩存
     for (NSURL *fileURL in fileEnumerator) {
         ......
         // 根據(jù)文件路徑最后修改時(shí)間來(lái)獲取內(nèi)容
         NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];
     // 判斷是否過(guò)緩存期
         if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate])         {
             [urlsToDelete addObject:fileURL];
             continue;
         }
    
         // 這里同時(shí)對(duì)未過(guò)期的文件根據(jù)文件大小進(jìn)行歸檔没宾,便以后續(xù)重置緩存.
         NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
         currentCacheSize += [totalAllocatedSize unsignedIntegerValue];
         [cacheFiles setObject:resourceValues forKey:fileURL];
     }
    復(fù)制代碼
    
    1. 刪除過(guò)期緩存
     for (NSURL *fileURL in urlsToDelete) {
         [_fileManager removeItemAtURL:fileURL error:nil];
     } 
    復(fù)制代碼
    
    1. 重置緩存大小
    // 依據(jù)文件修改時(shí)間,對(duì)未過(guò)期的文件進(jìn)行升序排序.
     NSArray *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
                                                     usingComparator:^NSComparisonResult(id obj1, id obj2) {
                                                         return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];
                                                     }];
    
     // 根據(jù)設(shè)定的緩存大小沸柔,對(duì)當(dāng)前緩存進(jìn)行調(diào)整循衰,刪除那些快過(guò)期的文件,使當(dāng)前總的文件大小小與設(shè)定的緩存大小褐澎。
     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;
             }
         }
     }
    復(fù)制代碼
    
  • app事件注冊(cè)使用經(jīng)典的觀(guān)察者模式会钝,當(dāng)觀(guān)察到內(nèi)存警告、程序被終止工三、程序進(jìn)入后臺(tái)這些事件時(shí)迁酸,程序?qū)⒆詣?dòng)調(diào)用相應(yīng)的方法處理

    當(dāng)收到系統(tǒng)內(nèi)存告警通知時(shí),對(duì)內(nèi)存緩存進(jìn)行處理

    [[NSNotificationCenter defaultCenter] addObserver:self
                                               selector:@selector(clearMemory)
                                                   name:UIApplicationDidReceiveMemoryWarningNotification
                                                 object:nil];
    
    復(fù)制代碼
    

    當(dāng)進(jìn)程終止時(shí)俭正,對(duì)緩存文件進(jìn)行處理

    [[NSNotificationCenter defaultCenter] addObserver:self
                                               selector:@selector(cleanDisk)
                                                   name:UIApplicationWillTerminateNotification
                                                 object:nil];
    復(fù)制代碼
    

    當(dāng)進(jìn)入后臺(tái)運(yùn)行時(shí)奸鬓,對(duì)緩存文件進(jìn)行處理

    [[NSNotificationCenter defaultCenter] addObserver:self
                                               selector:@selector(backgroundCleanDisk)
                                                   name:UIApplicationDidEnterBackgroundNotification
                                                 object:nil];
    復(fù)制代碼
    

SDWebImage 源碼解析

SDImageDownloader負(fù)責(zé)管理所有的下載任務(wù),具體的下載任務(wù)由SDImageDownloaderOperation類(lèi)負(fù)責(zé)掸读。

  • SDWebImageDownloaderOperation

    開(kāi)發(fā)者就可以不使用SDWebImage提供的下載任務(wù)類(lèi)串远,而可以自定義相關(guān)類(lèi),只需要遵守協(xié)議即可儿惫,SDWebImageDownloaderOperation類(lèi)也遵守了該協(xié)議澡罚,該類(lèi)繼承自 NSOperation 主要是為了將任務(wù)加進(jìn)并發(fā)隊(duì)列里實(shí)現(xiàn)多線(xiàn)程下載多張圖片,真正實(shí)現(xiàn)下載操作的是 NSURLSessionTask 類(lèi)的子類(lèi)肾请,這里就可以看出 SDWebImage 使用 NSURLSession 實(shí)現(xiàn)下載圖片的功能

  • SDImageDownloader

    SDWebImage主要使用了自定義NSOperation子類(lèi)留搔,并在這個(gè)自定義NSOperation子類(lèi)中通過(guò)一個(gè)可用的NSURLSession來(lái)創(chuàng)建一個(gè)執(zhí)行服務(wù)器交互數(shù)據(jù)的NSURLSessionDataTask的下載任務(wù),并由其全權(quán)負(fù)責(zé)下載工作铛铁,接著使用NSOperationQueue實(shí)現(xiàn)多線(xiàn)程的多圖片下載隔显。

其他

  • 常見(jiàn)SDWebImageOptions
    SDWebImageRetryFailed, 下載失敗后會(huì)自動(dòng)重新下載
    SDWebImageLowPriority, 當(dāng)正在與UI進(jìn)行交互時(shí)却妨,自動(dòng)暫停內(nèi)部的一些下載功能
    SDWebImageRetryFailed | SDWebImageLowPriority,同時(shí)存在上邊兩種
    SDWebImageCacheMemoryOnly荣月, 取消磁盤(pán)緩存只有內(nèi)存緩存
    SDWebImageProgressiveDownload管呵,默認(rèn)情況,圖像會(huì)在下載完成后一次性顯示

  • 默認(rèn)存儲(chǔ)法都是是內(nèi)存緩存和磁盤(pán)緩存結(jié)合的方式哺窄。如果你只需要內(nèi)存緩存捐下,那么在帶options選項(xiàng)的方法options這里選擇SDWebImageCacheMemoryOnly就可以了

  • 對(duì)于圖片的緩存實(shí)際應(yīng)用的是NSURLCache自帶的cache機(jī)制。NSURLCache每次都要把緩存的raw data 再轉(zhuǎn)化為UIImage

  • SDWebImage提供了如下三個(gè)category來(lái)進(jìn)行緩存
    MKAnnotationView(WebCache)
    UIButton(WebCache)
    UIImageView(WebCache)

  • 比如在下載某個(gè)圖片的過(guò)程中要響應(yīng)一個(gè)事件萌业,就覆蓋這個(gè)方法:

    [[SDWebImageManager sharedManager].imageDownloader downloadImageWithURL:urlPath options:0 progress:^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
       NSLog(@"下載進(jìn)度---%f", (float)receivedSize/expectedSize);
    } completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
       NSLog(@"下載完成---%@", [NSThread currentThread]);
    }];
    復(fù)制代碼
    
  • 圖片下載速度不一致坷襟,用戶(hù)快速滾動(dòng)的時(shí)候,會(huì)因?yàn)閏ell重用導(dǎo)致圖片混亂
    解決辦法:MVC生年,使用模型保持下載的圖像婴程,再次刷新表格。

  • 將圖像保存到模型里的優(yōu)缺點(diǎn)
    優(yōu)點(diǎn):不用重復(fù)下載抱婉,利用MVC刷新表格档叔,不會(huì)造成數(shù)據(jù)混亂,加載速度比較快
    缺點(diǎn):內(nèi)存蒸绩。所有下載好的圖像衙四,都會(huì)記錄在模型里。如果數(shù)據(jù)比較多(2000)造成內(nèi)存警告

  • 圖片格式簡(jiǎn)介
    PNG:無(wú)損壓縮患亿,壓縮比較低传蹈,PNG圖片一般會(huì)比JPG大。(GPU解壓縮的消耗非常小步藕,解壓縮的速度比較快惦界,比較清晰,蘋(píng)果推薦使用)
    JPG:有損壓縮咙冗!壓縮比非常高沾歪!照相機(jī)使用(GPU解壓縮的消耗非常大)
    GIF:動(dòng)圖
    BMP:位圖,沒(méi)有任何壓縮雾消,幾乎不用

  • SDWebImage自己的編解碼技術(shù)
    在展示一張圖片的時(shí)候常使用imageNamed:這樣的類(lèi)方法去獲取并展示這張圖片瞬逊,但是圖片是以二進(jìn)制的格式保存在磁盤(pán)或內(nèi)存中的,如果要展示一張圖片需要根據(jù)圖片的不同格式去解碼為正確的位圖交由系統(tǒng)控件來(lái)展示仪或,而解碼的操作默認(rèn)是放在主線(xiàn)程執(zhí)行,凡是放在主線(xiàn)程執(zhí)行的任務(wù)都務(wù)必需要考慮清楚士骤,如果有大量圖片要展示范删,就會(huì)在主線(xiàn)程中執(zhí)行大量的解碼任務(wù),勢(shì)必會(huì)阻塞主線(xiàn)程造成卡頓拷肌,所以SDWebImage自己實(shí)現(xiàn)相關(guān)的編解碼操作到旦,并在子線(xiàn)程中處理旨巷,就不會(huì)影響主線(xiàn)程的相關(guān)操作

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市添忘,隨后出現(xiàn)的幾起案子采呐,更是在濱河造成了極大的恐慌,老刑警劉巖搁骑,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件斧吐,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡仲器,警方通過(guò)查閱死者的電腦和手機(jī)煤率,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)乏冀,“玉大人蝶糯,你說(shuō)我怎么就攤上這事×韭伲” “怎么了昼捍?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)肢扯。 經(jīng)常有香客問(wèn)我妒茬,道長(zhǎng),這世上最難降的妖魔是什么鹃彻? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任郊闯,我火速辦了婚禮,結(jié)果婚禮上蛛株,老公的妹妹穿的比我還像新娘团赁。我一直安慰自己,他們只是感情好谨履,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布欢摄。 她就那樣靜靜地躺著,像睡著了一般笋粟。 火紅的嫁衣襯著肌膚如雪怀挠。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天害捕,我揣著相機(jī)與錄音绿淋,去河邊找鬼。 笑死尝盼,一個(gè)胖子當(dāng)著我的面吹牛吞滞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼裁赠,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼殿漠!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起佩捞,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤绞幌,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后一忱,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體莲蜘,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年掀潮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了菇夸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡仪吧,死狀恐怖庄新,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情薯鼠,我是刑警寧澤择诈,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站出皇,受9級(jí)特大地震影響羞芍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜郊艘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一荷科、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纱注,春花似錦畏浆、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至瞎嬉,卻和暖如春蝎毡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背氧枣。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工沐兵, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人便监。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓扎谎,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子簿透,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容