1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
| /* * 如果URL对应的图像在缓存中不存在,那么就下载指定的图片 ,否则返回缓存的图像 * * @param url 图片的URL地址 * @param options 指定此次请求策略的选项 * @param progressBlock 图片下载进度的回调 * @param completedBlock 操作完成后的回调 * 此参数是必须的,此block没有返回值 * Image:请求的 UIImage,如果出现错误,image参数是nil * error:如果出现错误,则error有值 * cacheType:`SDImageCacheType` 枚举,标示该图像的加载方式 * SDImageCacheTypeNone:从网络下载 * SDImageCacheTypeDisk:从本地缓存加载 * SDImageCacheTypeMemory:从内存缓存加载 * finished:如果图像下载完成则为YES,如果使用 SDWebImageProgressiveDownload 选项,同时只获取到部分图片时,返回 NO * imageURL:图片的URL地址 * * @return SDWebImageOperation对象,应该是SDWebimageDownloaderOperation实例 */ - (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionWithFinishedBlock)completedBlock { // Invoking this method without a completedBlock is pointless //没有completedblock,那么调用这个方法是毫无意义的 NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");
// Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, XCode won't // throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString. //检查用户传入的URL是否正确,如果该URL是NSString类型的,那么尝试转换 if ([url isKindOfClass:NSString.class]) { url = [NSURL URLWithString:(NSString *)url]; }
// Prevents app crashing on argument type error like sending NSNull instead of NSURL //防止因参数类型错误而导致应用程序崩溃,判断URL是否是NSURL类型的,如果不是则直接设置为nil if (![url isKindOfClass:NSURL.class]) { url = nil; }
//初始化一个SDWebImageCombinedOperationBlock块 __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new]; __weak SDWebImageCombinedOperation *weakOperation = operation;
BOOL isFailedUrl = NO; //初始化设定该URL是正确的
//加互斥锁,检索请求图片的URL是否在曾下载失败的集合中(URL黑名单) @synchronized (self.failedURLs) { isFailedUrl = [self.failedURLs containsObject:url]; }
//如果url不正确或者 选择的下载策略不是『下载失败尝试重新下载』且该URL存在于黑名单中,那么直接返回,回调任务完成block块,传递错误信息 if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
//该宏保证了completedBlock回调在主线程中执行 dispatch_main_sync_safe(^{ NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]; completedBlock(nil, error, SDImageCacheTypeNone, YES, url); }); return operation; }
//加互斥锁,把当前的下载任务添加到『当前正在执行任务数组』中 @synchronized (self.runningOperations) { [self.runningOperations addObject:operation]; } //得到该URL对应的缓存KEY NSString *key = [self cacheKeyForURL:url];
//该方法查找URLKEY对应的图片缓存是否存在,查找完毕之后把该图片(存在|不存在)和该图片的缓存方法以block的方式传递 //缓存情况查找完毕之后,在block块中进行后续处理(如果该图片没有缓存·下载|如果缓存存在|如果用户设置了下载的缓存策略是刷新缓存如何处理等等) operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) { //先判断该下载操作是否已经被取消,如果被取消则把当前操作从runningOperations数组中移除,并直接返回 if (operation.isCancelled) { @synchronized (self.runningOperations) { [self.runningOperations removeObject:operation]; }
return; }
//(图片不存在||下载策略为刷新缓存)且(shouldDownloadImageForURL不能响应||该图片存在缓存) if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) { //从此处开始,一直在处理downloaderOptions(即下载策略) if (image && options & SDWebImageRefreshCached) { //如果图像存在,但是下载策略为刷新缓存,则通知缓存图像并尝试重新下载 dispatch_main_sync_safe(^{ // 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. completedBlock(image, nil, cacheType, YES, url); }); } // download if no image or requested to refresh anyway, and download allowed by delegate SDWebImageDownloaderOptions downloaderOptions = 0; //如果下载策略为SDWebImageLowPriority 那么downloaderOptions = 其本身 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 (image && 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 //忽略从NSURLCache读取图片 downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse; } //到此处位置,downloaderOptions(即下载策略)处理操作结束
//核心方法:使用下载器,下载图片 id <SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) { if (weakOperation.isCancelled) { //如果此时操作被取消,那么什么也不做 // Do nothing if the operation was cancelled // See #699 for more details // if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data } else if (error) { //如果下载失败,则处理结束的回调,在合适的情况下把对应图片的URL添加到黑名单中 dispatch_main_sync_safe(^{ if (!weakOperation.isCancelled) { completedBlock(nil, error, SDImageCacheTypeNone, finished, 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 {//下载成功 //先判断当前的下载策略是否是SDWebImageRetryFailed,如果是那么把该URL从黑名单中删除 if ((options & SDWebImageRetryFailed)) { @synchronized (self.failedURLs) { [self.failedURLs removeObject:url]; } }
//是否要进行磁盘缓存? BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);
//如果下载策略为SDWebImageRefreshCached且该图片缓存中存在且未下载下来,那么什么都不做 if (options & SDWebImageRefreshCached && image && !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:)]) { //否则,如果下载图片存在且(不是可动画图片数组||下载策略为SDWebImageTransformAnimatedImage&&transformDownloadedImage方法可用) //开子线程处理 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]; [self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : data) forKey:key toDisk:cacheOnDisk]; }
//在主线程中回调completedBlock dispatch_main_sync_safe(^{ if (!weakOperation.isCancelled) { completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url); } }); }); } else { //得到下载的图片且已经完成,则进行缓存处理 if (downloadedImage && finished) { [self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk]; }
dispatch_main_sync_safe(^{ if (!weakOperation.isCancelled) { completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url); } }); } }
if (finished) { @synchronized (self.runningOperations) { [self.runningOperations removeObject:operation]; } } }];
//处理cancelBlock operation.cancelBlock = ^{ [subOperation cancel];
@synchronized (self.runningOperations) { [self.runningOperations removeObject:weakOperation]; } }; } else if (image) { //如果图片存在,且操作没有被取消,那么在主线程中回调completedBlock,并把当前操作移除 dispatch_main_sync_safe(^{ if (!weakOperation.isCancelled) { completedBlock(image, nil, cacheType, YES, url); } }); @synchronized (self.runningOperations) { [self.runningOperations removeObject:operation]; } } else { // Image not in cache and download disallowed by delegate //图片不存在缓存且不允许代理下载,那么在主线程中回调completedBlock,并把当前操作移除 dispatch_main_sync_safe(^{ if (!weakOperation.isCancelled) { completedBlock(nil, nil, SDImageCacheTypeNone, YES, url); } }); @synchronized (self.runningOperations) { [self.runningOperations removeObject:operation]; } } }];
return operation; }
|