SDWebImageManager -- 单例
1 - (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {
1.1 id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:
^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL)
1.2 [self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];
SDWebImageManager 1 - (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url 2 options:(SDWebImageOptions)options 3 progress:(SDWebImageDownloaderProgressBlock)progressBlock 4 completed:(SDWebImageCompletionWithFinishedBlock)completedBlock { 25 __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new]; 41 @synchronized (self.runningOperations) { 42 [self.runningOperations addObject:operation]; 43 } 44 NSString *key = [self cacheKeyForURL:url]; 45 46 operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) { 47 if (operation.isCancelled) { 49 [self.runningOperations removeObject:operation]; 52 return; 53 } 55 if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) { 56 if (image && options & SDWebImageRefreshCached) { 57 dispatch_main_sync_safe(^{ 58 // If image was found in the cache bug SDWebImageRefreshCached is provided, notify about the cached image 59 // AND try to re-download it in order to let a chance to NSURLCache to refresh it from server. 60 completedBlock(image, nil, cacheType, YES, url); 61 }); 62 } 79 id <SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) { 80 if (weakOperation.isCancelled) { 81 // Do nothing if the operation was cancelled 82 // See #699 for more details 83 // 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 84 } 85 else if (error) { 88 completedBlock(nil, error, SDImageCacheTypeNone, finished, url); 94 [self.failedURLs addObject:url]; 97 } 98 else { 99 BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly); 101 if (options & SDWebImageRefreshCached && image && !downloadedImage) { 102 // Image refresh hit the NSURLCache cache, do not call the completion block 103 } 104 // NOTE: We don‘t call transformDownloadedImage delegate method on animated images as most transformation code would mangle it 105 else if (downloadedImage && !downloadedImage.images && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) { 106 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 107 UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url]; 109 if (transformedImage && finished) { 110 BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage]; 111 [self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:data forKey:key toDisk:cacheOnDisk]; 112 }116 completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url);119 }); 120 } 121 else { 122 if (downloadedImage && finished) { 123 [self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk]; 124 }128 completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url);131 } 132 } 134 if (finished) {136 [self.runningOperations removeObject:operation];138 } 139 }]; 140 operation.cancelBlock = ^{ 141 [subOperation cancel];144 [self.runningOperations removeObject:weakOperation];146 }; 147 } 148 else if (image) {151 completedBlock(image, nil, cacheType, YES, url);155 [self.runningOperations removeObject:operation];157 } 158 else { 159 // Image not in cache and download disallowed by delegate162 completedBlock(nil, nil, SDImageCacheTypeNone, YES, url);166 [self.runningOperations removeObject:operation];168 } 169 }]; 170 171 return operation; 172 }
SDImageCache 1 - (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock { 11 // First check the in-memory cache... 12 UIImage *image = [self imageFromMemoryCacheForKey:key]; 13 if (image) { 14 doneBlock(image, SDImageCacheTypeMemory); 15 return nil; 16 } 17 18 NSOperation *operation = [NSOperation new]; 19 dispatch_async(self.ioQueue, ^{ 20 if (operation.isCancelled) { 21 return; 22 } 23 24 @autoreleasepool { 25 UIImage *diskImage = [self diskImageForKey:key]; 26 if (diskImage) { 27 CGFloat cost = diskImage.size.height * diskImage.size.width * diskImage.scale; 28 [self.memCache setObject:diskImage forKey:key cost:cost]; 29 } 30 31 dispatch_async(dispatch_get_main_queue(), ^{ 32 doneBlock(diskImage, SDImageCacheTypeDisk); 33 }); 34 } 35 }); 36 37 return operation; 38 }
SDWebImageDownloader - (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock { __block SDWebImageDownloaderOperation *operation; [self addProgressCallback:progressBlock andCompletedBlock:completedBlock forURL:url createCallback:^{ timeoutInterval = 15.0; // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData)
timeoutInterval:timeoutInterval]; operation = [[SDWebImageDownloaderOperation alloc] initWithRequest:request options:options progress:^(NSInteger receivedSize, NSInteger expectedSize) { SDWebImageDownloaderProgressBlock callback = callbacks[kProgressCallbackKey]; if (callback) callback(receivedSize, expectedSize); } completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey]; if (callback) callback(image, data, error, finished); } cancelled:^{ [sself removeCallbacksForURL:url]; }]; [wself.downloadQueue addOperation:operation]; [wself.lastAddedOperation addDependency:operation]; }]; return operation; }
- (void)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock andCompletedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock forURL:(NSURL *)url createCallback:(SDWebImageNoParamsBlock)createCallback { dispatch_barrier_sync(self.barrierQueue, ^{ BOOL first = NO; if (!self.URLCallbacks[url]) { self.URLCallbacks[url] = [NSMutableArray new];//第一次请求该url,新建URLCallbacks数组 first = YES;//标示是第一次 } // Handle single download of simultaneous download request for the same URL NSMutableArray *callbacksForURL = self.URLCallbacks[url]; NSMutableDictionary *callbacks = [NSMutableDictionary new]; if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];//保存progressBlock if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];//保存completedBlock [callbacksForURL addObject:callbacks];//保存至URLCallbacks self.URLCallbacks[url] = callbacksForURL;保存至self.URLCallbacks if (first) { createCallback();//第一次请求该url,执行createCallback } });}