該文章閱讀的SDWebImage的版本為4.3.3。
這個類負責影像下載的相關操作
1.SDWebImageOperation協議
這個協議中只有一個方法,就是取消方法。即實現這個協議的類必須實現取消方法。
- (void)cancel;
複製程式碼
2.全域性靜態常量
/**
下載任務開始的通知名
*/
FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadStartNotification;
/**
下載任務已經接收到響應的通知名
*/
FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadReceiveResponseNotification;
/**
下載任務結束的通知名
*/
FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadStopNotification;
/**
下載任務完成的通知名
*/
FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadFinishNotification;
複製程式碼
3.SDWebImageDownloaderOperationInterface協議
這個協議定義了作為一個圖片下載操作類必須要提供的功能。如果你想自己自定義一個圖片下載操作類,需要繼承NSOperation
類,並遵守這個協議。
/**
以指定請求物件、會話物件和下載選項初始化圖片下載操作類
*/
- (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
inSession:(nullable NSURLSession *)session
options:(SDWebImageDownloaderOptions)options;
複製程式碼
/**
新增進度回撥block和完成回撥block
*/
- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
複製程式碼
/**
是否解壓縮影像物件
*/
- (BOOL)shouldDecompressImages;
複製程式碼
/**
設定是否解壓縮影像物件
*/
- (void)setShouldDecompressImages:(BOOL)value;
複製程式碼
/**
認證挑戰的證書
*/
- (nullable NSURLCredential *)credential;
複製程式碼
/**
設定認證挑戰的證書
*/
- (void)setCredential:(nullable NSURLCredential *)value;
複製程式碼
/**
取消指定token的下載操作
*/
- (BOOL)cancel:(nullable id)token;
複製程式碼
4.公共屬性
/**
影像下載操作的請求物件
*/
@property (strong, nonatomic, readonly, nullable) NSURLRequest *request;
/**
影像下載操作的任務物件
*/
@property (strong, nonatomic, readonly, nullable) NSURLSessionTask *dataTask;
/**
是否解壓縮圖片
*/
@property (assign, nonatomic) BOOL shouldDecompressImages;
/**
該屬性已經廢棄
*/
@property (nonatomic, assign) BOOL shouldUseCredentialStorage __deprecated_msg("Property deprecated. Does nothing. Kept only for backwards compatibility");
/**
認證挑戰的證書
*/
@property (nonatomic, strong, nullable) NSURLCredential *credential;
/**
下載選項
*/
@property (assign, nonatomic, readonly) SDWebImageDownloaderOptions options;
/**
預計大小
*/
@property (assign, nonatomic) NSInteger expectedSize;
/**
影像下載任務的響應物件
*/
@property (strong, nonatomic, nullable) NSURLResponse *response;
複製程式碼
5.公共方法
/**
以指定請求物件、指定會話物件和下載操作物件
*/
- (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
inSession:(nullable NSURLSession *)session
options:(SDWebImageDownloaderOptions)options NS_DESIGNATED_INITIALIZER;
複製程式碼
/**
新增監聽程式block和監聽完成block
*/
- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
複製程式碼
/**
取消指定token的影像下載操作
*/
- (BOOL)cancel:(nullable id)token;
複製程式碼
6.私有巨集
/**
利用GCD的訊號量實現加鎖的效果
*/
#define LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
/**
利用GCD的訊號量實現解鎖的效果
*/
#define UNLOCK(lock) dispatch_semaphore_signal(lock);
複製程式碼
7.私有靜態常量
這三個靜態常量在iOS8中是定義在CFNetwork.framework
中,這裡再定義一遍是為了相容iOS8而不引入CFNetwork.framework
#if (__IPHONE_OS_VERSION_MIN_REQUIRED && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0)
const float NSURLSessionTaskPriorityHigh = 0.75;
const float NSURLSessionTaskPriorityDefault = 0.5;
const float NSURLSessionTaskPriorityLow = 0.25;
#endif
複製程式碼
定義回撥block對應的key
static NSString *const kProgressCallbackKey = @"progress";
static NSString *const kCompletedCallbackKey = @"completed";
複製程式碼
8.型別定義
定義了一個key是NSString
型別,value是id
型別的可變字典
typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
複製程式碼
9.類擴充套件屬性
/**
儲存回撥block
*/
@property (strong, nonatomic, nonnull) NSMutableArray<SDCallbacksDictionary *> *callbackBlocks;
/**
操作是否正在執行
*/
@property (assign, nonatomic, getter = isExecuting) BOOL executing;
/**
操作是否執行完成
*/
@property (assign, nonatomic, getter = isFinished) BOOL finished;
/**
儲存影像資料
*/
@property (strong, nonatomic, nullable) NSMutableData *imageData;
/**
儲存SDWebImageDownloaderIgnoreCachedResponse的快取資料
*/
@property (copy, nonatomic, nullable) NSData *cachedData;
/**
一個弱引用的屬性儲存會話物件
*/
@property (weak, nonatomic, nullable) NSURLSession *unownedSession;
/**
一個強引用的屬性儲存會話物件
*/
@property (strong, nonatomic, nullable) NSURLSession *ownedSession;
/**
儲存任務物件
*/
@property (strong, nonatomic, readwrite, nullable) NSURLSessionTask *dataTask;
/**
如果保證回撥執行緒安全的鎖
*/
@property (strong, nonatomic, nonnull) dispatch_semaphore_t callbacksLock; // a lock to keep the access to `callbackBlocks` thread-safe
/**
用於影像解碼的佇列
*/
@property (strong, nonatomic, nonnull) dispatch_queue_t coderQueue;
#if SD_UIKIT
/**
用於後臺下載的任務ID
*/
@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskId;
#endif
/**
編解碼器
*/
@property (strong, nonatomic, nullable) id<SDWebImageProgressiveCoder> progressiveCoder;
複製程式碼
10.方法實現
10.1 私有方法
/**
獲取指定金鑰的回撥block陣列
*/
- (nullable NSArray<id> *)callbacksForKey:(NSString *)key {
// 加鎖
LOCK(self.callbacksLock);
// 獲取指定金鑰下的回撥block陣列
NSMutableArray<id> *callbacks = [[self.callbackBlocks valueForKey:key] mutableCopy];
// 解鎖
UNLOCK(self.callbacksLock);
// 移除陣列中的[NSNull null]
[callbacks removeObjectIdenticalTo:[NSNull null]];
// 返回不可變陣列
return [callbacks copy];
}
複製程式碼
/**
取消
*/
- (void)cancelInternal {
// 如果當前狀態是已完成就不繼續向下執行了
if (self.isFinished) return;
// 呼叫父類的取消方法
[super cancel];
// 如果任務還在
if (self.dataTask) {
// 取消任務
[self.dataTask cancel];
// 主佇列非同步回撥傳送通知
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:weakSelf];
});
// 改變記錄狀態的屬性
if (self.isExecuting) self.executing = NO;
if (!self.isFinished) self.finished = YES;
}
// 呼叫重啟方法
[self reset];
}
複製程式碼
/**
完成
*/
- (void)done {
// 改變記錄狀態的屬性
self.finished = YES;
self.executing = NO;
// 呼叫重啟方法
[self reset];
}
複製程式碼
/**
重啟
*/
- (void)reset {
// 加鎖
LOCK(self.callbacksLock);
// 移除所有儲存的回撥block
[self.callbackBlocks removeAllObjects];
// 解鎖
UNLOCK(self.callbacksLock);
// 任務物件置空
self.dataTask = nil;
// 會話物件取消並置空
if (self.ownedSession) {
[self.ownedSession invalidateAndCancel];
self.ownedSession = nil;
}
}
複製程式碼
/**
根據字尾縮放圖片
*/
- (nullable UIImage *)scaledImageForKey:(nullable NSString *)key image:(nullable UIImage *)image {
return SDScaledImageForKey(key, image);
}
複製程式碼
/**
應用進入後臺後是不繼續下載任務
*/
- (BOOL)shouldContinueWhenAppEntersBackground {
return self.options & SDWebImageDownloaderContinueInBackground;
}
複製程式碼
/**
下載出錯的情況下回撥完成block
*/
- (void)callCompletionBlocksWithError:(nullable NSError *)error {
[self callCompletionBlocksWithImage:nil imageData:nil error:error finished:YES];
}
複製程式碼
/**
下載成功的情況下回撥完成block
*/
- (void)callCompletionBlocksWithImage:(nullable UIImage *)image
imageData:(nullable NSData *)imageData
error:(nullable NSError *)error
finished:(BOOL)finished {
NSArray<id> *completionBlocks = [self callbacksForKey:kCompletedCallbackKey];
dispatch_main_async_safe(^{
for (SDWebImageDownloaderCompletedBlock completedBlock in completionBlocks) {
completedBlock(image, imageData, error, finished);
}
});
}
複製程式碼
10.2 SDWebImageDownloaderOperationInterface協議方法
- (nonnull instancetype)initWithRequest:(nullable NSURLRequest *)request
inSession:(nullable NSURLSession *)session
options:(SDWebImageDownloaderOptions)options {
// 呼叫父類的初始化方法
if ((self = [super init])) {
// 儲存請求物件
_request = [request copy];
// 預設是解壓縮影像
_shouldDecompressImages = YES;
// 儲存下載選項
_options = options;
// 初始化可變陣列儲存回撥block
_callbackBlocks = [NSMutableArray new];
// 執行狀態設定為NO
_executing = NO;
// 完成狀態狀態設定為NO
_finished = NO;
// 預計大小預設為0
_expectedSize = 0;
// 用弱引用的屬性儲存會話物件
_unownedSession = session;
// 生成訊號量
_callbacksLock = dispatch_semaphore_create(1);
// 生成序列佇列
_coderQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderOperationCoderQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
複製程式碼
- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
// 建立可變字典儲存進度回撥block和完成回撥block
SDCallbacksDictionary *callbacks = [NSMutableDictionary new];
if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
// 加鎖
LOCK(self.callbacksLock);
// 用陣列儲存可變字典
[self.callbackBlocks addObject:callbacks];
// 解鎖
UNLOCK(self.callbacksLock);
// 返回可變字典
return callbacks;
}
複製程式碼
- (BOOL)cancel:(nullable id)token {
// 建立變數儲存是否需要取消,預設為NO
BOOL shouldCancel = NO;
// 加鎖
LOCK(self.callbacksLock);
// 移除token對應的回撥block
[self.callbackBlocks removeObjectIdenticalTo:token];
// 如果沒有回撥block就設定為YES
if (self.callbackBlocks.count == 0) {
shouldCancel = YES;
}
// 解鎖
UNLOCK(self.callbacksLock);
// 如果需要取消就取消
if (shouldCancel) {
[self cancel];
}
// 返回是否需要取消
return shouldCancel;
}
複製程式碼
10.3 重寫父類NSOperation方法
- (nonnull instancetype)init {
// 呼叫自定義初始化方法
return [self initWithRequest:nil inSession:nil options:0];
}
複製程式碼
- (void)start {
// 同步鎖
@synchronized (self) {
// 如果當前狀態是取消
if (self.isCancelled) {
// 完成狀態設定為YES
self.finished = YES;
// 呼叫重置
[self reset];
// 返回
return;
}
#if SD_UIKIT
// 如果設定了後臺下載選項,就申請後臺任務
Class UIApplicationClass = NSClassFromString(@"UIApplication");
BOOL hasApplication = UIApplicationClass && [UIApplicationClass respondsToSelector:@selector(sharedApplication)];
if (hasApplication && [self shouldContinueWhenAppEntersBackground]) {
__weak __typeof__ (self) wself = self;
UIApplication * app = [UIApplicationClass performSelector:@selector(sharedApplication)];
self.backgroundTaskId = [app beginBackgroundTaskWithExpirationHandler:^{
__strong __typeof (wself) sself = wself;
if (sself) {
// 取消任務
[sself cancel];
[app endBackgroundTask:sself.backgroundTaskId];
sself.backgroundTaskId = UIBackgroundTaskInvalid;
}
}];
}
#endif
// 獲取會話物件
NSURLSession *session = self.unownedSession;
// 如果沒獲取到會話物件
if (!session) {
// 建立一個會話物件並設定請求超時時間為15秒
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.timeoutIntervalForRequest = 15;
session = [NSURLSession sessionWithConfiguration:sessionConfig
delegate:self
delegateQueue:nil];
self.ownedSession = session;
}
// 如果設定了忽略NSURLCache
if (self.options & SDWebImageDownloaderIgnoreCachedResponse) {
// 從繪畫物件中獲取快取資料
NSURLCache *URLCache = session.configuration.URLCache;
// 如果沒有就獲取單例
if (!URLCache) {
URLCache = [NSURLCache sharedURLCache];
}
// 獲取快取響應物件
NSCachedURLResponse *cachedResponse;
@synchronized (URLCache) {
cachedResponse = [URLCache cachedResponseForRequest:self.request];
}
// 獲取快取資料
if (cachedResponse) {
self.cachedData = cachedResponse.data;
}
}
// 建立影像下載任務
self.dataTask = [session dataTaskWithRequest:self.request];
// 執行狀態設定為YES
self.executing = YES;
}
if (self.dataTask) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
// 設定下載任務的優先順序
if ([self.dataTask respondsToSelector:@selector(setPriority:)]) {
if (self.options & SDWebImageDownloaderHighPriority) {
self.dataTask.priority = NSURLSessionTaskPriorityHigh;
} else if (self.options & SDWebImageDownloaderLowPriority) {
self.dataTask.priority = NSURLSessionTaskPriorityLow;
}
}
#pragma clang diagnostic pop
// 啟動下載任務
[self.dataTask resume];
// 回撥進度block
for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
progressBlock(0, NSURLResponseUnknownLength, self.request.URL);
}
// 主佇列非同步傳送通知
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:weakSelf];
});
} else {
// 如果下載任務建立失敗就回撥錯誤
[self callCompletionBlocksWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorUnknown userInfo:@{NSLocalizedDescriptionKey : @"Task can't be initialized"}]];
// 結束
[self done];
return;
}
#if SD_UIKIT
// 如果開啟了後臺下載任務就結束任務
Class UIApplicationClass = NSClassFromString(@"UIApplication");
if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
return;
}
if (self.backgroundTaskId != UIBackgroundTaskInvalid) {
UIApplication * app = [UIApplication performSelector:@selector(sharedApplication)];
[app endBackgroundTask:self.backgroundTaskId];
self.backgroundTaskId = UIBackgroundTaskInvalid;
}
#endif
}
複製程式碼
- (void)cancel {
// 呼叫自定義的取消方法
@synchronized (self) {
[self cancelInternal];
}
}
複製程式碼
- (void)setFinished:(BOOL)finished {
// 手動實現KVO傳送
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
複製程式碼
- (void)setExecuting:(BOOL)executing {
// 手動實現KVO傳送
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
複製程式碼
- (BOOL)isConcurrent {
// 始終返回YES
return YES;
}
複製程式碼
10.3 NSURLSessionDataDelegate代理方法
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
// 建立變數儲存會話響應意向,預設為允許
NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
// 建立變數儲存預期大小,預設為0
NSInteger expected = (NSInteger)response.expectedContentLength;
expected = expected > 0 ? expected : 0;
// 屬性儲存預期大小
self.expectedSize = expected;
// 屬性儲存響應物件
self.response = response;
// 獲取響應狀態碼
NSInteger statusCode = [response respondsToSelector:@selector(statusCode)] ? ((NSHTTPURLResponse *)response).statusCode : 200;
// 建立變數儲存是否有效:狀態碼比400小就有效
BOOL valid = statusCode < 400;
// 但是如果狀態碼是304並且沒有快取資料,就設定為無效
if (statusCode == 304 && !self.cachedData) {
valid = NO;
}
if (valid) {
// 如果有效,回撥進度block
for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
progressBlock(0, expected, self.request.URL);
}
} else {
// 如果無效設定會話響應意向為取消
disposition = NSURLSessionResponseCancel;
}
// 傳送通知
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadReceiveResponseNotification object:weakSelf];
});
// 返回會話響應意向
if (completionHandler) {
completionHandler(disposition);
}
}
複製程式碼
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
// 初始化屬性儲存影像資料
if (!self.imageData) {
self.imageData = [[NSMutableData alloc] initWithCapacity:self.expectedSize];
}
// 儲存下載的資料
[self.imageData appendData:data];
// 如果設定漸進式下載並且預期大小大於零
if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0) {
// 獲取已經下載的影像資料
__block NSData *imageData = [self.imageData copy];
// 獲取已經下載的影像大小
const NSInteger totalSize = imageData.length;
// 如果已下載的影像大小不小於預期大小就是下載完成
BOOL finished = (totalSize >= self.expectedSize);
// 建立逐行編解碼器
if (!self.progressiveCoder) {
for (id<SDWebImageCoder>coder in [SDWebImageCodersManager sharedInstance].coders) {
if ([coder conformsToProtocol:@protocol(SDWebImageProgressiveCoder)] &&
[((id<SDWebImageProgressiveCoder>)coder) canIncrementallyDecodeFromData:imageData]) {
self.progressiveCoder = [[[coder class] alloc] init];
break;
}
}
}
// 序列佇列非同步執行
dispatch_async(self.coderQueue, ^{
// 逐行解碼影像資料獲取影像物件
UIImage *image = [self.progressiveCoder incrementallyDecodedImageWithData:imageData finished:finished];
if (image) {
// 根據影像的地址獲取快取金鑰
NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
// 縮放影像
image = [self scaledImageForKey:key image:image];
// 如果需要解壓縮就解壓縮
if (self.shouldDecompressImages) {
image = [[SDWebImageCodersManager sharedInstance] decompressedImageWithImage:image data:&imageData options:@{SDWebImageCoderScaleDownLargeImagesKey: @(NO)}];
}
// 回撥完成block
[self callCompletionBlocksWithImage:image imageData:nil error:nil finished:NO];
}
});
}
// 回撥進度block
for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
progressBlock(self.imageData.length, self.expectedSize, self.request.URL);
}
}
複製程式碼
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
willCacheResponse:(NSCachedURLResponse *)proposedResponse
completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler {
NSCachedURLResponse *cachedResponse = proposedResponse;
// 如果沒設定使用NSURLCache就將快取響應置空後返回
if (!(self.options & SDWebImageDownloaderUseNSURLCache)) {
cachedResponse = nil;
}
if (completionHandler) {
completionHandler(cachedResponse);
}
}
複製程式碼
10.4 NSURLSessionTaskDelegate代理方法
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
// 同步鎖
@synchronized(self) {
// 儲存下載任務的屬性置空
self.dataTask = nil;
// 傳送通知
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:weakSelf];
if (!error) {
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadFinishNotification object:weakSelf];
}
});
}
if (error) {
// 如果出錯就回撥錯誤
[self callCompletionBlocksWithError:error];
// 完成
[self done];
} else {
// 如果有完成回撥的block
if ([self callbacksForKey:kCompletedCallbackKey].count > 0) {
// 獲取到下載的影像資料
__block NSData *imageData = [self.imageData copy];
if (imageData) {
// 如果設定了忽略快取響應選項,並且快取資料和下載資料相同
if (self.options & SDWebImageDownloaderIgnoreCachedResponse && [self.cachedData isEqualToData:imageData]) {
// 完成回撥返回空
[self callCompletionBlocksWithImage:nil imageData:nil error:nil finished:YES];
// 完成
[self done];
} else {
// 序列佇列非同步執行
dispatch_async(self.coderQueue, ^{
// 解碼影像資料獲得影像物件
UIImage *image = [[SDWebImageCodersManager sharedInstance] decodedImageWithData:imageData];
// 根據影像地址獲取影像快取金鑰
NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
// 縮放影像物件
image = [self scaledImageForKey:key image:image];
// 如果影像是GIF和WebP格式的就不解碼
BOOL shouldDecode = YES;
if (image.images) {
shouldDecode = NO;
} else {
#ifdef SD_WEBP
SDImageFormat imageFormat = [NSData sd_imageFormatForImageData:imageData];
if (imageFormat == SDImageFormatWebP) {
shouldDecode = NO;
}
#endif
}
// 如果需要解碼,並且需要解壓縮就進行解壓縮
if (shouldDecode) {
if (self.shouldDecompressImages) {
BOOL shouldScaleDown = self.options & SDWebImageDownloaderScaleDownLargeImages;
image = [[SDWebImageCodersManager sharedInstance] decompressedImageWithImage:image data:&imageData options:@{SDWebImageCoderScaleDownLargeImagesKey: @(shouldScaleDown)}];
}
}
// 獲取影像大小
CGSize imageSize = image.size;
if (imageSize.width == 0 || imageSize.height == 0) {
// 如果影像沒有大小就回撥錯誤
[self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}]];
} else {
// 如果影像有大小就回撥結果
[self callCompletionBlocksWithImage:image imageData:imageData error:nil finished:YES];
}
// 完成
[self done];
});
}
} else {
// 如果沒有影像資料就回撥錯誤
[self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}]];
// 完成
[self done];
}
} else {
// 沒有完成回撥block就呼叫完成方法
[self done];
}
}
}
複製程式碼
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
// 建立變數儲存驗證挑戰意向
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
// 建立變數儲存證書
__block NSURLCredential *credential = nil;
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// 如果證書驗證方法是信任伺服器
if (!(self.options & SDWebImageDownloaderAllowInvalidSSLCertificates)) {
// 如果設定了信任無效SSL證書意向就設定為預設
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
} else {
// 如果沒設定,生成證書,意向是通過證書
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
disposition = NSURLSessionAuthChallengeUseCredential;
}
} else {
if (challenge.previousFailureCount == 0) {
// 如果失敗嘗試數為0
if (self.credential) {
// 如果有證書就設定意向為通過證書
credential = self.credential;
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
// 如果沒有證書就設定意向為取消
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
// 如果失敗嘗試數不為0,就設定意向為取消
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
}
// 返回意向和證書
if (completionHandler) {
completionHandler(disposition, credential);
}
}
複製程式碼
11.總結
這個類主要是對影像下載操作的處理。利用傳入的請求物件和會話物件建立下載任務,在下載任務代理方法中處理下載過程中的種種情況,通過傳入的回撥block反饋下載進度和下載結果。
原始碼閱讀系列: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