...
|
...
|
@@ -20,6 +20,7 @@ NSString *const kCompletedCallbackKey = @"completed"; |
|
|
|
|
|
@property (strong, nonatomic) NSOperationQueue *downloadQueue;
|
|
|
@property (strong, nonatomic) NSMutableDictionary *URLCallbacks;
|
|
|
@property (assign, nonatomic) dispatch_queue_t barrierQueue;
|
|
|
|
|
|
@end
|
|
|
|
...
|
...
|
@@ -65,6 +66,7 @@ NSString *const kCompletedCallbackKey = @"completed"; |
|
|
_downloadQueue = NSOperationQueue.new;
|
|
|
_downloadQueue.maxConcurrentOperationCount = 10;
|
|
|
_URLCallbacks = NSMutableDictionary.new;
|
|
|
_barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT);
|
|
|
}
|
|
|
return self;
|
|
|
}
|
...
|
...
|
@@ -82,71 +84,85 @@ NSString *const kCompletedCallbackKey = @"completed"; |
|
|
- (NSOperation *)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(void (^)(NSUInteger, long long))progressBlock completed:(void (^)(UIImage *, NSError *, BOOL))completedBlock
|
|
|
{
|
|
|
__block SDWebImageDownloaderOperation *operation;
|
|
|
__weak SDWebImageDownloader *wself = self;
|
|
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^ // NSDictionary isn't thread safe
|
|
|
[self addProgressCallback:progressBlock andCompletedBlock:completedBlock forURL:url createCallback:^
|
|
|
{
|
|
|
BOOL performDownload = NO;
|
|
|
// In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests
|
|
|
NSMutableURLRequest *request = [NSMutableURLRequest.alloc initWithURL:url cachePolicy:NSURLCacheStorageNotAllowed timeoutInterval:15];
|
|
|
request.HTTPShouldHandleCookies = NO;
|
|
|
request.HTTPShouldUsePipelining = YES;
|
|
|
[request addValue:@"image/*" forHTTPHeaderField:@"Accept"];
|
|
|
operation = [SDWebImageDownloaderOperation.alloc initWithRequest:request options:options progress:^(NSUInteger receivedSize, long long expectedSize)
|
|
|
{
|
|
|
if (!wself) return;
|
|
|
SDWebImageDownloader *sself = wself;
|
|
|
NSArray *callbacksForURL = [sself removeAndReturnCallbacksForURL:url];
|
|
|
for (NSDictionary *callbacks in callbacksForURL)
|
|
|
{
|
|
|
SDWebImageDownloaderProgressBlock callback = callbacks[kProgressCallbackKey];
|
|
|
if (callback) callback(receivedSize, expectedSize);
|
|
|
}
|
|
|
}
|
|
|
completed:^(UIImage *image, NSError *error, BOOL finished)
|
|
|
{
|
|
|
if (!wself) return;
|
|
|
SDWebImageDownloader *sself = wself;
|
|
|
NSArray *callbacksForURL = [sself removeAndReturnCallbacksForURL:url];
|
|
|
for (NSDictionary *callbacks in callbacksForURL)
|
|
|
{
|
|
|
SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey];
|
|
|
if (callback) callback(image, error, finished);
|
|
|
}
|
|
|
}
|
|
|
cancelled:^
|
|
|
{
|
|
|
if (!wself) return;
|
|
|
SDWebImageDownloader *sself = wself;
|
|
|
[sself removeAndReturnCallbacksForURL:url];
|
|
|
}];
|
|
|
[self.downloadQueue addOperation:operation];
|
|
|
}];
|
|
|
|
|
|
|
|
|
return operation;
|
|
|
}
|
|
|
|
|
|
- (void)addProgressCallback:(void (^)(NSUInteger, long long))progressBlock andCompletedBlock:(void (^)(UIImage *, NSError *, BOOL))completedBlock forURL:(NSURL *)url createCallback:(void (^)())createCallback
|
|
|
{
|
|
|
dispatch_barrier_async(self.barrierQueue, ^
|
|
|
{
|
|
|
BOOL first = NO;
|
|
|
if (!self.URLCallbacks[url])
|
|
|
{
|
|
|
self.URLCallbacks[url] = NSMutableArray.new;
|
|
|
performDownload = YES;
|
|
|
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;
|
|
|
if (completedBlock) callbacks[kCompletedCallbackKey] = completedBlock;
|
|
|
[callbacksForURL addObject:callbacks];
|
|
|
self.URLCallbacks[url] = callbacksForURL;
|
|
|
|
|
|
if (first)
|
|
|
{
|
|
|
NSMutableArray *callbacksForURL = self.URLCallbacks[url];
|
|
|
NSMutableDictionary *callbacks = NSMutableDictionary.new;
|
|
|
if (progressBlock) callbacks[kProgressCallbackKey] = progressBlock;
|
|
|
if (completedBlock) callbacks[kCompletedCallbackKey] = completedBlock;
|
|
|
[callbacksForURL addObject:callbacks];
|
|
|
self.URLCallbacks[url] = callbacksForURL;
|
|
|
}
|
|
|
|
|
|
if (performDownload)
|
|
|
{
|
|
|
// In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests
|
|
|
NSMutableURLRequest *request = [NSMutableURLRequest.alloc initWithURL:url cachePolicy:NSURLCacheStorageNotAllowed timeoutInterval:15];
|
|
|
request.HTTPShouldHandleCookies = NO;
|
|
|
request.HTTPShouldUsePipelining = YES;
|
|
|
[request addValue:@"image/*" forHTTPHeaderField:@"Accept"];
|
|
|
operation = [SDWebImageDownloaderOperation.alloc initWithRequest:request options:options progress:^(NSUInteger receivedSize, long long expectedSize)
|
|
|
{
|
|
|
dispatch_async(dispatch_get_main_queue(), ^
|
|
|
{
|
|
|
NSMutableArray *callbacksForURL = self.URLCallbacks[url];
|
|
|
for (NSDictionary *callbacks in callbacksForURL)
|
|
|
{
|
|
|
SDWebImageDownloaderProgressBlock callback = callbacks[kProgressCallbackKey];
|
|
|
if (callback) callback(receivedSize, expectedSize);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
completed:^(UIImage *image, NSError *error, BOOL finished)
|
|
|
{
|
|
|
dispatch_async(dispatch_get_main_queue(), ^
|
|
|
{
|
|
|
NSMutableArray *callbacksForURL = self.URLCallbacks[url];
|
|
|
[self.URLCallbacks removeObjectForKey:url];
|
|
|
for (NSDictionary *callbacks in callbacksForURL)
|
|
|
{
|
|
|
SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey];
|
|
|
if (callback) callback(image, error, finished);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
cancelled:^
|
|
|
{
|
|
|
dispatch_async(dispatch_get_main_queue(), ^
|
|
|
{
|
|
|
[self.URLCallbacks removeObjectForKey:url];
|
|
|
});
|
|
|
}];
|
|
|
[self.downloadQueue addOperation:operation];
|
|
|
createCallback();
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
return operation;
|
|
|
- (NSArray *)removeAndReturnCallbacksForURL:(NSURL *)url
|
|
|
{
|
|
|
__block NSArray *callbacksForURL;
|
|
|
dispatch_barrier_sync(self.barrierQueue, ^
|
|
|
{
|
|
|
callbacksForURL = self.URLCallbacks[url];
|
|
|
[self.URLCallbacks removeObjectForKey:url];
|
|
|
});
|
|
|
return callbacksForURL;
|
|
|
}
|
|
|
|
|
|
@end |
...
|
...
|
|