上一章解析到- (id)loadImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock 中self.imageCache 调用 - (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock doneBlock
回调doneBlock 执行
回调内容比较多,所以分部分贴代码
第一段
if (operation.isCancelled) {
[self safelyRemoveOperationFromRunning:operation];
return;
}
- (void)safelyRemoveOperationFromRunning:(nullable SDWebImageCombinedOperation*)operation {
@synchronized (self.runningOperations) {
if (operation) {
[self.runningOperations removeObject:operation];
}
}
}
这个很简单 ,要是 线程被取消掉了,那么就从runningOperations 数组中将该操作移除
第二段
if ((!cachedImage || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url]))
要是没有cachedImage 或者 options 配置SDWebImageRefreshCached (刷新缓存) 或者是self.delegate 调用- (BOOL)imageManager:(nonnull SDWebImageManager *)imageManager shouldDownloadImageForURL:(nullable NSURL *)imageURL; 返回YES 就往下执行操作
第三段 是假设第二段需要继续执行的返回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 还是进来的话我们调用下面这个函数
- (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 这个函数后面分析
第四段 是假设第二段需要继续执行的返回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;
}
这些都是配置下载项 最后一项比较特殊,要是 有缓存图片,并且配置刷新图片,那么我们就需要配置下载选项,清除下载进度条 并且要忽略cached 配置。
第五段 是假设第二段需要继续执行的返回YES
SDWebImageDownloadToken *subOperationToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished){ ..... }
这个就是开始调用imageDownLoader 进行下载操作了
进入函数- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
options:(SDWebImageDownloaderOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock
这个函数分两部分看我们以 《》标记
《1》第一部分是调用
[self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{....}
进入该函数
- (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 直接执行block 并且返回
第二步:调用GCD dispatch_barrier_sync 切换到self.barrierQueue 线程操作
第三步:检查下载URLOperations 字典中有没有这个url的 operation
第四步:要是没有,就调用createCallback 生成一个operation 把 operation 放入 URLOperations 字段中,key 是url,并且给operation 完成的completionBlock 赋值。completionBlock block 调用的话会执行,切换到self.barrierQueue 线程,检查self.URLOperations 中url 对应的value 是不是和现在的这个一样,要是一样的话,就冲线程字典中移除
第五步调用 - (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;
}
这个函数 就是给opertion 数组中添加 一个字典,字典中包含两个两个回调函数,并且返回这个字典。
第六步 生成一个 SDWebImageDownloadToken 对象,改对象保存 请求url 和 url 进度条操作和完成操作
《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;
我看着现在感觉就是山路十八弯啊,绕来绕去,据我估计看博客的人也会绕晕。
没事看不懂也要强行装逼看。看完最后总结路线。
这个block 回调我们以<> 来标记
<1>第一部分:先判断strongOperation 是否取消掉了。要是取消掉了啥呀不做
<2>第二部分:要是block参数 error 不是nil 那么就执行[self callCompletionBlockForOperation:strongOperation completion:completedBlock error:error url:url]; 并且检查error 错误,不是NSURLErrorNotConnectedToInternet,NSURLErrorCancelled,NSURLErrorTimedOut,NSURLErrorInternationalRoamingOff,NSURLErrorDataNotAllowed,NSURLErrorCannotFindHost,NSURLErrorCannotConnectToHost,NSURLErrorNetworkConnectionLost那么就将该url 加入到failedURLs数组中。
<3>第三部分:检查options 是不是配置 SDWebImageRetryFailed 要是配置该参数
就将url从failure 数组中移除掉
<4>第四部分: options & SDWebImageRefreshCached && cachedImage && !downloadedImage 如果配置 SDWebImageRefreshCached 并且 cachedImage 但是 downloadedImage 参数没有值就不做任何处理。
<5>第五部分:downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]
这么多条件,执行下面操作
异步dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),并发线程执行[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();
}
}
}
分析下这个函数
1 :要是没有image 或者key 如果有completionBlock 直接调用completionBlock
2:要还是配置shouldCacheImagesInMemory 那么缓存 image 到记忆缓存
3:要是toDisk 不为空,切换到ioQueue 线程,判断是否有图片或者data ,没有data 但是有 image情况,那么就生成一个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 的函数中 ,检查是否生成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];
}
}
先检查 当前线程是不是IOQueue 不是就输出log,再坚持路径,没有路径就生成路径,生成图片名字,将生成的路径转换成NSURL 。
将数据写入磁盘, 如果shouldDisableiCloud 打开,就执行这个操作
[fileURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
NSURLIsExcludedFromBackupKey 属性看这里
4:将data 写入磁盘
5:有completionBlock 回到主线程调用completionBlock
6:其他情况,有completionBlock 回到主线程调用completionBlock
《3》第三部分:cancelblock
operation.cancelBlock = ^{
[self.imageDownloader cancel:subOperationToken];
__strong __typeof(weakOperation) strongOperation = weakOperation;
[self safelyRemoveOperationFromRunning:strongOperation];
};
这个就是给operation.cancelBlock 赋值, 具体执行啥呢
- (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 对应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 移除对象token 。要是数组的数量没有就调用cancel 这个设计到nsopertion 的取消操作,网络部分,暂时不讨论。
- (void)safelyRemoveOperationFromRunning:(nullable SDWebImageCombinedOperation*)operation {
@synchronized (self.runningOperations) {
if (operation) {
[self.runningOperations removeObject:operation];
}
}
}
最后从self.runningOperations 移除数据。
第六段 我们看 self.imageDownloader 调用- (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];
}
要是有缓存,就执行 回调移除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];
}
最后一种情况一样调用上面的函数。
到此- (id)loadImageWithURL:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock分析完毕,据我估计看博客的人肯定看的一脸懵逼。没关系,我和你一样的状态。
接着往下看。不看懂誓不罢休。
返回到- (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 函数中往下看。
[self sd_setImageLoadOperation:operation forKey:validOperationKey];
在这个if 语句中最后一块。将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 函数中调用
- (SDOperationsDictionary *)operationDictionary
这个函数比较简单,就是self 关联一个引用字典,返回这个self关联字典, 在字典里面找key关联的值,如果找到了,再检测改之是不是NSArray ,是就让array里面的value 执行cancel操作,不是array 检测是不是遵守SDWebImageOperation 协议,是就执行cancel操作,最后从dic移除该key
- (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key这个函数就是取消所有operation的操作,再增加最近的opertion
到目前为止,应该已经在下载了,什么在下载?我特么都没看见下载代码。我一脸懵逼。
没办法,谁叫咱是菜鸟呢?只能打断点实验了。
全局搜索 addOperation 只有一个地方,我了个去。
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
options:(SDWebImageDownloaderOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock函数中,- (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock
forURL:(nullable NSURL *)url
createCallback:(SDWebImageDownloaderOperation *(^)())createCallback 的回调函数忘记分析了
下面分析这个block
我们用 1> 标记
1> 首先获取超时时间,默认是15s
2>设置缓存策略
3>生成request
4>配置request 相关参数
5>生成 SDWebImageDownloaderOperation
6>配置账号和密码
7>将operation 增加到queue中
8>下载操作看怎么进出的
发起网络请求就是这么简单。那我们看看下载完成进行啥操作了吧
那网络请求发出去了。那么看数据回来怎么操作的
看SDWebImageDownloaderOperation 函数数据回调
- (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]; 将数据追加到self.imageData;
第二步:判断self.options & SDWebImageDownloaderProgressiveDownload && self.expectedSize>0是不是YES
是的话,执行下面操作。self.expectedSize 是在响应头获取的
第三步:生成CGImageSourceRef 根据self.imageData
第四步 : 根据width + Height == 0 获取图片的width Height 还有orientation
第五步: 要是 宽高有值了。 生成CGImageRef
第六步 要是生成了 CGImageRef 那就生成bitMap的bmContext
第七步 : 如果生成的bmContext 不是nil 就用bmContext 生成 partialImageRef image
第八步:要是 partialImageRef 转换成UIImage
第九步:对图片处理并返回
最后一步:要是有进度条。那就执行进度条
其实接受数据部分就是对不完整数据进行生成图片加载。
当数据都接受完成的时候
- (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];
}
第一步:发通知
第二步:判断请求是否错误 error 就调用error回调
第三步:如果有完成回调 那么执行下面操作
第四步:要是self.imageData 有数据,那么就把imageData 生成image 根据响应的option 对image进行操作
看到这里没看见缓存,那么继续找缓存在哪里。
其实在上面分析的block中已经完成缓存分析了。只有由于跳转太多,并且不是严格按照请求image 来分析的,所以看着有点乱,无头无需的。没关系。学习么?那那么容易一下看懂。那我们下篇就先分析分析我们请求过程中使用的每个类的作用