該文章閱讀的SDWebImage的版本為4.3.3。
這個類是載入影像的核心類,它處理了影像快取,影像下載功能的邏輯,為外部提供了簡潔的影像載入方法。
1.公共列舉
/**
這個位列舉定義了影像載入的可選項
*/
typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
/**
預設情況下,當url無法下載時,url會被列入黑名單,之後再載入該url就不會繼續嘗試。
設定這個選項可禁用此黑名單,可以再次下載已經下載失敗過的url
*/
SDWebImageRetryFailed = 1 << 0,
/**
預設情況下,在UI互動期間影像下載就開始了。
設定這個選項可禁用此功能,比如在UIScrollView滾動變慢時就會延遲影像下載。
*/
SDWebImageLowPriority = 1 << 1,
/**
預設情況下,影像下載完成後,會被快取到記憶體和硬碟中。
設定這個選項可禁用硬碟快取,僅快取在記憶體中。
*/
SDWebImageCacheMemoryOnly = 1 << 2,
/**
預設情況下,影像僅在完全下載後顯示。
設定這個選項可啟用漸進式下載,影像在瀏覽器中下載時逐步顯示。
*/
SDWebImageProgressiveDownload = 1 << 3,
/**
預設情況下,同一個url不會重複下載,下載成功後再次載入該url會直接從快取中獲取影像。
但有一種情況是,該url對應的伺服器資源發生了變化,但是該url沒有變。
設定這個選項就可以重新下載該url並重新整理對應的快取。
*/
SDWebImageRefreshCached = 1 << 4,
/**
預設情況下,應用切到後臺後就停止下載。
設定這個選項可向系統申請額外的時間來實現後臺下載。
*/
SDWebImageContinueInBackground = 1 << 5,
/**
是否使用預設的cookie處理請求,設定這個選項就為是
*/
SDWebImageHandleCookies = 1 << 6,
/**
是否允許通過不受信任的SSL證書,,設定這個選項就為是
*/
SDWebImageAllowInvalidSSLCertificates = 1 << 7,
/**
預設情況下,影像是按順序下載。
設定這個選項可使該影像下載擁有高優先順序,以排在較前的順序下載
*/
SDWebImageHighPriority = 1 << 8,
/**
預設情況下,在載入影像時會先載入佔點陣圖。
設定這個選項可延遲載入佔點陣圖,直到影像載入完成。
*/
SDWebImageDelayPlaceholder = 1 << 9,
/**
預設情況下,處理動圖時不會呼叫imageManager:transformDownloadedImage:withURL:這個代理方法。
設定這個選項可在處理動圖時也呼叫該代理方法。
*/
SDWebImageTransformAnimatedImage = 1 << 10,
/**
預設情況下,影像下載完成後會被自動新增到imageView上。
但有一種情況是,我們想在影像被新增到imageView上之前對影像做一些處理。
設定這個選項可使影像不自動新增到imageView,而手動新增。
*/
SDWebImageAvoidAutoSetImage = 1 << 11,
/**
預設情況下,影像會根據其原始大小進行解碼。
設定這個選項可壓縮影像大小。
但是如果也同時設定了SDWebImageProgressiveDownload這個選項,壓縮影像的功能就會失效。
*/
SDWebImageScaleDownLargeImages = 1 << 12,
/**
預設情況下,當記憶體中有快取時,就不會再去查詢硬碟中的快取。
設定這個選項可強制同時查詢硬碟中的快取。
但是建議和SDWebImageQueryDiskSync一起使用,以確保影像在同一個runloop中載入。
*/
SDWebImageQueryDataWhenInMemory = 1 << 13,
/**
預設情況下,是同步查詢記憶體快取,非同步查詢硬碟快取。 設定這個選項可強制同步查詢硬碟快取,以確保在同一個runloop中載入影像。
*/
SDWebImageQueryDiskSync = 1 << 14,
/**
預設情況下,當沒有快取時,才從網路下載影像。
設定這個選項可強制從網路下載影像。
*/
SDWebImageFromCacheOnly = 1 << 15,
/**
預設情況下,使用SDWebImageTransition新增的過渡動畫只適用於從網路下載的影像。
設定這個選項可使過渡動畫也強制適用於從快取獲取的影像
*/
SDWebImageForceTransition = 1 << 16
};
複製程式碼
2.公共型別定義
/**
用於外部分類中的完成回撥block
*/
typedef void(^SDExternalCompletionBlock)(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL);
複製程式碼
/**
用於該類內部中的完成回撥block
*/
typedef void(^SDInternalCompletionBlock)(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL);
複製程式碼
/**
用於將url處理成快取用的key的方法,可以刪除url中的查詢欄位
*/
typedef NSString * _Nullable(^SDWebImageCacheKeyFilterBlock)(NSURL * _Nullable url);
複製程式碼
/**
用於將影像快取到硬碟的解碼演算法
*/
typedef NSData * _Nullable(^SDWebImageCacheSerializerBlock)(UIImage * _Nonnull image, NSData * _Nullable data, NSURL * _Nullable imageURL);
複製程式碼
3.SDWebImageManagerDelegate代理
/**
如果沒找到url對應的快取,url需要從網路下載時會呼叫這個代理方法
*/
- (BOOL)imageManager:(nonnull SDWebImageManager *)imageManager shouldDownloadImageForURL:(nullable NSURL *)imageURL;
複製程式碼
/**
如果url對應的影像下載失敗了,就會呼叫這個代理方法
*/
- (BOOL)imageManager:(nonnull SDWebImageManager *)imageManager shouldBlockFailedURL:(nonnull NSURL *)imageURL withError:(nonnull NSError *)error;
複製程式碼
/**
影像下載完成以後,如果想轉換圖片就呼叫這個代理方法
*/
- (nullable UIImage *)imageManager:(nonnull SDWebImageManager *)imageManager transformDownloadedImage:(nullable UIImage *)image withURL:(nullable NSURL *)imageURL;
複製程式碼
4.公共屬性
/**
代理
*/
@property (weak, nonatomic, nullable) id <SDWebImageManagerDelegate> delegate;
複製程式碼
/**
影像快取物件
*/
@property (strong, nonatomic, readonly, nullable) SDImageCache *imageCache;
複製程式碼
/**
影像下載器
*/
@property (strong, nonatomic, readonly, nullable) SDWebImageDownloader *imageDownloader;
複製程式碼
/**
將url處理成key的方法
*/
@property (nonatomic, copy, nullable) SDWebImageCacheKeyFilterBlock cacheKeyFilter;
複製程式碼
/**
影像解碼的方法
*/
@property (nonatomic, copy, nullable) SDWebImageCacheSerializerBlock cacheSerializer;
複製程式碼
5.公共方法
/**
獲取單例物件的方法
*/
+ (nonnull instancetype)sharedManager;
複製程式碼
/**
以指定影像快取物件和影像下載器初始化物件
*/
- (nonnull instancetype)initWithCache:(nonnull SDImageCache *)cache downloader:(nonnull SDWebImageDownloader *)downloader NS_DESIGNATED_INITIALIZER;
複製程式碼
/**
載入url影像的方法,如果沒有快取就從網路下載
*/
- (nullable id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock;
複製程式碼
/**
以指定的url快取指定影像
*/
- (void)saveImageToCache:(nullable UIImage *)image forURL:(nullable NSURL *)url;
複製程式碼
/**
取消當前所有操作
*/
- (void)cancelAll;
複製程式碼
/**
檢視當前是否有操作正在進行
*/
- (BOOL)isRunning;
複製程式碼
/**
非同步查詢指定url是否有快取
*/
- (void)cachedImageExistsForURL:(nullable NSURL *)url
completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
複製程式碼
/**
非同步查詢指定url在硬碟中是否有快取
*/
- (void)diskImageExistsForURL:(nullable NSURL *)url
completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
複製程式碼
/**
獲取指定url的快取key
*/
- (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url;
複製程式碼
6.SDWebImageCombinedOperation類
6.1.屬性
/**
取消
*/
@property (assign, nonatomic, getter = isCancelled) BOOL cancelled;
複製程式碼
/**
影像下載token
*/
@property (strong, nonatomic, nullable) SDWebImageDownloadToken *downloadToken;
複製程式碼
/**
快取操作物件
*/
@property (strong, nonatomic, nullable) NSOperation *cacheOperation;
複製程式碼
/**
影像管理者物件
*/
@property (weak, nonatomic, nullable) SDWebImageManager *manager;
複製程式碼
6.2.方法
- (void)cancel {
// 加鎖
@synchronized(self) {
// 記錄取消狀態
self.cancelled = YES;
// 如果正在快取就取消快取,並將屬性置空
if (self.cacheOperation) {
[self.cacheOperation cancel];
self.cacheOperation = nil;
}
// 如果正在下載就取消下載
if (self.downloadToken) {
[self.manager.imageDownloader cancel:self.downloadToken];
}
// 移除掉當前操作
[self.manager safelyRemoveOperationFromRunning:self];
}
}
複製程式碼
7.類擴充套件屬性
/**
儲存影像快取物件
*/
@property (strong, nonatomic, readwrite, nonnull) SDImageCache *imageCache;
複製程式碼
/**
儲存影像下載物件
*/
@property (strong, nonatomic, readwrite, nonnull) SDWebImageDownloader *imageDownloader;
複製程式碼
/**
儲存下載失敗的url
*/
@property (strong, nonatomic, nonnull) NSMutableSet<NSURL *> *failedURLs;
複製程式碼
/**
儲存正在進行的操作
*/
@property (strong, nonatomic, nonnull) NSMutableArray<SDWebImageCombinedOperation *> *runningOperations;
複製程式碼
8.實現
8.1.生命週期方法實現
- (nonnull instancetype)init {
// 建立影像快取物件
SDImageCache *cache = [SDImageCache sharedImageCache];
// 建立影像下載物件
SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
// 用萬能方法初始化物件並返回
return [self initWithCache:cache downloader:downloader];
}
複製程式碼
8.2.私有方法實現
/**
縮放影像物件
*/
- (nullable UIImage *)scaledImageForKey:(nullable NSString *)key image:(nullable UIImage *)image {
return SDScaledImageForKey(key, image);
}
複製程式碼
- (void)safelyRemoveOperationFromRunning:(nullable SDWebImageCombinedOperation*)operation {
// 加鎖
@synchronized (self.runningOperations) {
// 從集合屬性中移除影像載入操作封裝物件
if (operation) {
[self.runningOperations removeObject:operation];
}
}
}
複製程式碼
- (void)callCompletionBlockForOperation:(nullable SDWebImageCombinedOperation*)operation
completion:(nullable SDInternalCompletionBlock)completionBlock
error:(nullable NSError *)error
url:(nullable NSURL *)url {
// 呼叫下面的全能方法
[self callCompletionBlockForOperation:operation completion:completionBlock image:nil data:nil error:error cacheType:SDImageCacheTypeNone finished:YES url:url];
}
複製程式碼
- (void)callCompletionBlockForOperation:(nullable SDWebImageCombinedOperation*)operation
completion:(nullable SDInternalCompletionBlock)completionBlock
image:(nullable UIImage *)image
data:(nullable NSData *)data
error:(nullable NSError *)error
cacheType:(SDImageCacheType)cacheType
finished:(BOOL)finished
url:(nullable NSURL *)url {
// 主佇列非同步呼叫
dispatch_main_async_safe(^{
// 如果有影像載入操作封裝物件,
// 並且影像載入操作封裝物件沒被取消
// 並且有完成回撥block
if (operation && !operation.isCancelled && completionBlock) {
// 回撥結果
completionBlock(image, data, error, cacheType, finished, url);
}
});
}
複製程式碼
8.3.公共方法實現
+ (nonnull instancetype)sharedManager {
// 獲取單例物件
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
instance = [self new];
});
return instance;
}
複製程式碼
- (nonnull instancetype)initWithCache:(nonnull SDImageCache *)cache downloader:(nonnull SDWebImageDownloader *)downloader {
// 初始化屬性
if ((self = [super init])) {
_imageCache = cache;
_imageDownloader = downloader;
_failedURLs = [NSMutableSet new];
_runningOperations = [NSMutableArray new];
}
return self;
}
複製程式碼
- (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url {
// 如果沒傳url就返回空字串
if (!url) {
return @"";
}
// 如果設定了url處理方法就呼叫處理方法,否則就返回url的字串格式
if (self.cacheKeyFilter) {
return self.cacheKeyFilter(url);
} else {
return url.absoluteString;
}
}
複製程式碼
- (void)cachedImageExistsForURL:(nullable NSURL *)url
completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock {
// 獲取url對應的key
NSString *key = [self cacheKeyForURL:url];
// 通過快取物件獲取key對應的記憶體快取
BOOL isInMemoryCache = ([self.imageCache imageFromMemoryCacheForKey:key] != nil);
// 如果有記憶體快取
if (isInMemoryCache) {
// 主佇列非同步回撥
dispatch_async(dispatch_get_main_queue(), ^{
if (completionBlock) {
completionBlock(YES);
}
});
return;
}
// 通過快取物件獲取key對應的硬碟快取
[self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) {
// 回撥
if (completionBlock) {
completionBlock(isInDiskCache);
}
}];
}
複製程式碼
- (void)diskImageExistsForURL:(nullable NSURL *)url
completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock {
// 獲取url對應的key
NSString *key = [self cacheKeyForURL:url];
// 通過快取物件獲取key對應的硬碟快取
[self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) {
// 回撥
if (completionBlock) {
completionBlock(isInDiskCache);
}
}];
}
複製程式碼
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock {
// 按成回撥block是必傳的
NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");
// 雖然引數要求傳NSURL型別物件但是如果傳NSStriing型別物件並不會有警告,所以再做一下處理
if ([url isKindOfClass:NSString.class]) {
url = [NSURL URLWithString:(NSString *)url];
}
// 防止引數url的型別錯誤導致崩潰,例如url的是NSNull型別的
if (![url isKindOfClass:NSURL.class]) {
url = nil;
}
// 生成一個影像載入操作的封裝物件
SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
operation.manager = self;
// 定義變數儲存要載入的url是否失敗過
BOOL isFailedUrl = NO;
if (url) {
// 加鎖
@synchronized (self.failedURLs) {
// 判斷是否失敗過
isFailedUrl = [self.failedURLs containsObject:url];
}
}
// 如果連結地址不正確,
// 或者之前載入失敗過並且沒有設定失敗url重試選項,
/// 就直接回撥錯誤並返回
if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url];
return operation;
}
// 加鎖
@synchronized (self.runningOperations) {
// 儲存當前操作封裝物件到屬性中
[self.runningOperations addObject:operation];
}
// 獲取url對應的key
NSString *key = [self cacheKeyForURL:url];
// 建立變數儲存影像快取選項
SDImageCacheOptions cacheOptions = 0;
// 根據設定的選項設定影像快取的選項
if (options & SDWebImageQueryDataWhenInMemory) cacheOptions |= SDImageCacheQueryDataWhenInMemory;
if (options & SDWebImageQueryDiskSync) cacheOptions |= SDImageCacheQueryDiskSync;
// 通過快取物件查詢url對應的快取
__weak SDWebImageCombinedOperation *weakOperation = operation;
operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key options:cacheOptions done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) {
__strong __typeof(weakOperation) strongOperation = weakOperation;
// 如果影像載入操作封裝物件不存在,
// 或者影像載入操作封裝物件被取消,
// 就從影像載入操作封裝物件集合屬性中移除並返回
if (!strongOperation || strongOperation.isCancelled) {
[self safelyRemoveOperationFromRunning:strongOperation];
return;
}
// 建立變數儲存是否要下載影像
// 想要從網路下載影像必須同時滿足以下條件:
// 沒有設定只從快取載入的選項
// 沒找到快取影像,或者設定了需要重新整理快取影像的選項
// 代理物件沒有實現這個代理方法,或者代理物件實現了這個代理方法並且代理方法返回了YES,意思是在快取中找不到圖片時要從網路上下載
BOOL shouldDownload = (!(options & SDWebImageFromCacheOnly))
&& (!cachedImage || options & SDWebImageRefreshCached)
&& (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url]);
// 如果需要下載影像
if (shouldDownload) {
// 如果有影像快取並且設定了重新整理圖片快取,就先呼叫完成回撥block
if (cachedImage && options & SDWebImageRefreshCached) {
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
}
// 建立變數儲存影像下載的選項
SDWebImageDownloaderOptions downloaderOptions = 0;
// 根據設定的選項設定影像下載的選項
if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;
if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;
if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache;
if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground;
if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies;
if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates;
if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority;
if (options & SDWebImageScaleDownLargeImages) downloaderOptions |= SDWebImageDownloaderScaleDownLargeImages;
// 如果有快取影像,並且選擇了重新整理快取的選項
if (cachedImage && options & SDWebImageRefreshCached) {
// 取消漸進下載
downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
// 忽視從NSURLCache中獲取快取
downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
}
// 開啟下載任務並獲取下載token
__weak typeof(strongOperation) weakSubOperation = strongOperation;
strongOperation.downloadToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {
__strong typeof(weakSubOperation) strongSubOperation = weakSubOperation;
if (!strongSubOperation || strongSubOperation.isCancelled) {
// 如果影像載入操作封裝物件不存在,
// 或者影像載入操作封裝物件被取消,
// 就什麼都不做
} else if (error) {
// 如果下載出錯
// 通過完成回撥block回撥錯誤
[self callCompletionBlockForOperation:strongSubOperation completion:completedBlock error:error url:url];
// 建立變數儲存是否儲存錯誤url
BOOL shouldBlockFailedURL;
if ([self.delegate respondsToSelector:@selector(imageManager:shouldBlockFailedURL:withError:)]) {
// 如果實現了代理就遵循代理方法返回的資料
shouldBlockFailedURL = [self.delegate imageManager:self shouldBlockFailedURL:url withError:error];
} else {
// 如果沒有實現代理,
// 想要阻止下載失敗的連結就得滿足以下條件:
// 不是沒聯網、
// 不是被取消、
// 不是連線超時、
// 不是關閉了國際漫遊、
// 不是不允許蜂窩資料連線、
// 不是沒有找到host、
// 不是無法連線host、
// 不是連線丟失
shouldBlockFailedURL = ( error.code != NSURLErrorNotConnectedToInternet
&& error.code != NSURLErrorCancelled
&& error.code != NSURLErrorTimedOut
&& error.code != NSURLErrorInternationalRoamingOff
&& error.code != NSURLErrorDataNotAllowed
&& error.code != NSURLErrorCannotFindHost
&& error.code != NSURLErrorCannotConnectToHost
&& error.code != NSURLErrorNetworkConnectionLost);
}
// 如果需要阻止失敗url再次下載,
// 就把url新增到黑名單中儲存
if (shouldBlockFailedURL) {
@synchronized (self.failedURLs) {
[self.failedURLs addObject:url];
}
}
}
else {
// 如果下載成功
// 如果設定了重新下載失敗的url選項,
// 就把url從黑名單中移除
if ((options & SDWebImageRetryFailed)) {
@synchronized (self.failedURLs) {
[self.failedURLs removeObject:url];
}
}
// 建立變數儲存是否快取到硬碟,並根據設定的選項賦值
BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);
// 在單例管理物件SDWebImageDownloader中已經實現了圖片的縮放,這裡是用於自定義管理物件以避免額外的縮放
if (self != [SDWebImageManager sharedManager] && self.cacheKeyFilter && downloadedImage) {
// 如果當前物件不是SDWebImageManager的單例物件,
// 並且設定裡過濾連結程式碼塊,
// 並且下載到了圖片。就進行縮放
downloadedImage = [self scaledImageForKey:key image:downloadedImage];
}
if (options & SDWebImageRefreshCached && cachedImage && !downloadedImage) {
// 如果設定了重新整理快取選項,
// 並且有快取圖,
// 並且沒有下載圖,
// 就什麼也不需要做
} else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
// 如果有下載圖
// 並且下載的不是動圖,
// 如果是動圖但是設定了動圖轉換選項
// 並且代理物件實現了轉換動圖的代理方法
// 全域性併發佇列非同步執行
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// 呼叫代理方法獲取轉換後的圖片
UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];
// 如果獲取到了轉換後的圖片並且下載完成
if (transformedImage && finished) {
// 判斷轉換後的圖片是否真的被轉換了
BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
// 建立變數儲存快取資料
NSData *cacheData;
// pass nil if the image was transformed, so we can recalculate the data from the image
if (self.cacheSerializer) {
// 如果自定義了影像解碼演算法,就按照自定義的演算法來解碼。
cacheData = self.cacheSerializer(transformedImage, (imageWasTransformed ? nil : downloadedData), url);
} else {
// 否則就檢查圖片是否被轉換,
// 如果被轉換過,就不返回影像資料了,
// 如果沒轉換過就返回影像資料
cacheData = (imageWasTransformed ? nil : downloadedData);
}
// 通過快取物件快取影像
[self.imageCache storeImage:transformedImage imageData:cacheData forKey:key toDisk:cacheOnDisk completion:nil];
}
// 呼叫完成回撥block回撥結果
[self callCompletionBlockForOperation:strongSubOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
});
} else {
// 否則
// 如果有下載圖並且下載完成
if (downloadedImage && finished) {
if (self.cacheSerializer) {
// 如果自定義了影像解碼演算法
// 全域性併發佇列非同步呼叫
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// 按照自定義的演算法來解碼獲取影像資料
NSData *cacheData = self.cacheSerializer(downloadedImage, downloadedData, url);
// 通過快取物件快取影像
[self.imageCache storeImage:downloadedImage imageData:cacheData forKey:key toDisk:cacheOnDisk completion:nil];
});
} else {
// 如果沒自定義解碼演算法就直接通過快取物件快取影像
[self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key toDisk:cacheOnDisk completion:nil];
}
}
// 呼叫完成回撥block回撥結果
[self callCompletionBlockForOperation:strongSubOperation completion:completedBlock image:downloadedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
}
}
// 如果下載完成就把當前影像載入操作封裝物件從集合屬性中移除
if (finished) {
[self safelyRemoveOperationFromRunning:strongSubOperation];
}
}];
} else if (cachedImage) {
// 如果不需要下載,
// 並且獲取到了快取圖,
// 呼叫完成回撥block回撥結果
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
// 把當前影像載入操作封裝物件從集合屬性中移除
[self safelyRemoveOperationFromRunning:strongOperation];
} else {
// 如果不需要下載,
// 並且也沒有快取圖
// 呼叫完成回撥block回撥結果
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];
// 把當前影像載入操作封裝物件從集合屬性中移除
[self safelyRemoveOperationFromRunning:strongOperation];
}
}];
// 返回影像載入操作封裝物件
return operation;
}
複製程式碼
- (void)saveImageToCache:(nullable UIImage *)image forURL:(nullable NSURL *)url {
// 必須要傳引數
if (image && url) {
// 獲取url對應的key
NSString *key = [self cacheKeyForURL:url];
// 通過快取物件進行快取
[self.imageCache storeImage:image forKey:key toDisk:YES completion:nil];
}
}
複製程式碼
- (void)cancelAll {
// 加鎖
@synchronized (self.runningOperations) {
// 獲取到當前所有正在執行的操作
NSArray<SDWebImageCombinedOperation *> *copiedOperations = [self.runningOperations copy];
// 呼叫所有影像載入操作封裝物件的取消方法
[copiedOperations makeObjectsPerformSelector:@selector(cancel)];
// 移除獲取到的所有的操作
[self.runningOperations removeObjectsInArray:copiedOperations];
}
}
複製程式碼
- (BOOL)isRunning {
// 建立變數儲存執行狀態,預設為NO
BOOL isRunning = NO;
// 加鎖
@synchronized (self.runningOperations) {
// 如果當前有正在執行的操作就為YES
isRunning = (self.runningOperations.count > 0);
}
// 返回狀態
return isRunning;
}
複製程式碼
9.總結
可以看到,由於有良好的框架設計,在實現影像載入操作時的邏輯就很清晰:
由影像快取物件SDImageCache
來負責快取的查詢和新增;由影像下載物件SDWebImageDownloader
來負責影像的網路下載;由影像載入操作封裝物件SDWebImageCombinedOperation
來負責整體影像載入操作的控制。所以,一個良好的架構是非常非常必要和重要的。
原始碼閱讀系列:SDWebImage
原始碼閱讀:SDWebImage(二)——SDWebImageCompat
原始碼閱讀:SDWebImage(三)——NSData+ImageContentType
原始碼閱讀:SDWebImage(四)——SDWebImageCoder
原始碼閱讀:SDWebImage(五)——SDWebImageFrame
原始碼閱讀:SDWebImage(六)——SDWebImageCoderHelper
原始碼閱讀:SDWebImage(七)——SDWebImageImageIOCoder
原始碼閱讀:SDWebImage(八)——SDWebImageGIFCoder
原始碼閱讀:SDWebImage(九)——SDWebImageCodersManager
原始碼閱讀:SDWebImage(十)——SDImageCacheConfig
原始碼閱讀:SDWebImage(十一)——SDImageCache
原始碼閱讀:SDWebImage(十二)——SDWebImageDownloaderOperation
原始碼閱讀:SDWebImage(十三)——SDWebImageDownloader
原始碼閱讀:SDWebImage(十四)——SDWebImageManager
原始碼閱讀:SDWebImage(十五)——SDWebImagePrefetcher
原始碼閱讀:SDWebImage(十六)——SDWebImageTransition
原始碼閱讀:SDWebImage(十七)——UIView+WebCacheOperation
原始碼閱讀:SDWebImage(十八)——UIView+WebCache
原始碼閱讀:SDWebImage(十九)——UIImage+ForceDecode/UIImage+GIF/UIImage+MultiFormat
原始碼閱讀:SDWebImage(二十)——UIButton+WebCache
原始碼閱讀:SDWebImage(二十一)——UIImageView+WebCache/UIImageView+HighlightedWebCache