SDWebImage源碼分析(二)

上一章解析到- (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è)類的作用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末山橄,一起剝皮案震驚了整個(gè)濱河市垮媒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌航棱,老刑警劉巖睡雇,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異饮醇,居然都是意外死亡它抱,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門驳阎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)抗愁,“玉大人,你說(shuō)我怎么就攤上這事呵晚。” “怎么了沫屡?”我有些...
    開(kāi)封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵饵隙,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我沮脖,道長(zhǎng)金矛,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任勺届,我火速辦了婚禮驶俊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘免姿。我一直安慰自己饼酿,他們只是感情好糜烹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著暮芭,像睡著了一般胎围。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上药版,一...
    開(kāi)封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天辑舷,我揣著相機(jī)與錄音,去河邊找鬼槽片。 笑死何缓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的还栓。 我是一名探鬼主播碌廓,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蝙云!你這毒婦竟也來(lái)了氓皱?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤勃刨,失蹤者是張志新(化名)和其女友劉穎波材,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體身隐,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡廷区,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贾铝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片隙轻。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖垢揩,靈堂內(nèi)的尸體忽然破棺而出玖绿,到底是詐尸還是另有隱情,我是刑警寧澤叁巨,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布斑匪,位于F島的核電站,受9級(jí)特大地震影響锋勺,放射性物質(zhì)發(fā)生泄漏蚀瘸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一庶橱、第九天 我趴在偏房一處隱蔽的房頂上張望贮勃。 院中可真熱鬧,春花似錦苏章、人聲如沸寂嘉。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)垫释。三九已至丝格,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間棵譬,已是汗流浹背显蝌。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留订咸,地道東北人曼尊。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像脏嚷,于是被迫代替她去往敵國(guó)和親骆撇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容