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