此為SDWebImage的原始碼閱讀筆記
- 使用GCD時,如果需要比較兩個執行緒是否相等的話,可以使用獲取執行緒的label,然後比較的方法:
// SDWebImageCompat.h
// L104
if (strcmp(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL), dispatch_queue_get_label(dispatch_get_main_queue())) == 0) {
//...
}
複製程式碼
上面的程式碼會判斷當前的執行緒是否為主執行緒,因為對於UIImageView
的更新的話需要發生在主執行緒上面。
Downloader
從模組名稱我們能看出,這個模組是用於圖片下載的。
- 下載選項
typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
// 預設模式,
SDWebImageDownloaderLowPriority = 1 << 0;
// 漸進式下載,每接收到一段資料就會進行返回,可以一點一點顯示圖片
SDWebImageDownloaderProgressiveDownload = 1 << 1;
// 使用此標誌的話,使用NSURLCache
SDWebImageDownloadUseNSURLCache = 1 << 2;
// 如果圖片是從NSURLCache中讀取的話,在completion block中使用的圖片引數資料是nil
SDWebImageDownloaderIgnoreCachedResponse = 1 << 3;
// 允許在app進入後臺後,繼續圖片的下載
SDWebImageDownloaderContinueInBackground = 1 << 4;
// 處理NSHTTPCookieStore中的cookies
SDWebImageDownloaderHandleCookies = 1 << 5;
// 允許使用不被信任的證書 SDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6;
// 將圖片的下載放到優先順序比較高的執行緒
SDWebImageDownloaderHighPriority = 1 << 7;
// 縮小圖片
SDWebImageDownloaderScaleDownLargeImages = 1 << 8;
}
複製程式碼
-
SDWebImage提供了兩種圖片的下載順序,分別是
SDWebImageDownloadFIFOExecutionOrder
和SDWebImageDownloaderLIFOExecutionOrder
。不難從名稱中看出下載佇列中圖片的執行順序。 -
SDWebImageDownloadToken
:每個下載都有一個token引數,可以根據token引數的值來取消對應的下載 -
SDWebImageDownloader
是一個同步圖片下載和優化器(挑幾個自己覺得重要的屬性和方法)
shouldDecompressImage
: 一個布林值,預設是YES。表明在下載和快取圖片的過程中是否對圖片進行解壓操作,雖然可以優化圖片的顯示,但是會話費較多的記憶體。所以在記憶體不足的情況下,可以關閉這個屬性maxConcurrentDownloads
:最大的並行下載數,預設值是6downloadTimeout
: 下載的超時時長,預設為15s
-
SDWebImage內,在下載過程中,下載使用的執行緒是
NSOperation
和NSOperationQueue
組合,而處理下載操作的處理時,還有用到了GCD佇列 -
下載器的session初始化:
self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
delegate:self
delegateQueue:nil];
複製程式碼
delegate的處理佇列傳入的引數值是nil
,傳入nil的話,可以保證session建立一個序列佇列來處理方法的呼叫。會根據圖片的下載順序,對佇列中的操作新增依賴
-
在根據token的取值取消圖片下載的時候,通過
dispatch_barrier_async
對操作佇列進行barrier的操作 -
在新增回撥block的時候,會在
barrierQueue
中進行同步操作,設定token相關的引數,並新增到操作字典URLOperations
中。(SDWebImageDownloader - `addProgressCallback:completedBlock:forURL:createCallback:) -
SDWebImage中,通過繼承
NSOperation
寫了一個SDWebImageDownloaderOperation
用於下載執行緒的管理。 在NSOperation
中,作者將必要實現的方法抽象出來變成SDWebImageDownloaderOperationInterface
,如果需要高度定製下載圖片的話,需要實現介面的方法 -
SDWebImageDownloaderOperation
是一個併發操作 -
(SDWebImageDownloaderOperation.m - L308)使用
CGImageSourceCreateWithData
將獲取到的資料轉化為CGImageSourceRef
型別(作為data source),在每次獲取到新的資料時,都需要拼接到原有的資料上,再傳入到方法中,而不是隻傳入新的資料。 -
(SDWebImageDownloaderOperation.m - L310 ~ L330)當通過
NSURLSession
的delegate獲取到資料後,如果當前的圖片寬和高資訊均為0的話,則通過CGImageSourceCopyPropertiesAtIndex()
方法獲取到CFDictionaryRef
的字典,根據kCGImagePropertyPixelHeight
,kCGImagePropertyPixelWidth
和kCGImagePropertyOrientation
可以獲取到圖片的寬,高和方向這三個值(獲取圖片方向的原因是因為通過Core Graphics繪製時,會丟失方向的引數) -
(SDWebImageDownloaderOperation.m - L332 ~ L377)使用
CGImageSourceCreateImageAtIndex()
來建立CGImage
型別的image例項(記住要使用CGImageRelease()
進行釋放),接下來就是使用Core Graphics框架進行圖片的繪製,關鍵的方法CGBitmapContextCreate()
。關於使用Core Graphics進行圖片繪製的相關知識的話,可以參考官網文件。這裡就不詳細介紹了:developer.apple.com/library/con…。當繪製成功之後,轉化為UIImage
物件,使用imageWithCGImage:scale:orientation
來傳入方向引數。最後根據需求對圖片進行壓縮。
Cache
Cache是SDWebImage用於快取的模組。SDImageCache
繼承自NSObject
,有記憶體快取和硬碟快取兩種方式。對於硬碟快取的話,在進行寫操作的時候使用的是非同步寫入的方式。
-
圖片的預設快取時長為1周時間:`static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
-
在進行圖片快取的時候,每張圖片都有一個唯一的key值,以便對快取進行查詢,key值通常是圖片的絕對路徑
-
在進行快取時,使用的是一個序列的IO佇列來進行寫入
-
在對
SDImageCache
進行初始化的時候,註冊監聽了三個通知:
- 在接收到記憶體警告
UIApplicationDidReceiveMemoryWarningNotification
的時候,會清空記憶體快取 - 在app將要退出
UIApplicationWillTerminateNotification
的時候,會刪除舊檔案 - 在進入後臺
UIApplicationDidEnterBackgroundNotification
時,會在後臺執行緒中刪除舊檔案
- 快取時,會將圖片的路徑(即key值)進行MD5取值作為快取的檔名
- (nullable NSString *)cachedFileNameForKey:(nullable NSString *)key {
const char *str = key.UTF8String;
if (str == NULL) {
str = "";
}
unsigned char r[CC_MD5_DIGEST_LENGTH];
CC_MD5(str, (CC_LONG)strlen(str), r);
NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%@",
r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10],
r[11], r[12], r[13], r[14], r[15], [key.pathExtension isEqualToString:@""] ? @"" : [NSString stringWithFormat:@".%@", key.pathExtension]];
return filename;
}
複製程式碼
-
硬碟快取的儲存路徑是在'Library/Caches'路徑下。預設情況下,如果沒有取消快取策略的話,都會在記憶體快取中(
NSCache *memCache
)儲存一份,對於硬碟儲存的則是可選項. -
從快取獲取圖片的時候,首先會檢查記憶體快取,如果記憶體快取中沒有這張圖片的話,就會檢查硬碟快取,如果硬碟快取中沒有,就會開始下載。但是,如果在硬碟快取中命中的話,在返回圖片的同時,會將圖片快取到記憶體中,提高查詢效率。 想要移除圖片的話,首先移除記憶體快取中的資料,然後再移除硬碟快取中的。 在SDWebImage中,對硬碟快取的操作都是在
ioQueue
的GCD佇列中進行非同步操作,然後返回到主執行緒中執行block。避免對硬碟的讀寫操作會阻塞到主執行緒介面UI的更新 -
刪除檔案的時候,使用
NSDirectoryEnumerator
來獲取檔案的是否為資料夾的標誌以及修改日期,大小的資訊:
NSArray<NSString *> *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];
// This enumerator prefetches useful properties for our cache files.
NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL
includingPropertiesForKeys:resourceKeys
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:NULL];
複製程式碼
然後根據修改日期和需要刪除的圖片的大小來進行舊檔案的刪除。
Utils
SDWebImageManager
- 在載入圖片的過程中,SDWebImage提供幾個相關的選項:
typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
/**
* 預設情況下,當圖片URL失效或者下載失敗時,該URL會被新增到黑名單中並不會在嘗試載入。
* 此標誌可以禁止URL被新增到黑名單中
*/
SDWebImageRetryFailed = 1 << 0,
/**
* 預設情況,圖片的載入會發生在UI的互動期間。此標誌位的話會將圖片的載入延遲到UIScrollView滾動結束的期間
*/
SDWebImageLowPriority = 1 << 1,
/**
* 只將圖片快取到記憶體中
*/
SDWebImageCacheMemoryOnly = 1 << 2,
/**
* 開啟進度下載,當有資料了將載入完成的那部分顯示出來,預設情況的話,只會在圖片完全載入完畢之後才將圖片顯示出來
*/
SDWebImageProgressiveDownload = 1 << 3,
/**
* 不管圖片是否已經快取,忽略HTTP響應的快取控制並且重新整理圖片
* 這個標誌位的話可以用於同一個URL地址的圖片,有的時候,雖然URL相同,但是圖片如果有更新的話,快取機制並不知曉這點。所以需要設定這個標誌位來更新圖片
*/
SDWebImageRefreshCached = 1 << 4,
/**
* 在iOS 4+, 允許app在進入後臺後,繼續下載圖片。這會在app進入後臺時,延長app的駐留時間,在這段時間中來完成圖片的載入
*/
SDWebImageContinueInBackground = 1 << 5,
/**
* 通過將NSMutableURLRequest.HTTPShouldHandleCookies設定為YES來處理NSHTTPCookieStore中的cookie
*/
SDWebImageHandleCookies = 1 << 6,
/**
* 允許使用不被信任的證書
*/
SDWebImageAllowInvalidSSLCertificates = 1 << 7,
/**
* 預設情況下,圖片會根據佇列中的順序進行載入,這個標誌位的話會將圖片的優先順序提高,也就是將圖片的前移
*/
SDWebImageHighPriority = 1 << 8,
/**
* 通常情況下,佔點陣圖片會在圖片載入的過程中顯示,這個標誌位的話會將佔位符延遲到圖片載入完成後再進行佔位符的載入
*/
SDWebImageDelayPlaceholder = 1 << 9,
/**
* 使用這個標誌位來進行圖片的變換
*/
SDWebImageTransformAnimatedImage = 1 << 10,
/**
* 圖片通常會在載入完成後顯示到imageView上。但是有時候,可以通過這個標誌位來對圖片進行修改之後再進行顯示
*/
SDWebImageAvoidAutoSetImage = 1 << 11,
/**
* 預設情況,圖片會根據原始的大小進行解碼操作。在iOS中,會根據裝置的記憶體限制進行縮小。
*/
SDWebImageScaleDownLargeImages = 1 << 12
};
複製程式碼
- 在下載圖片過程中,如果是以下錯誤的話,則不會將圖片URL新增到黑名單當中
NSURLErrorNotConnectedToInternet
- 無法建立網路連線NSURLErrorCancelled
- 任務被取消了NSURLErrorTimedOut
- 超時NSURLErrorInternationalRoamingOff
- 無法建立漫遊網路NSURLErrorDataNotAllowed
- 行動網路不允許建立連線NSURLErrorCannotFindHost
- 無法解析主機NSURLErrorCannotConnectToHost
- 連線主機時失敗NSURLErrorNetworkConnectionLost
- 連線丟失
基本上從上述的錯誤原因能看到,當網路出現故障,無法連線到主機的時候,圖片的URL不會被新增到黑名單當中,SDWebImage會嘗試去重新連線下載
SDWebImageDecoder
-
使用
CGBitmapContextCreate
建立出來的上下文是不含有圖片的透明度資訊的 -
decodedImageWithImage:
方法實際上返回的就是去除了透明通道資訊的圖片 -
在解碼圖片時,預設情況下每一個畫素還有4個位元組
Categories
這個模組是其他Cocoa框架的類的Category
NSData+ImageContentType
通過獲取NSData
的圖片資料的第一個位元組來判斷圖片的格式
switch (c) {
case 0xFF:
return SDImageFormatJPEG;
case 0x89:
return SDImageFormatPNG;
case 0x47:
return SDImageFormatGIF;
case 0x49:
case 0x4D:
return SDImageFormatTIFF;
case 0x52:
// R as RIFF for WEBP
if (data.length < 12) {
return SDImageFormatUndefined;
}
NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
return SDImageFormatWebP;
}
}
複製程式碼
UIImage+GIF
對於GIF影象的話,獲取第一幀圖片來顯示,而完整的GIF圖片的播放則由FLAnimatedImageView
來完成。
UIImage
中有一個images
陣列屬性,對於GIF這種動態圖片而言, images
為非nil。所以根據這個變數是否為nil可以判斷是否為GIF格式
UIImage+MultiFormat
UIImage+WebP
UIView+WebCacheOperation
為UIView新增了一個動態屬性字典,用於儲存圖片的下載操作。
WebCache Categories
這個模組的主要功能是為控制元件新增圖片載入的功能。通過核心方法sd_internalSetImageWithURL:placeholderImage:options:operationKey:setImageBlock:progress:completed:
進行圖片載入