SDWebImage源码分析

1 - (NSInteger)maxConcurrentDownloads {
2     return _downloadQueue.maxConcurrentOperationCount;
3 }

 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 delegate
162 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
}
});
}
 

 

原文地址:https://www.cnblogs.com/so-magic/p/4748729.html