原始碼閱讀:SDWebImage(十三)——SDWebImageDownloader

堯少羽發表於2018-07-03

該文章閱讀的SDWebImage的版本為4.3.3。

這個類是下載器類,管理著影像的下載。

1.公共列舉

/**
 這個位列舉定義了下載器的可選項
 */
typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
    /**
     低優先順序
     */
    SDWebImageDownloaderLowPriority = 1 << 0,
    
    /**
     漸進式下載
     */
    SDWebImageDownloaderProgressiveDownload = 1 << 1,

    /**
     預設情況下,請求阻止使用NSURLCache。設定這個選項就會使用NSURLCache
     */
    SDWebImageDownloaderUseNSURLCache = 1 << 2,

    /**
     這個選項要與上面那個選項配合使用,如果影像是從NSURLCache讀取的,
     那麼回撥block中返回的image和imageData引數就是nil
     */
    SDWebImageDownloaderIgnoreCachedResponse = 1 << 3,
    
    /**
     應用進入後臺時繼續下載
     */
    SDWebImageDownloaderContinueInBackground = 1 << 4,

    /**
     通過設定NSMutableURLRequest.HTTPShouldHandleCookies = YES獲取儲存在NSHTTPCookieStore中的cookies
     */
    SDWebImageDownloaderHandleCookies = 1 << 5,

    /**
     允許接受不受信任的SSL證書
     */
    SDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6,

    /**
     高優先順序
     */
    SDWebImageDownloaderHighPriority = 1 << 7,
    
    /**
     縮小圖片
     */
    SDWebImageDownloaderScaleDownLargeImages = 1 << 8,
};
複製程式碼
/**
 這個列舉定義了下載執行順序
 */
typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {
    /**
     先進先出,預設情況
     */
    SDWebImageDownloaderFIFOExecutionOrder,

    /**
     後進先出
     */
    SDWebImageDownloaderLIFOExecutionOrder
};
複製程式碼

2.公共常量

/**
 下載開始通知名稱
 */
FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadStartNotification;
複製程式碼
/**
 下載停止通知名稱
 */
FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageDownloadStopNotification;
複製程式碼

3.公共型別定義

/**
 下載進度回撥block
 */
typedef void(^SDWebImageDownloaderProgressBlock)(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL);
複製程式碼
/**
 下載完成回撥block
 */
typedef void(^SDWebImageDownloaderCompletedBlock)(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished);
複製程式碼
/**
 HTTP請求頭字典
 */
typedef NSDictionary<NSString *, NSString *> SDHTTPHeadersDictionary;
複製程式碼
/**
 HTTP請求頭可變字典
 */
typedef NSMutableDictionary<NSString *, NSString *> SDHTTPHeadersMutableDictionary;
複製程式碼
/**
 HTTP請求頭過濾演算法block
 */
typedef SDHTTPHeadersDictionary * _Nullable (^SDWebImageDownloaderHeadersFilterBlock)(NSURL * _Nullable url, SDHTTPHeadersDictionary * _Nullable headers);
複製程式碼

4.SDWebImageDownloadToken類

4.1 公共屬性

/**
 用於下載的url
 */
@property (nonatomic, strong, nullable) NSURL *url;
複製程式碼
/**
 用於取消的token
 */
@property (nonatomic, strong, nullable) id downloadOperationCancelToken;
複製程式碼

4.2 類擴充套件屬性

/**
 下載操作物件
 */
@property (nonatomic, weak, nullable) NSOperation<SDWebImageDownloaderOperationInterface> *downloadOperation;
複製程式碼

4.3 實現

- (void)cancel {
    // 有下載操作物件
    if (self.downloadOperation) {
        // 有取消token
        SDWebImageDownloadToken *cancelToken = self.downloadOperationCancelToken;
        if (cancelToken) {
            // 取消下載操作
            [self.downloadOperation cancel:cancelToken];
        }
    }
}
複製程式碼

5.公共屬性

/**
 解壓影像可以提高效能,但會佔用大量記憶體。
 預設為YES。如果由於過多的記憶體消耗而遇到崩潰,請將此項設定為NO。
 */
@property (assign, nonatomic) BOOL shouldDecompressImages;
複製程式碼
/**
 最大併發下載數量
 */
@property (assign, nonatomic) NSInteger maxConcurrentDownloads;
複製程式碼
/**
 仍需要下載的載量
 */
@property (readonly, nonatomic) NSUInteger currentDownloadCount;
複製程式碼
/**
 下載請求超時的時間,預設15秒
 */
@property (assign, nonatomic) NSTimeInterval downloadTimeout;
複製程式碼
/**
 會話配置物件
 */
@property (readonly, nonatomic, nonnull) NSURLSessionConfiguration *sessionConfiguration;
複製程式碼
/**
 下載順序
 */
@property (assign, nonatomic) SDWebImageDownloaderExecutionOrder executionOrder;
複製程式碼
/**
 請求操作的證書
 */
@property (strong, nonatomic, nullable) NSURLCredential *urlCredential;
複製程式碼
/**
 證書的使用者名稱
 */
@property (strong, nonatomic, nullable) NSString *username;
複製程式碼
/**
 證書的密碼
 */
@property (strong, nonatomic, nullable) NSString *password;
複製程式碼
/**
 為HTTP請求頭設定過濾器
 */
@property (nonatomic, copy, nullable) SDWebImageDownloaderHeadersFilterBlock headersFilter;
複製程式碼

6.公共方法

/**
 獲取單例物件
 */
+ (nonnull instancetype)sharedDownloader;
複製程式碼
/**
 以指定會話配置物件初始化
 */
- (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration NS_DESIGNATED_INITIALIZER;
複製程式碼
/**
 為HTTP請求頭新增欄位
 */
- (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)field;
複製程式碼
/**
 獲取HTTP請求頭指定欄位的值
 */
- (nullable NSString *)valueForHTTPHeaderField:(nullable NSString *)field;
複製程式碼
/**
 設定下載操作物件
 */
- (void)setOperationClass:(nullable Class)operationClass;
複製程式碼
/**
 下載指定url的影像
 */
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
                                                   options:(SDWebImageDownloaderOptions)options
                                                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                                 completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
複製程式碼
/**
 取消指定token的操作
 */
- (void)cancel:(nullable SDWebImageDownloadToken *)token;
複製程式碼
/**
 設定下載的暫停狀態
 */
- (void)setSuspended:(BOOL)suspended;
複製程式碼
/**
 取消所有的下載
 */
- (void)cancelAllDownloads;
複製程式碼
/**
 以指定會話配置物件建立新的會話物件
 */
- (void)createNewSessionWithConfiguration:(nonnull NSURLSessionConfiguration *)sessionConfiguration;
複製程式碼
/**
 使會話物件無效並取消任務
 */
- (void)invalidateSessionAndCancel:(BOOL)cancelPendingOperations;
複製程式碼

7.私有巨集

/**
 利用GCD的訊號量實現加鎖的效果
 */
#define LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
複製程式碼
/**
 利用GCD的訊號量實現解鎖的效果
 */
#define UNLOCK(lock) dispatch_semaphore_signal(lock);
複製程式碼

8.類擴充套件屬性

/**
 下載操作佇列
 */
@property (strong, nonatomic, nonnull) NSOperationQueue *downloadQueue;
複製程式碼
/**
 最後新增的操作物件
 */
@property (weak, nonatomic, nullable) NSOperation *lastAddedOperation;
複製程式碼
/**
 下載操作類
 */
@property (assign, nonatomic, nullable) Class operationClass;
複製程式碼
/**
 儲存url和其對應的操作物件
 */
@property (strong, nonatomic, nonnull) NSMutableDictionary<NSURL *, SDWebImageDownloaderOperation *> *URLOperations;
複製程式碼
/**
 HTTP請求頭
 */
@property (strong, nonatomic, nullable) SDHTTPHeadersMutableDictionary *HTTPHeaders;
複製程式碼
/**
 用於保證對屬性URLOperations操作執行緒安全的鎖
 */
@property (strong, nonatomic, nonnull) dispatch_semaphore_t operationsLock;
複製程式碼
/**
 用於保證對屬性HTTPHeaders操作執行緒安全的鎖
 */
@property (strong, nonatomic, nonnull) dispatch_semaphore_t headersLock;
複製程式碼
/**
 會話物件
 */
@property (strong, nonatomic) NSURLSession *session;
複製程式碼

9.實現

9.1 生命週期方法

+ (void)initialize {
    // 如果獲取到了SDNetworkActivityIndicator類
    if (NSClassFromString(@"SDNetworkActivityIndicator")) {

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        // 獲取SDNetworkActivityIndicator類的單例物件
        id activityIndicator = [NSClassFromString(@"SDNetworkActivityIndicator") performSelector:NSSelectorFromString(@"sharedActivityIndicator")];
#pragma clang diagnostic pop

        // 移除之前新增的觀察者
        [[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStartNotification object:nil];
        [[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStopNotification object:nil];

        // 新增觀察者
        [[NSNotificationCenter defaultCenter] addObserver:activityIndicator
                                                 selector:NSSelectorFromString(@"startActivity")
                                                     name:SDWebImageDownloadStartNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:activityIndicator
                                                 selector:NSSelectorFromString(@"stopActivity")
                                                     name:SDWebImageDownloadStopNotification object:nil];
    }
}
複製程式碼
- (nonnull instancetype)init {
    // 以預設會話配置物件初始化
    return [self initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
}
複製程式碼
- (void)dealloc {
    // 取消任務並使會話物件無效
    [self.session invalidateAndCancel];
    // 儲存會話物件的屬性置空
    self.session = nil;

    // 取消操作佇列中所有操作
    [self.downloadQueue cancelAllOperations];
}
複製程式碼

9.2 私有方法

/**
 新增下載操作
 */
- (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
                                           completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock
                                                   forURL:(nullable NSURL *)url
                                           createCallback:(SDWebImageDownloaderOperation *(^)(void))createCallback {
    // url是必傳的
    if (url == nil) {
        if (completedBlock != nil) {
            completedBlock(nil, nil, nil, NO);
        }
        return nil;
    }
    
    // 加鎖
    LOCK(self.operationsLock);
    // 從快取中獲取url對應的操作物件
    SDWebImageDownloaderOperation *operation = [self.URLOperations objectForKey:url];
    // 如果沒獲取到,防止同一影像重複下載
    if (!operation) {
        // 從引數中獲取操作物件
        operation = createCallback();
        __weak typeof(self) wself = self;
        operation.completionBlock = ^{
            // 操作完成時,從快取中移除該url對應的操作物件
            __strong typeof(wself) sself = wself;
            if (!sself) {
                return;
            }
            LOCK(sself.operationsLock);
            [sself.URLOperations removeObjectForKey:url];
            UNLOCK(sself.operationsLock);
        };
        // 快取url和其對應的操作物件
        [self.URLOperations setObject:operation forKey:url];
        // 將操作物件新增到操作佇列中
        [self.downloadQueue addOperation:operation];
    }
    // 解鎖
    UNLOCK(self.operationsLock);

    // 生成下載操作取消token
    id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
    
    // 生成下載token
    SDWebImageDownloadToken *token = [SDWebImageDownloadToken new];
    token.downloadOperation = operation;
    token.url = url;
    token.downloadOperationCancelToken = downloadOperationCancelToken;

    // 返回token
    return token;
}
複製程式碼
/**
 獲取操作佇列中task對應的下載操作物件
 */
- (SDWebImageDownloaderOperation *)operationWithTask:(NSURLSessionTask *)task {
    // 建立變數奧村下載操作物件
    SDWebImageDownloaderOperation *returnOperation = nil;
    // 遍歷找到task對應的下載操作物件
    for (SDWebImageDownloaderOperation *operation in self.downloadQueue.operations) {
        if (operation.dataTask.taskIdentifier == task.taskIdentifier) {
            returnOperation = operation;
            break;
        }
    }
    // 返回下載操作物件
    return returnOperation;
}
複製程式碼

9.3 公共方法

+ (nonnull instancetype)sharedDownloader {
    // 獲取該類的單例物件
    static dispatch_once_t once;
    static id instance;
    dispatch_once(&once, ^{
        instance = [self new];
    });
    return instance;
}
複製程式碼
- (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration {
    if ((self = [super init])) {
        // 操作物件類預設是SDWebImageDownloaderOperation
        _operationClass = [SDWebImageDownloaderOperation class];
        // 預設是解壓縮影像
        _shouldDecompressImages = YES;
        // 預設順序是先進先出
        _executionOrder = SDWebImageDownloaderFIFOExecutionOrder;
        // 初始化操作佇列物件
        _downloadQueue = [NSOperationQueue new];
        // 預設最大併發下載數為6
        _downloadQueue.maxConcurrentOperationCount = 6;
        // 設定操作佇列物件的名稱
        _downloadQueue.name = @"com.hackemist.SDWebImageDownloader";
        // 初始化字典
        _URLOperations = [NSMutableDictionary new];
#ifdef SD_WEBP
        // 如果有webp格式的影像的HTTP請求頭
        _HTTPHeaders = [@{@"Accept": @"image/webp,image/*;q=0.8"} mutableCopy];
#else
        // 如果沒有webp格式的影像的HTTP請求頭
        _HTTPHeaders = [@{@"Accept": @"image/*;q=0.8"} mutableCopy];
#endif
        // 利用GCD的訊號量初始化鎖
        _operationsLock = dispatch_semaphore_create(1);
        // 利用GCD的訊號量初始化鎖
        _headersLock = dispatch_semaphore_create(1);
        // 預設15秒下載請求超時
        _downloadTimeout = 15.0;

        // 建立會話物件
        [self createNewSessionWithConfiguration:sessionConfiguration];
    }
    return self;
}
複製程式碼
- (void)createNewSessionWithConfiguration:(NSURLSessionConfiguration *)sessionConfiguration {
    // 取消所有下載
    [self cancelAllDownloads];

    // 如果當前有會話就物件就取消會話物件的任務並使會話物件無效
    if (self.session) {
        [self.session invalidateAndCancel];
    }

    // 設定會話配置物件的請求超時時間
    sessionConfiguration.timeoutIntervalForRequest = self.downloadTimeout;

    // 建立會話物件
    self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
                                                 delegate:self
                                            delegateQueue:nil];
}
複製程式碼
- (void)invalidateSessionAndCancel:(BOOL)cancelPendingOperations {
    // 當前類是SDWebImageDownloader的單例物件就不繼續向下執行
    if (self == [SDWebImageDownloader sharedDownloader]) {
        return;
    }
    if (cancelPendingOperations) {
        // 取消任務並使會話物件無效
        [self.session invalidateAndCancel];
    } else {
        // 等待任務完成並使會話物件無效
        [self.session finishTasksAndInvalidate];
    }
}
複製程式碼
- (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)field {
    // 加鎖
    LOCK(self.headersLock);
    if (value) {
        // 如果傳入了值,就給指定欄位賦值
        self.HTTPHeaders[field] = value;
    } else {
        // 如果未傳值,就刪除指定欄位
        [self.HTTPHeaders removeObjectForKey:field];
    }
    // 解鎖
    UNLOCK(self.headersLock);
}
複製程式碼
- (nullable NSString *)valueForHTTPHeaderField:(nullable NSString *)field {
    // 獲取HTTP頭中指定欄位的值
    if (!field) {
        return nil;
    }
    return [[self allHTTPHeaderFields] objectForKey:field];
}
複製程式碼
- (void)setOperationClass:(nullable Class)operationClass {
    // 操作物件類必須是NSOperation類的子類,並且遵守了SDWebImageDownloaderOperationInterface協議,否則就設定為SDWebImageDownloaderOperation類
    if (operationClass && [operationClass isSubclassOfClass:[NSOperation class]] && [operationClass conformsToProtocol:@protocol(SDWebImageDownloaderOperationInterface)]) {
        _operationClass = operationClass;
    } else {
        _operationClass = [SDWebImageDownloaderOperation class];
    }
}
複製程式碼
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
                                                   options:(SDWebImageDownloaderOptions)options
                                                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                                 completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
    __weak SDWebImageDownloader *wself = self;

    // 新增任務相關引數
    return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{
        __strong __typeof (wself) sself = wself;
        // 獲取下載請求超時時間,預設15秒
        NSTimeInterval timeoutInterval = sself.downloadTimeout;
        if (timeoutInterval == 0.0) {
            timeoutInterval = 15.0;
        }

        // 根據設定的選項不同,設定不同的請求快取策略
        NSURLRequestCachePolicy cachePolicy = options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData;
        // 建立請求物件
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url
                                                                    cachePolicy:cachePolicy
                                                                timeoutInterval:timeoutInterval];
                                                                
        // Cookies處理方式
        request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
        // 預設使用管線化
        request.HTTPShouldUsePipelining = YES;
        // 設定HTTP請求頭欄位
        if (sself.headersFilter) {
            request.allHTTPHeaderFields = sself.headersFilter(url, [sself allHTTPHeaderFields]);
        }
        else {
            request.allHTTPHeaderFields = [sself allHTTPHeaderFields];
        }
        // 建立下載操作物件
        SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];
        // 設定下載操作物件是否解壓縮
        operation.shouldDecompressImages = sself.shouldDecompressImages;
        
        // 設定下載操作物件的證書
        if (sself.urlCredential) {
            operation.credential = sself.urlCredential;
        } else if (sself.username && sself.password) {
            operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession];
        }
        
        // 設定下載操作物件的優先順序
        if (options & SDWebImageDownloaderHighPriority) {
            operation.queuePriority = NSOperationQueuePriorityHigh;
        } else if (options & SDWebImageDownloaderLowPriority) {
            operation.queuePriority = NSOperationQueuePriorityLow;
        }
        
        // 如果設定了後進先出就通過新增依賴的方式實現
        if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
            [sself.lastAddedOperation addDependency:operation];
            sself.lastAddedOperation = operation;
        }

        // 將生成的操作物件作為引數傳遞
        return operation;
    }];
}
複製程式碼
- (void)cancel:(nullable SDWebImageDownloadToken *)token {
    // 獲取token中的url,如果沒有就不繼續執行了
    NSURL *url = token.url;
    if (!url) {
        return;
    }
    // 加鎖
    LOCK(self.operationsLock);
    // 獲取到url對應的操作物件
    SDWebImageDownloaderOperation *operation = [self.URLOperations objectForKey:url];
    // 取消操作
    if (operation) {
        BOOL canceled = [operation cancel:token.downloadOperationCancelToken];
        // 移除url對應的操作物件
        if (canceled) {
            [self.URLOperations removeObjectForKey:url];
        }
    }
    // 解鎖
    UNLOCK(self.operationsLock);
}
複製程式碼
- (void)setSuspended:(BOOL)suspended {
    // 設定暫停狀態
    self.downloadQueue.suspended = suspended;
}
複製程式碼
- (void)cancelAllDownloads {
    // 取消所有操作
    [self.downloadQueue cancelAllOperations];
}
複製程式碼

9.4 自定義getting/setting

- (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads {
    // 直接給下載操作佇列物件的最大併發數賦值
    _downloadQueue.maxConcurrentOperationCount = maxConcurrentDownloads;
}

- (NSInteger)maxConcurrentDownloads {
    // 返回下載操作佇列物件的最大併發數
    return _downloadQueue.maxConcurrentOperationCount;
}
複製程式碼
- (NSUInteger)currentDownloadCount {
    // 返回當前下載操作佇列中操作物件數量
    return _downloadQueue.operationCount;
}
複製程式碼
- (NSURLSessionConfiguration *)sessionConfiguration {
    // 返回會話物件配置物件
    return self.session.configuration;
}
複製程式碼

9.5 NSURLSessionDataDelegate方法實現

- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {

    // 如果呼叫這個代理方法的dataTask是SDWebImageDownloaderOperation生成的,就將代理方法實現轉交給SDWebImageDownloaderOperation類物件去實現
    SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask];
    if ([dataOperation respondsToSelector:@selector(URLSession:dataTask:didReceiveResponse:completionHandler:)]) {
        [dataOperation URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler];
    } else {
        // 如果不是的話就預設響應意向為允許
        if (completionHandler) {
            completionHandler(NSURLSessionResponseAllow);
        }
    }
}
複製程式碼
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {

    // 如果呼叫這個代理方法的dataTask是SDWebImageDownloaderOperation生成的,就將代理方法實現轉交給SDWebImageDownloaderOperation類物件去實現
    SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask];
    if ([dataOperation respondsToSelector:@selector(URLSession:dataTask:didReceiveData:)]) {
        [dataOperation URLSession:session dataTask:dataTask didReceiveData:data];
    }
}
複製程式碼
- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
 willCacheResponse:(NSCachedURLResponse *)proposedResponse
 completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler {

    // 如果呼叫這個代理方法的dataTask是SDWebImageDownloaderOperation生成的,就將代理方法實現轉交給SDWebImageDownloaderOperation類物件去實現
    SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask];
    if ([dataOperation respondsToSelector:@selector(URLSession:dataTask:willCacheResponse:completionHandler:)]) {
        [dataOperation URLSession:session dataTask:dataTask willCacheResponse:proposedResponse completionHandler:completionHandler];
    } else {
        if (completionHandler) {
            completionHandler(proposedResponse);
        }
    }
}
複製程式碼

9.6 NSURLSessionTaskDelegate方法實現

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    
    // 如果呼叫這個代理方法的dataTask是SDWebImageDownloaderOperation生成的,就將代理方法實現轉交給SDWebImageDownloaderOperation類物件去實現
    SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:task];
    if ([dataOperation respondsToSelector:@selector(URLSession:task:didCompleteWithError:)]) {
        [dataOperation URLSession:session task:task didCompleteWithError:error];
    }
}
複製程式碼
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler {
    
    // 如果呼叫這個代理方法的dataTask是SDWebImageDownloaderOperation生成的,就將代理方法實現轉交給SDWebImageDownloaderOperation類物件去實現
    SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:task];
    if ([dataOperation respondsToSelector:@selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)]) {
        [dataOperation URLSession:session task:task willPerformHTTPRedirection:response newRequest:request completionHandler:completionHandler];
    } else {
        if (completionHandler) {
            completionHandler(request);
        }
    }
}
複製程式碼
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {

    // 如果呼叫這個代理方法的dataTask是SDWebImageDownloaderOperation生成的,就將代理方法實現轉交給SDWebImageDownloaderOperation類物件去實現
    SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:task];
    if ([dataOperation respondsToSelector:@selector(URLSession:task:didReceiveChallenge:completionHandler:)]) {
        [dataOperation URLSession:session task:task didReceiveChallenge:challenge completionHandler:completionHandler];
    } else {
        if (completionHandler) {
            completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
        }
    }
}
複製程式碼

10.總結

這個類從字面上理解是一個下載器類,實際看程式碼,發現這個類並沒有做實際的下載,而是維護著一個NSURLSession物件和一個NSOperationQueue物件,管理著一些SDWebImageDownloaderOperation物件。

SDWebImageDownloader通過傳入的引數生成一個SDWebImageDownloaderOperation物件,並新增到操作佇列中。具體的網路下載操作則由SDWebImageDownloaderOperation物件自己處理。

原始碼閱讀系列:SDWebImage

原始碼閱讀: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

相關文章