匯總記錄:
本文基于SDWebImage 4.2.3版本進(jìn)行分析和整理。
SDWebImage
|----SDWebImageCompat處理不同平臺(iOS、TV、OS、Watch)宏,以及根據(jù)文件名@2x、@3x進(jìn)行圖片處理和縮放
|----SDWebImageOperation.h添加cancel的delegate
+----Cache
|--------SDImageCache主要處理緩存邏輯,重點(diǎn)集中在:NSCache(Memory)、Disk讀寫、清理Old File
|--------SDImageCacheConfig配置緩存參數(shù):是否壓縮、iCloud、InMemory、ReadingOption、時間和CacheSize
+----Downloader
|--------SDWebImageDownloaderOperation主要提供下載的Operation操作
|--------SDWebImageDownloader提供下載管理入口
+----Utils
|--------SDWebImageManager提供外層管理cache和download入口
|--------SDWebImagePrefetcher預(yù)處理獲取Image,主要應(yīng)用預(yù)加載的地方
+----Categories
|--------NSData+ImageContentType提供類型判斷和ImageIO類型轉(zhuǎn)換
|--------UIImage+GIFData轉(zhuǎn)UIImage(GIF)擴(kuò)展
|--------UIImage+MultiFormat提供BitMap或者未知類型的Data轉(zhuǎn)UIImage擴(kuò)展
|--------UIImage+WebPData轉(zhuǎn)WebP擴(kuò)展
|--------UIImage+ForceDecode解壓操作
|--------UIView+WebCacheOperation提供頂層關(guān)于取消和下載記錄的擴(kuò)展
+----Decoder
|--------SDWebImageCodersManager整體Coders的入口,提供是否可Coder和Coder轉(zhuǎn)發(fā)
|--------SDWebImageCoder主要說明Coder Delegate 需要實(shí)現(xiàn)的接口
|--------SDWebImageImageIOCoderPNG/JPEG的Encode和解壓操作
|--------SDWebImageGIFCoderGIF的Coder操作
|--------SDWebImageWebPCoderWebP的Coder操作
|--------SDWebImageFrame輔助類,主要在GIF等動態(tài)圖使用
|--------SDWebImageCoderHelper輔助類,包括方向、Gif圖合成等
整體組件結(jié)構(gòu)

整體框架結(jié)構(gòu)比較清晰,因為Decoder部分相對比較獨(dú)立,業(yè)務(wù)邏輯處理主要在Cache、Downloader層級以及以上。
下文會以Cache、Downloader進(jìn)行分解。
1、緩存部分解析
緩存部分邏輯主要是在SDImageCache,包括如下幾個方面:
新增
刪除
查詢
緩存管理(過期)
SDWebImage的緩存中,主要走了一套NSCache管理內(nèi)存和根據(jù)傳入的Key轉(zhuǎn)換MD5作為文件名存儲。以及創(chuàng)建了一個IO操作的Queue進(jìn)行管理IO操作。
這里重點(diǎn)注意,任何耗時:包括IO讀寫、轉(zhuǎn)碼等操作,都不應(yīng)該放到主線程里面使用。
緩存部分其他地方都比較簡單易懂,直接看源碼即可。
重點(diǎn)說下如下兩個值得學(xué)習(xí)的地方:
1、通過NSOperation管理queue任務(wù)
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock {
? ? if (!key) {
? ? ? ? if (doneBlock) {
? ? ? ? ? ? doneBlock(nil, nil, SDImageCacheTypeNone);
? ? ? ? }
? ? ? ? return nil;
? ? }
? ? // First check the in-memory cache...
? ? UIImage *image = [self imageFromMemoryCacheForKey:key];
? ? if (image) {
? ? ? ? NSData *diskData = nil;
? ? ? ? if (image.images) {
? ? ? ? ? ? diskData = [self diskImageDataBySearchingAllPathsForKey:key];
? ? ? ? }
? ? ? ? if (doneBlock) {
? ? ? ? ? ? doneBlock(image, diskData, SDImageCacheTypeMemory);
? ? ? ? }
? ? ? ? return nil;
? ? }
? ? NSOperation *operation = [NSOperation new];
? ? dispatch_async(self.ioQueue, ^{
? ? ? ? if (operation.isCancelled) {
? ? ? ? ? ? // do not call the completion if cancelled
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? @autoreleasepool {
? ? ? ? ? ? NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
? ? ? ? ? ? UIImage *diskImage = [self diskImageForKey:key];
? ? ? ? ? ? if (diskImage && self.config.shouldCacheImagesInMemory) {
? ? ? ? ? ? ? ? NSUInteger cost = SDCacheCostForImage(diskImage);
? ? ? ? ? ? ? ? [self.memCache setObject:diskImage forKey:key cost:cost];
? ? ? ? ? ? }
? ? ? ? ? ? if (doneBlock) {
? ? ? ? ? ? ? ? dispatch_async(dispatch_get_main_queue(), ^{
? ? ? ? ? ? ? ? ? ? doneBlock(diskImage, diskData, SDImageCacheTypeDisk);
? ? ? ? ? ? ? ? });
? ? ? ? ? ? }
? ? ? ? }
? ? });
? ? return operation;
}
查詢緩存的時候,這里采用了NSOperation進(jìn)行是否取消的操作,因為當(dāng)下載/緩存內(nèi)容過多時,畢定存在先后處理順序的問題,這時候可能由于用戶操作等需要取消當(dāng)前緩存處理,那么NSOperation這里起的唯一作用就是提供取消操作??梢詤⒖季唧w的Manager里面緩存調(diào)起邏輯
2、申請系統(tǒng)后臺時間處理任務(wù)
-(void)backgroundDeleteOldFiles{Class UIApplicationClass=NSClassFromString(@"UIApplication");if(!UIApplicationClass||![UIApplicationClass respondsToSelector:@selector(sharedApplication)]){return;}UIApplication*application=[UIApplication performSelector:@selector(sharedApplication)];__block UIBackgroundTaskIdentifier bgTask=[application beginBackgroundTaskWithExpirationHandler:^{// Clean up any unfinished task business by marking where you// stopped or ending the task outright.[application endBackgroundTask:bgTask];bgTask=UIBackgroundTaskInvalid;}];// Start the long-running task and return immediately.[selfdeleteOldFilesWithCompletionBlock:^{[application endBackgroundTask:bgTask];bgTask=UIBackgroundTaskInvalid;}];}
這里有個疑問點(diǎn)要注意,為啥會存在前后兩部分都去釋放Task任務(wù)。
iOS的后臺任務(wù)有個背景,不管任何時候,都需要手動去調(diào)用endBackgroundTask結(jié)束后臺任務(wù),其實(shí)開啟一個后臺job的時候,因為時長有限,所以會存在兩種結(jié)局:
在允許的時間內(nèi)執(zhí)行完成
規(guī)定時間內(nèi)未執(zhí)行完成
如上兩種情況,在結(jié)束后都必須手動調(diào)用endBackgroundTask:;
2、下載器(Downloader)
下載部分,主要是提供了一個Operation和一個Manager,其中SDWebImageDownloaderOperation里面提供了常用的Operation操作,也支持自定義的下載邏輯(實(shí)現(xiàn)SDWebImageDownloaderOperationInterface即可)。
2.1 SDWebImageDownloaderOperation 邏輯
該文件里面重點(diǎn)是Delegate:SDWebImageDownloaderOperationInterface的設(shè)計和一種實(shí)現(xiàn)方式SDWebImageDownloaderOperation(PS:優(yōu)秀的開源庫基本都會設(shè)計一套接口,再做一套基礎(chǔ)的實(shí)現(xiàn))。
// 這里描述寫的很清楚,如果需要自定義的Downloader op,那么需要繼承NSOperation并且實(shí)現(xiàn)SDWebImageDownloaderOperation
/**
Describes a downloader operation. If one wants to use a custom downloader op, it needs to inherit from `NSOperation` and conform to this protocol
*/
@protocol SDWebImageDownloaderOperationInterface<NSObject>
- (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? inSession:(nullable NSURLSession *)session
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? options:(SDWebImageDownloaderOptions)options;
- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
? ? ? ? ? ? ? ? ? ? ? ? ? ? completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
- (BOOL)shouldDecompressImages;
- (void)setShouldDecompressImages:(BOOL)value;
- (nullable NSURLCredential *)credential;
- (void)setCredential:(nullable NSURLCredential *)value;
@end
SDWebImageDownloaderOperation主要是提供內(nèi)置的下載實(shí)現(xiàn),重點(diǎn)是使用NSURLSessionTask進(jìn)行下載,邏輯不復(fù)雜,詳細(xì)的參考源碼