SDWebImage源码探究(三)
详解:
- 入口
UIImageView+WebCache.m
//这个是入口,下载图片的结果方法最终都是走的这个方法 - (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDExternalCompletionBlock)completedBlock { [self sd_internalSetImageWithURL:url placeholderImage:placeholder options:options operationKey:nil setImageBlock:nil progress:progressBlock completed:completedBlock]; }
- UIView的分类
UIView+WebCache.m
- (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 { NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]); [self sd_cancelImageLoadOperationWithKey:validOperationKey]; objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); if (!(options & SDWebImageDelayPlaceholder)) { dispatch_main_async_safe(^{ [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock]; }); } //如果URL不为空,调用管理器SDWebImageManager if (url) { //加载指示器 if ([self sd_showActivityIndicatorView]) { [self sd_addActivityIndicator]; } __weak __typeof(self)wself = self; //调用管理器SDWebImageManager id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { //下面的是调用管理器完成的回调 __strong __typeof (wself) sself = wself; [sself sd_removeActivityIndicator]; if (!sself) { return; } dispatch_main_async_safe(^{ if (!sself) { return; } if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) { completedBlock(image, error, cacheType, url); return; } else if (image) { [sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock]; [sself sd_setNeedsLayout]; } else { if ((options & SDWebImageDelayPlaceholder)) { [sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock]; [sself sd_setNeedsLayout]; } } if (completedBlock && finished) { completedBlock(image, error, cacheType, url); } }); }]; //取消下载操作 [self sd_setImageLoadOperation:operation forKey:validOperationKey]; } //如果url为空就抛出异常 else { dispatch_main_async_safe(^{ //移除指示器 [self sd_removeActivityIndicator]; if (completedBlock) { //完成以后的回调 NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}]; completedBlock(nil, error, SDImageCacheTypeNone, url); } }); } }
- 调用管理类 SDWebImageManager
SDWebImageManager.m
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url options:(SDWebImageOptions)options progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDInternalCompletionBlock)completedBlock { //断言,判断completedBlock是否为空 NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead"); //这里是一个容错机制,及时我们给url传递的NSString而不是NSURL,框架也会给我们自动转化 if ([url isKindOfClass:NSString.class]) { url = [NSURL URLWithString:(NSString *)url]; } //防崩溃处理,如果不是NSString和NSURL类型,就赋值为nil,防止app崩溃 if (![url isKindOfClass:NSURL.class]) { url = nil; } __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new]; __weak SDWebImageCombinedOperation *weakOperation = operation; BOOL isFailedUrl = NO; if (url) { //这里的是failedURLs是NSMutableSet集合 @synchronized (self.failedURLs) { //判断集合中是否包含这个url isFailedUrl = [self.failedURLs containsObject:url]; } } if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) { //调用操作完成模块 [self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url]; return operation; } @synchronized (self.runningOperations) { //如果操作operation不为空,就讲该操作放在可变数组runningOperations里面 [self.runningOperations addObject:operation]; } //获取urlKey作为操作的key NSString *key = [self cacheKeyForURL:url]; //查询该操作是否在缓存操作中 operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) { //如果缓存中有这个操作,就移除这个operation if (operation.isCancelled) { [self safelyRemoveOperationFromRunning:operation]; return; } //如果缓存中没有图片,就调用先判断是否可以下载 if ((!cachedImage || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) { if (cachedImage && options & SDWebImageRefreshCached) { //如果缓存中有图片,设置了SDWebImageRefreshCached,就会重新下载,NSURLCache从服务器重新下载 [self callCompletionBlockForOperation:weakOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url]; } //如果没有图片,利用代理进行下载,下面是下载选项的枚举 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) { downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload; downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse; } //下载图片并给出回调结果 SDWebImageDownloadToken *subOperationToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) { __strong __typeof(weakOperation) strongOperation = weakOperation; if (!strongOperation || strongOperation.isCancelled) { //如果操作不存在或者操作被取消,就什么都不做,如果我们调用完成block,对于同一个对象,在这个block和其他的完成block之间就会产生竞争状态,因此如果这个被二次调用,我们就会覆盖新数据。 } else if (error) { [self callCompletionBlockForOperation:strongOperation completion:completedBlock error:error url:url]; //错误码处理 if ( error.code != NSURLErrorNotConnectedToInternet && error.code != NSURLErrorCancelled && error.code != NSURLErrorTimedOut && error.code != NSURLErrorInternationalRoamingOff && error.code != NSURLErrorDataNotAllowed && error.code != NSURLErrorCannotFindHost && error.code != NSURLErrorCannotConnectToHost) { @synchronized (self.failedURLs) { [self.failedURLs addObject:url]; } } } else { //如果下载失败的话,就将这个url从集合set中移除 if ((options & SDWebImageRetryFailed)) { @synchronized (self.failedURLs) { [self.failedURLs removeObject:url]; } } BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly); if (options & SDWebImageRefreshCached && cachedImage && !downloadedImage) { // Image refresh hit the NSURLCache cache, do not call the completion block } else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url]; if (transformedImage && finished) { BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage]; // pass nil if the image was transformed, so we can recalculate the data from the image [self.imageCache storeImage:transformedImage imageData:(imageWasTransformed ? nil : downloadedData) forKey:key toDisk:cacheOnDisk completion:nil]; } [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url]; }); } else { if (downloadedImage && finished) { [self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key toDisk:cacheOnDisk completion:nil]; } [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:downloadedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url]; } } if (finished) { //完成后才操作集合中移除该操作 [self safelyRemoveOperationFromRunning:strongOperation]; } }]; //如果操作取消了,同样从操作中移除该操作 operation.cancelBlock = ^{ [self.imageDownloader cancel:subOperationToken]; __strong __typeof(weakOperation) strongOperation = weakOperation; [self safelyRemoveOperationFromRunning:strongOperation]; }; } //如果有缓存图片 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]; } else { //缓存中没有图片,也不用代理下载,并移除操作 __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]; } }]; return operation; }
- 下载前,先检查缓存 SSImageCache
SDImageCache.m
//查询是否缓存过该操作 - (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock { //不存在这个key,就调用完成block if (!key) { if (doneBlock) { doneBlock(nil, nil, SDImageCacheTypeNone); } return nil; } // 先检查内存是否存在该图片 UIImage *image = [self imageFromMemoryCacheForKey:key]; if (image) { NSData *diskData = nil; if ([image isGIF]) {//gif diskData = [self diskImageDataBySearchingAllPathsForKey:key]; } if (doneBlock) { doneBlock(image, diskData, SDImageCacheTypeMemory); } return nil; } //实例化一个新操作 NSOperation *operation = [NSOperation new]; dispatch_async(self.ioQueue, ^{ if (operation.isCancelled) { //取消操作就返回 return; } @autoreleasepool { NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key]; UIImage *diskImage = [self diskImageForKey:key]; //如果盘中存在图片,并且如果可以使用缓存 if (diskImage && self.config.shouldCacheImagesInMemory) { //图片的尺寸 NSUInteger cost = SDCacheCostForImage(diskImage); //将硬盘中的图片在缓存中存一份 [self.memCache setObject:diskImage forKey:key cost:cost]; } if (doneBlock) { dispatch_async(dispatch_get_main_queue(), ^{ //完成的回调,返回图片,数据以及图片的获取途径 // typedef NS_ENUM(NSInteger, SDImageCacheType) { // //缓存中不存在图片,要从网上下载 // SDImageCacheTypeNone, // //图片缓存在磁盘 // SDImageCacheTypeDisk, // 图片缓存在内存 // SDImageCacheTypeMemory // }; doneBlock(diskImage, diskData, SDImageCacheTypeDisk); }); } } }); return operation; }
- 如果缓存中没有,就下载图片 SDWebImageDownloader
SDWebImageDownloader.m
//下面就是下载图片的方法了 - (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; //设置超时15s NSTimeInterval timeoutInterval = sself.downloadTimeout; if (timeoutInterval == 0.0) { timeoutInterval = 15.0; } // NSURLCache + SDImageCache)防止重复缓存,开始请求图片 NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval]; request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies); request.HTTPShouldUsePipelining = YES; //过滤HTTP请求,该字典用来HTTP请求的header 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; //设置默认的 URL credential 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; } //将操作放在NSOperationQueue中 [sself.downloadQueue addOperation:operation]; //设置操作的执行顺序 //typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) { //队列模式 先进先出,这个是默认值 //SDWebImageDownloaderFIFOExecutionOrder, //堆栈模式,后进先出 //SDWebImageDownloaderLIFOExecutionOrder //}; if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) { //添加操作的依赖关系,这里利用lastAddedOperation记录操作 [sself.lastAddedOperation addDependency:operation]; sself.lastAddedOperation = operation; } return operation; }]; }
- 上面的SDWebImageDownloadToken 是从下面这个方法获取的
SDWebImageDownloader.m
//加入进度回调,返回SDWebImageDownloadToken对象 - (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock forURL:(nullable NSURL *)url createCallback:(SDWebImageDownloaderOperation *(^)())createCallback { //如果URL用于回调字典的key,不能为空,如果为空,就会立即调用完成block,不带有图片和数据 if (url == nil) { if (completedBlock != nil) { completedBlock(nil, nil, nil, NO); } return nil; } __block SDWebImageDownloadToken *token = nil; //加入GCD阻塞 dispatch_barrier_sync(self.barrierQueue, ^{ //根据url作为key返回操作 SDWebImageDownloaderOperation *operation = self.URLOperations[url]; if (!operation) { operation = createCallback(); self.URLOperations[url] = operation; __weak SDWebImageDownloaderOperation *woperation = operation; operation.completionBlock = ^{ SDWebImageDownloaderOperation *soperation = woperation; if (!soperation) return; //如果这次加入的操作已经存在,就移除该操作 if (self.URLOperations[url] == soperation) { [self.URLOperations removeObjectForKey:url]; }; }; } id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock]; //重新初始化SDWebImageDownloadToken对象 token = [SDWebImageDownloadToken new]; token.url = url; token.downloadOperationCancelToken = downloadOperationCancelToken; }); return token; }
- 下载完成后就对图片解码
#import <Foundation/Foundation.h> #import "SDWebImageCompat.h" @interface UIImage (ForceDecode) + (nullable UIImage *)decodedImageWithImage:(nullable UIImage *)image; + (nullable UIImage *)decodedAndScaledDownImageWithImage:(nullable UIImage *)image; @end