上一章解析到- (id)loadImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock 中self.imageCache? 調(diào)用 - (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock doneBlock
回調(diào)doneBlock 執(zhí)行
回調(diào)內(nèi)容比較多,所以分部分貼代碼
第一段
if (operation.isCancelled) {
[self safelyRemoveOperationFromRunning:operation];
return;
}
- (void)safelyRemoveOperationFromRunning:(nullable SDWebImageCombinedOperation*)operation {
@synchronized (self.runningOperations) {
if (operation) {
[self.runningOperations removeObject:operation];
}
}
}
這個(gè)很簡(jiǎn)單 ,要是 線程被取消掉了,那么就從runningOperations 數(shù)組中將該操作移除
第二段
if ((!cachedImage || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url]))
要是沒(méi)有cachedImage? 或者 options 配置SDWebImageRefreshCached (刷新緩存) 或者是self.delegate 調(diào)用- (BOOL)imageManager:(nonnull SDWebImageManager *)imageManager shouldDownloadImageForURL:(nullable NSURL *)imageURL; 返回YES 就往下執(zhí)行操作
第三段 是假設(shè)第二段需要繼續(xù)執(zhí)行的返回YES
if (cachedImage && options & SDWebImageRefreshCached) {
// If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image
// AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
[self callCompletionBlockForOperation:weakOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
}
要是 上面都滿足 cachedImage && options & SDWebImageRefreshCached 還是進(jìn)來(lái)的話我們調(diào)用下面這個(gè)函數(shù)
- (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 這個(gè)函數(shù)后面分析
第四段 是假設(shè)第二段需要繼續(xù)執(zhí)行的返回YES
// download if no image or requested to refresh anyway, and download allowed by delegate
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) {
// force progressive off if image already cached but forced refreshing
downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
// ignore image read from NSURLCache if image if cached but force refreshing
downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
}
這些都是配置下載項(xiàng) 最后一項(xiàng)比較特殊,要是 有緩存圖片,并且配置刷新圖片,那么我們就需要配置下載選項(xiàng)怕篷,清除下載進(jìn)度條 并且要忽略cached 配置。
第五段 是假設(shè)第二段需要繼續(xù)執(zhí)行的返回YES
SDWebImageDownloadToken *subOperationToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished){ ?..... ? }
這個(gè)就是開(kāi)始調(diào)用imageDownLoader 進(jìn)行下載操作了
進(jìn)入函數(shù)- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
options:(SDWebImageDownloaderOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock ?
這個(gè)函數(shù)分兩部分看我們以 《》標(biāo)記
《1》第一部分是調(diào)用
[self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{....}
進(jìn)入該函數(shù)
- (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock
forURL:(nullable NSURL *)url
createCallback:(SDWebImageDownloaderOperation *(^)())createCallback {
// The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data.
if (url == nil) {
if (completedBlock != nil) {
completedBlock(nil, nil, nil, NO);
}
return nil;
}
__block SDWebImageDownloadToken *token = nil;
dispatch_barrier_sync(self.barrierQueue, ^{
SDWebImageDownloaderOperation *operation = self.URLOperations[url];
if (!operation) {
operation = createCallback();
self.URLOperations[url] = operation;
__weak SDWebImageDownloaderOperation *woperation = operation;
operation.completionBlock = ^{
dispatch_barrier_sync(self.barrierQueue, ^{
SDWebImageDownloaderOperation *soperation = woperation;
if (!soperation) return;
if (self.URLOperations[url] == soperation) {
[self.URLOperations removeObjectForKey:url];
};
});
};
}
id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
token = [SDWebImageDownloadToken new];
token.url = url;
token.downloadOperationCancelToken = downloadOperationCancelToken;
});
return token;
}
第一步:檢查url 是不是nil 是nil 直接執(zhí)行block 并且返回
第二步:調(diào)用GCD? dispatch_barrier_sync 切換到self.barrierQueue 線程操作?
第三步:檢查下載URLOperations 字典中有沒(méi)有這個(gè)url的 operation?
第四步:要是沒(méi)有桶蛔,就調(diào)用createCallback 生成一個(gè)operation? 把 operation 放入 URLOperations 字段中匙头,key 是url,并且給operation 完成的completionBlock 賦值仔雷。completionBlock block 調(diào)用的話會(huì)執(zhí)行蹂析,切換到self.barrierQueue 線程,檢查self.URLOperations 中url 對(duì)應(yīng)的value 是不是和現(xiàn)在的這個(gè)一樣碟婆,要是一樣的話电抚,就沖線程字典中移除
第五步調(diào)用 - (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock 。
- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
SDCallbacksDictionary *callbacks = [NSMutableDictionary new];
if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
dispatch_barrier_async(self.barrierQueue, ^{
[self.callbackBlocks addObject:callbacks];
});
return callbacks;
}
這個(gè)函數(shù) 就是給opertion 數(shù)組中添加 一個(gè)字典竖共,字典中包含兩個(gè)兩個(gè)回調(diào)函數(shù)蝙叛,并且返回這個(gè)字典。
第六步 生成一個(gè) SDWebImageDownloadToken 對(duì)象公给,改對(duì)象保存 請(qǐng)求url 和 url 進(jìn)度條操作和完成操作
《2》第二部分是 callBack
__strong __typeof (wself) sself = wself;
NSTimeInterval timeoutInterval = sself.downloadTimeout;
if (timeoutInterval == 0.0) {
timeoutInterval = 15.0;
}
// In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise
NSURLRequestCachePolicy cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
if (options & SDWebImageDownloaderUseNSURLCache) {
if (options & SDWebImageDownloaderIgnoreCachedResponse) {
cachePolicy = NSURLRequestReturnCacheDataDontLoad;
} else {
cachePolicy = NSURLRequestUseProtocolCachePolicy;
}
}
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:cachePolicy timeoutInterval:timeoutInterval];
request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
request.HTTPShouldUsePipelining = YES;
if (sself.headersFilter) {
request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]);
}
else {
request.allHTTPHeaderFields = sself.HTTPHeaders;
}
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;
}
[sself.downloadQueue addOperation:operation];
if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
// Emulate LIFO execution order by systematically adding new operations as last operation's dependency
[sself.lastAddedOperation addDependency:operation];
sself.lastAddedOperation = operation;
}
return operation;
我看著現(xiàn)在感覺(jué)就是山路十八彎啊借帘,繞來(lái)繞去蜘渣,據(jù)我估計(jì)看博客的人也會(huì)繞暈。
沒(méi)事看不懂也要強(qiáng)行裝逼看肺然∧韪祝看完最后總結(jié)路線。
這個(gè)block 回調(diào)我們以<> 來(lái)標(biāo)記
<1>第一部分:先判斷strongOperation 是否取消掉了际起。要是取消掉了啥呀不做
<2>第二部分:要是block參數(shù) error 不是nil 那么就執(zhí)行[self callCompletionBlockForOperation:strongOperation completion:completedBlock error:error url:url];? 并且檢查error 錯(cuò)誤拾碌,不是NSURLErrorNotConnectedToInternet,NSURLErrorCancelled街望,NSURLErrorTimedOut校翔,NSURLErrorInternationalRoamingOff,NSURLErrorDataNotAllowed灾前,NSURLErrorCannotFindHost防症,NSURLErrorCannotConnectToHost,NSURLErrorNetworkConnectionLost那么就將該url 加入到failedURLs數(shù)組中豫柬。
<3>第三部分:檢查options? 是不是配置 SDWebImageRetryFailed 要是配置該參數(shù)
就將url從failure 數(shù)組中移除掉
<4>第四部分: options & SDWebImageRefreshCached && cachedImage && !downloadedImage? 如果配置 SDWebImageRefreshCached 并且 cachedImage 但是 downloadedImage 參數(shù)沒(méi)有值就不做任何處理告希。
<5>第五部分:downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]?
這么多條件,執(zhí)行下面操作?
異步dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),并發(fā)線程執(zhí)行[self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url]獲取url
要是 transformedImage? 有并且 finished =yes的話烧给,self.imageCache 緩存圖片
- (void)storeImage:(nullable UIImage *)image
imageData:(nullable NSData *)imageData
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock {
if (!image || !key) {
if (completionBlock) {
completionBlock();
}
return;
}
// if memory cache is enabled
if (self.config.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(image);
[self.memCache setObject:image forKey:key cost:cost];
}
if (toDisk) {
dispatch_async(self.ioQueue, ^{
@autoreleasepool {
NSData *data = imageData;
if (!data && image) {
SDImageFormat imageFormatFromData = [NSData sd_imageFormatForImageData:data];
data = [image sd_imageDataAsFormat:imageFormatFromData];
}
[self storeImageDataToDisk:data forKey:key];
}
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
});
}
});
} else {
if (completionBlock) {
completionBlock();
}
}
}
分析下這個(gè)函數(shù)
1 :要是沒(méi)有image 或者key 如果有completionBlock 直接調(diào)用completionBlock
2:要還是配置shouldCacheImagesInMemory 那么緩存 image 到記憶緩存
3:要是toDisk 不為空,切換到ioQueue 線程喝噪,判斷是否有圖片或者data 础嫡,沒(méi)有data 但是有 image情況,那么就生成一個(gè)data酝惧,
- (nullable NSData *)sd_imageDataAsFormat:(SDImageFormat)imageFormat {
NSData *imageData = nil;
if (self) {
#if SD_UIKIT || SD_WATCH
int alphaInfo = CGImageGetAlphaInfo(self.CGImage);
BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||
alphaInfo == kCGImageAlphaNoneSkipFirst ||
alphaInfo == kCGImageAlphaNoneSkipLast);
BOOL usePNG = hasAlpha;
// the imageFormat param has priority here. But if the format is undefined, we relly on the alpha channel
if (imageFormat != SDImageFormatUndefined) {
usePNG = (imageFormat == SDImageFormatPNG);
}
if (usePNG) {
imageData = UIImagePNGRepresentation(self);
} else {
imageData = UIImageJPEGRepresentation(self, (CGFloat)1.0);
}
#else
NSBitmapImageFileType imageFileType = NSJPEGFileType;
if (imageFormat == SDImageFormatGIF) {
imageFileType = NSGIFFileType;
} else if (imageFormat == SDImageFormatPNG) {
imageFileType = NSPNGFileType;
}
imageData = [NSBitmapImageRep representationOfImageRepsInArray:self.representations
usingType:imageFileType
properties:@{}];
#endif
}
return imageData;
}
用image 生成data 的函數(shù)中 榴鼎,檢查是否生成png data 還是生成jpg格式圖片。
- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key {
if (!imageData || !key) {
return;
}
[self checkIfQueueIsIOQueue];
if (![_fileManager fileExistsAtPath:_diskCachePath]) {
[_fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
}
// get cache Path for image key
NSString *cachePathForKey = [self defaultCachePathForKey:key];
// transform to NSUrl
NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];
[_fileManager createFileAtPath:cachePathForKey contents:imageData attributes:nil];
// disable iCloud backup
if (self.config.shouldDisableiCloud) {
[fileURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
}
}
先檢查 當(dāng)前線程是不是IOQueue 不是就輸出log晚唇,再堅(jiān)持路徑巫财,沒(méi)有路徑就生成路徑,生成圖片名字哩陕,將生成的路徑轉(zhuǎn)換成NSURL 平项。
將數(shù)據(jù)寫入磁盤, 如果shouldDisableiCloud 打開(kāi)悍及,就執(zhí)行這個(gè)操作
[fileURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
NSURLIsExcludedFromBackupKey 屬性看這里
4:將data 寫入磁盤
5:有completionBlock 回到主線程調(diào)用completionBlock
6:其他情況闽瓢,有completionBlock 回到主線程調(diào)用completionBlock
《3》第三部分:cancelblock?
operation.cancelBlock = ^{
[self.imageDownloader cancel:subOperationToken];
__strong __typeof(weakOperation) strongOperation = weakOperation;
[self safelyRemoveOperationFromRunning:strongOperation];
};
這個(gè)就是給operation.cancelBlock 賦值, 具體執(zhí)行啥呢?
- (void)cancel:(nullable SDWebImageDownloadToken *)token {
dispatch_barrier_async(self.barrierQueue, ^{
SDWebImageDownloaderOperation *operation = self.URLOperations[token.url];
BOOL canceled = [operation cancel:token.downloadOperationCancelToken];
if (canceled) {
[self.URLOperations removeObjectForKey:token.url];
}
});
}
切換到 barrierQueue 線程心赶,找到 URLOperations 對(duì)應(yīng)URL的SDWebImageDownloaderOperation
- (BOOL)cancel:(nullable id)token {
__block BOOL shouldCancel = NO;
dispatch_barrier_sync(self.barrierQueue, ^{
[self.callbackBlocks removeObjectIdenticalTo:token];
if (self.callbackBlocks.count == 0) {
shouldCancel = YES;
}
});
if (shouldCancel) {
[self cancel];
}
return shouldCancel;
}
從 self.callbackBlocks 移除對(duì)象token 扣讼。要是數(shù)組的數(shù)量沒(méi)有就調(diào)用cancel 這個(gè)設(shè)計(jì)到nsopertion 的取消操作,網(wǎng)絡(luò)部分缨叫,暫時(shí)不討論椭符。
- (void)safelyRemoveOperationFromRunning:(nullable SDWebImageCombinedOperation*)operation {
@synchronized (self.runningOperations) {
if (operation) {
[self.runningOperations removeObject:operation];
}
}
}?
最后從self.runningOperations 移除數(shù)據(jù)荔燎。
第六段 我們看 self.imageDownloader 調(diào)用- (id)loadImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock 返回的block塊
else if (cachedImage) {
__strong __typeof(weakOperation) strongOperation = weakOperation;
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
[self safelyRemoveOperationFromRunning:operation];
}
要是有緩存,就執(zhí)行 回調(diào)移除opertion
else {
// Image not in cache and download disallowed by delegate
__strong __typeof(weakOperation) strongOperation = weakOperation;
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];
[self safelyRemoveOperationFromRunning:operation];
}
最后一種情況一樣調(diào)用上面的函數(shù)销钝。
到此- (id)loadImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock分析完畢湖雹,據(jù)我估計(jì)看博客的人肯定看的一臉懵逼。沒(méi)關(guān)系曙搬,我和你一樣的狀態(tài)摔吏。
接著往下看。不看懂誓不罷休纵装。
返回到- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
placeholderImage:(nullable UIImage *)placeholder
options:(SDWebImageOptions)options
operationKey:(nullable NSString *)operationKey
setImageBlock:(nullable SDSetImageBlock)setImageBlock
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDExternalCompletionBlock)completedBlock 函數(shù)中往下看征讲。
[self sd_setImageLoadOperation:operation forKey:validOperationKey];?
在這個(gè)if 語(yǔ)句中最后一塊。將opertion?
- (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key {? ? if (key) {? ? ? ? [self sd_cancelImageLoadOperationWithKey:key];? ? ? ? if (operation) {? ? ? ? ? ? SDOperationsDictionary *operationDictionary = [self operationDictionary];? ? ? ? ? ? operationDictionary[key] = operation;? ? ? ? }? ? }}- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key {? ? // Cancel in progress downloader from queue? ? SDOperationsDictionary *operationDictionary = [self operationDictionary];? ? id operations = operationDictionary[key];? ? if (operations) {? ? ? ? if ([operations isKindOfClass:[NSArray class]]) {? ? ? ? ? ? for (idoperation in operations) {? ? ? ? ? ? ? ? if (operation) {? ? ? ? ? ? ? ? ? ? [operation cancel];? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? } else if ([operations conformsToProtocol:@protocol(SDWebImageOperation)]){? ? ? ? ? ? [(id) operations cancel];
}
[operationDictionary removeObjectForKey:key];
}
}
- (void)sd_removeImageLoadOperationWithKey:(nullable NSString *)key {
if (key) {
SDOperationsDictionary *operationDictionary = [self operationDictionary];
[operationDictionary removeObjectForKey:key];
}
}
- (SDOperationsDictionary *)operationDictionary {
SDOperationsDictionary *operations = objc_getAssociatedObject(self, &loadOperationKey);
if (operations) {
return operations;
}
operations = [NSMutableDictionary dictionary];
objc_setAssociatedObject(self, &loadOperationKey, operations, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return operations;
}
在- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key 函數(shù)中調(diào)用
- (SDOperationsDictionary *)operationDictionary
這個(gè)函數(shù)比較簡(jiǎn)單橡娄,就是self 關(guān)聯(lián)一個(gè)引用字典诗箍,返回這個(gè)self關(guān)聯(lián)字典, 在字典里面找key關(guān)聯(lián)的值挽唉,如果找到了滤祖,再檢測(cè)改之是不是NSArray ,是就讓array里面的value 執(zhí)行cancel操作瓶籽,不是array 檢測(cè)是不是遵守SDWebImageOperation 協(xié)議匠童,是就執(zhí)行cancel操作,最后從dic移除該key
- (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key這個(gè)函數(shù)就是取消所有operation的操作塑顺,再增加最近的opertion
到目前為止汤求,應(yīng)該已經(jīng)在下載了,什么在下載严拒?我特么都沒(méi)看見(jiàn)下載代碼扬绪。我一臉懵逼。
沒(méi)辦法裤唠,誰(shuí)叫咱是菜鳥呢挤牛?只能打斷點(diǎn)實(shí)驗(yàn)了。
全局搜索 addOperation 只有一個(gè)地方种蘸,我了個(gè)去墓赴。
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
options:(SDWebImageDownloaderOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock函數(shù)中,- (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock
forURL:(nullable NSURL *)url
createCallback:(SDWebImageDownloaderOperation *(^)())createCallback ?的回調(diào)函數(shù)忘記分析了
下面分析這個(gè)block
我們用 1> 標(biāo)記
1> 首先獲取超時(shí)時(shí)間劈彪,默認(rèn)是15s
2>設(shè)置緩存策略
3>生成request
4>配置request 相關(guān)參數(shù)
5>生成 SDWebImageDownloaderOperation?
6>配置賬號(hào)和密碼
7>將operation 增加到queue中
8>下載操作看怎么進(jìn)出的
發(fā)起網(wǎng)絡(luò)請(qǐng)求就是這么簡(jiǎn)單竣蹦。那我們看看下載完成進(jìn)行啥操作了吧
那網(wǎng)絡(luò)請(qǐng)求發(fā)出去了。那么看數(shù)據(jù)回來(lái)怎么操作的
看SDWebImageDownloaderOperation 函數(shù)數(shù)據(jù)回調(diào)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
[self.imageData appendData:data];
if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0) {
// The following code is from http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/
// Thanks to the author @Nyx0uf
// Get the total bytes downloaded
const NSInteger totalSize = self.imageData.length;
// Update the data source, we must pass ALL the data, not just the new bytes
CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)self.imageData, NULL);
if (width + height == 0) {
CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);
if (properties) {
NSInteger orientationValue = -1;
CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight);
if (val) CFNumberGetValue(val, kCFNumberLongType, &height);
val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth);
if (val) CFNumberGetValue(val, kCFNumberLongType, &width);
val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation);
if (val) CFNumberGetValue(val, kCFNumberNSIntegerType, &orientationValue);
CFRelease(properties);
// When we draw to Core Graphics, we lose orientation information,
// which means the image below born of initWithCGIImage will be
// oriented incorrectly sometimes. (Unlike the image born of initWithData
// in didCompleteWithError.) So save it here and pass it on later.
#if SD_UIKIT || SD_WATCH
orientation = [[self class] orientationFromPropertyValue:(orientationValue == -1 ? 1 : orientationValue)];
#endif
}
}
if (width + height > 0 && totalSize < self.expectedSize) {
// Create the image
CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
#if SD_UIKIT || SD_WATCH
// Workaround for iOS anamorphic image
if (partialImageRef) {
const size_t partialHeight = CGImageGetHeight(partialImageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8, width * 4, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(colorSpace);
if (bmContext) {
CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = width, .size.height = partialHeight}, partialImageRef);
CGImageRelease(partialImageRef);
partialImageRef = CGBitmapContextCreateImage(bmContext);
CGContextRelease(bmContext);
}
else {
CGImageRelease(partialImageRef);
partialImageRef = nil;
}
}
#endif
if (partialImageRef) {
#if SD_UIKIT || SD_WATCH
UIImage *image = [UIImage imageWithCGImage:partialImageRef scale:1 orientation:orientation];
#elif SD_MAC
UIImage *image = [[UIImage alloc] initWithCGImage:partialImageRef size:NSZeroSize];
#endif
NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
UIImage *scaledImage = [self scaledImageForKey:key image:image];
if (self.shouldDecompressImages) {
image = [UIImage decodedImageWithImage:scaledImage];
}
else {
image = scaledImage;
}
CGImageRelease(partialImageRef);
[self callCompletionBlocksWithImage:image imageData:nil error:nil finished:NO];
}
}
CFRelease(imageSource);
}
for (SDWebImageDownloaderProgressBlock progressBlock in [self callbacksForKey:kProgressCallbackKey]) {
progressBlock(self.imageData.length, self.expectedSize, self.request.URL);
}
}
分步看
第一步:[self.imageData appendData:data]; 將數(shù)據(jù)追加到self.imageData;
第二步:判斷self.options & SDWebImageDownloaderProgressiveDownload && self.expectedSize>0是不是YES?
是的話沧奴,執(zhí)行下面操作痘括。self.expectedSize 是在響應(yīng)頭獲取的
第三步:生成CGImageSourceRef 根據(jù)self.imageData
第四步 : 根據(jù)width + Height == 0 獲取圖片的width Height 還有orientation?
第五步: 要是 寬高有值了。 生成CGImageRef?
第六步 要是生成了 CGImageRef 那就生成bitMap的bmContext?
第七步 : 如果生成的bmContext 不是nil 就用bmContext 生成? partialImageRef image
第八步:要是 partialImageRef 轉(zhuǎn)換成UIImage
第九步:對(duì)圖片處理并返回
最后一步:要是有進(jìn)度條。那就執(zhí)行進(jìn)度條
其實(shí)接受數(shù)據(jù)部分就是對(duì)不完整數(shù)據(jù)進(jìn)行生成圖片加載纲菌。
當(dāng)數(shù)據(jù)都接受完成的時(shí)候
- (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];
} else {
if ([self callbacksForKey:kCompletedCallbackKey].count > 0) {
/**
*? If you specified to use `NSURLCache`, then the response you get here is what you need.
*? if you specified to only use cached data via `SDWebImageDownloaderIgnoreCachedResponse`,
*? the response data will be nil.
*? So we don't need to check the cache option here, since the system will obey the cache option
*/
if (self.imageData) {
UIImage *image = [UIImage sd_imageWithData:self.imageData];
NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
image = [self scaledImageForKey:key image:image];
// Do not force decoding animated GIFs
if (!image.images) {
if (self.shouldDecompressImages) {
if (self.options & SDWebImageDownloaderScaleDownLargeImages) {
#if SD_UIKIT || SD_WATCH
image = [UIImage decodedAndScaledDownImageWithImage:image];
[self.imageData setData:UIImagePNGRepresentation(image)];
#endif
} else {
image = [UIImage decodedImageWithImage:image];
}
}
}
if (CGSizeEqualToSize(image.size, CGSizeZero)) {
[self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}]];
} else {
[self callCompletionBlocksWithImage:image imageData:self.imageData error:nil finished:YES];
}
} else {
[self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}]];
}
}
}
[self done];
}
第一步:發(fā)通知
第二步:判斷請(qǐng)求是否錯(cuò)誤 error 就調(diào)用error回調(diào)
第三步:如果有完成回調(diào) 那么執(zhí)行下面操作
第四步:要是self.imageData 有數(shù)據(jù)挠日,那么就把imageData 生成image 根據(jù)響應(yīng)的option 對(duì)image進(jìn)行操作
看到這里沒(méi)看見(jiàn)緩存,那么繼續(xù)找緩存在哪里翰舌。
其實(shí)在上面分析的block中已經(jīng)完成緩存分析了嚣潜。只有由于跳轉(zhuǎn)太多,并且不是嚴(yán)格按照請(qǐng)求image 來(lái)分析的椅贱,所以看著有點(diǎn)亂懂算,無(wú)頭無(wú)需的。沒(méi)關(guān)系庇麦。學(xué)習(xí)么计技?那那么容易一下看懂。那我們下篇就先分析分析我們請(qǐng)求過(guò)程中使用的每個(gè)類的作用