Merge pull request #1586 from oanapopescu/nsurlsession
Updated image downloader with a single session that manages all tasks
This commit is contained in:
commit
5d42a2ac5e
|
@ -13,7 +13,7 @@
|
|||
static NSString *const kProgressCallbackKey = @"progress";
|
||||
static NSString *const kCompletedCallbackKey = @"completed";
|
||||
|
||||
@interface SDWebImageDownloader ()
|
||||
@interface SDWebImageDownloader () <NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
|
||||
|
||||
@property (strong, nonatomic) NSOperationQueue *downloadQueue;
|
||||
@property (weak, nonatomic) NSOperation *lastAddedOperation;
|
||||
|
@ -23,6 +23,9 @@ static NSString *const kCompletedCallbackKey = @"completed";
|
|||
// This queue is used to serialize the handling of the network responses of all the download operation in a single queue
|
||||
@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t barrierQueue;
|
||||
|
||||
// The session in which data tasks will run
|
||||
@property (strong, nonatomic) NSURLSession *session;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SDWebImageDownloader
|
||||
|
@ -74,11 +77,26 @@ static NSString *const kCompletedCallbackKey = @"completed";
|
|||
#endif
|
||||
_barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT);
|
||||
_downloadTimeout = 15.0;
|
||||
|
||||
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
sessionConfig.timeoutIntervalForRequest = _downloadTimeout;
|
||||
|
||||
/**
|
||||
* Create the session for this task
|
||||
* We send nil as delegate queue so that the session creates a serial operation queue for performing all delegate
|
||||
* method calls and completion handler calls.
|
||||
*/
|
||||
self.session = [NSURLSession sessionWithConfiguration:sessionConfig
|
||||
delegate:self
|
||||
delegateQueue:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self.session invalidateAndCancel];
|
||||
self.session = nil;
|
||||
|
||||
[self.downloadQueue cancelAllOperations];
|
||||
SDDispatchQueueRelease(_barrierQueue);
|
||||
}
|
||||
|
@ -133,6 +151,7 @@ static NSString *const kCompletedCallbackKey = @"completed";
|
|||
request.allHTTPHeaderFields = wself.HTTPHeaders;
|
||||
}
|
||||
operation = [[wself.operationClass alloc] initWithRequest:request
|
||||
inSession:self.session
|
||||
options:options
|
||||
progress:^(NSInteger receivedSize, NSInteger expectedSize) {
|
||||
SDWebImageDownloader *sself = wself;
|
||||
|
@ -233,4 +252,66 @@ static NSString *const kCompletedCallbackKey = @"completed";
|
|||
[self.downloadQueue cancelAllOperations];
|
||||
}
|
||||
|
||||
#pragma mark Helper methods
|
||||
|
||||
- (SDWebImageDownloaderOperation *)operationWithTask:(NSURLSessionTask *)task {
|
||||
SDWebImageDownloaderOperation *returnOperation = nil;
|
||||
for (SDWebImageDownloaderOperation *operation in self.downloadQueue.operations) {
|
||||
if (operation.dataTask.taskIdentifier == task.taskIdentifier) {
|
||||
returnOperation = operation;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return returnOperation;
|
||||
}
|
||||
|
||||
#pragma mark NSURLSessionDataDelegate
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
dataTask:(NSURLSessionDataTask *)dataTask
|
||||
didReceiveResponse:(NSURLResponse *)response
|
||||
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
|
||||
|
||||
// Identify the operation that runs this task and pass it the delegate method
|
||||
SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask];
|
||||
|
||||
[dataOperation URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
|
||||
|
||||
// Identify the operation that runs this task and pass it the delegate method
|
||||
SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask];
|
||||
|
||||
[dataOperation URLSession:session dataTask:dataTask didReceiveData:data];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
dataTask:(NSURLSessionDataTask *)dataTask
|
||||
willCacheResponse:(NSCachedURLResponse *)proposedResponse
|
||||
completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler {
|
||||
|
||||
// Identify the operation that runs this task and pass it the delegate method
|
||||
SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask];
|
||||
|
||||
[dataOperation URLSession:session dataTask:dataTask willCacheResponse:proposedResponse completionHandler:completionHandler];
|
||||
}
|
||||
|
||||
#pragma mark NSURLSessionTaskDelegate
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
|
||||
// Identify the operation that runs this task and pass it the delegate method
|
||||
SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:task];
|
||||
|
||||
[dataOperation URLSession:session task:task didCompleteWithError:error];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
|
||||
|
||||
// Identify the operation that runs this task and pass it the delegate method
|
||||
SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:task];
|
||||
|
||||
[dataOperation URLSession:session task:task didReceiveChallenge:challenge completionHandler:completionHandler];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -15,13 +15,18 @@ extern NSString *const SDWebImageDownloadReceiveResponseNotification;
|
|||
extern NSString *const SDWebImageDownloadStopNotification;
|
||||
extern NSString *const SDWebImageDownloadFinishNotification;
|
||||
|
||||
@interface SDWebImageDownloaderOperation : NSOperation <SDWebImageOperation>
|
||||
@interface SDWebImageDownloaderOperation : NSOperation <SDWebImageOperation, NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
|
||||
|
||||
/**
|
||||
* The request used by the operation's connection.
|
||||
* The request used by the operation's task.
|
||||
*/
|
||||
@property (strong, nonatomic, readonly) NSURLRequest *request;
|
||||
|
||||
/**
|
||||
* The operation's task
|
||||
*/
|
||||
@property (strong, nonatomic, readonly) NSURLSessionTask *dataTask;
|
||||
|
||||
|
||||
@property (assign, nonatomic) BOOL shouldDecompressImages;
|
||||
|
||||
|
@ -53,6 +58,7 @@ extern NSString *const SDWebImageDownloadFinishNotification;
|
|||
* @see SDWebImageDownloaderOperation
|
||||
*
|
||||
* @param request the URL request
|
||||
* @param session the URL session in which this operation will run
|
||||
* @param options downloader options
|
||||
* @param progressBlock the block executed when a new chunk of data arrives.
|
||||
* @note the progress block is executed on a background queue
|
||||
|
@ -63,9 +69,32 @@ extern NSString *const SDWebImageDownloadFinishNotification;
|
|||
* @return the initialized instance
|
||||
*/
|
||||
- (id)initWithRequest:(NSURLRequest *)request
|
||||
inSession:(NSURLSession *)session
|
||||
options:(SDWebImageDownloaderOptions)options
|
||||
progress:(SDWebImageDownloaderProgressBlock)progressBlock
|
||||
completed:(SDWebImageDownloaderCompletedBlock)completedBlock
|
||||
cancelled:(SDWebImageNoParamsBlock)cancelBlock;
|
||||
|
||||
/**
|
||||
* Initializes a `SDWebImageDownloaderOperation` object
|
||||
*
|
||||
* @see SDWebImageDownloaderOperation
|
||||
*
|
||||
* @param request the URL request
|
||||
* @param options downloader options
|
||||
* @param progressBlock the block executed when a new chunk of data arrives.
|
||||
* @note the progress block is executed on a background queue
|
||||
* @param completedBlock the block executed when the download is done.
|
||||
* @note the completed block is executed on the main queue for success. If errors are found, there is a chance the block will be executed on a background queue
|
||||
* @param cancelBlock the block executed if the download (operation) is cancelled
|
||||
*
|
||||
* @return the initialized instance. The operation will run in a separate session created for this operation
|
||||
*/
|
||||
- (id)initWithRequest:(NSURLRequest *)request
|
||||
options:(SDWebImageDownloaderOptions)options
|
||||
progress:(SDWebImageDownloaderProgressBlock)progressBlock
|
||||
completed:(SDWebImageDownloaderCompletedBlock)completedBlock
|
||||
cancelled:(SDWebImageNoParamsBlock)cancelBlock
|
||||
__deprecated_msg("Method deprecated. Use `initWithRequest:inSession:options:progress:completed:cancelled`");
|
||||
|
||||
@end
|
||||
|
|
|
@ -17,7 +17,7 @@ NSString *const SDWebImageDownloadReceiveResponseNotification = @"SDWebImageDown
|
|||
NSString *const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNotification";
|
||||
NSString *const SDWebImageDownloadFinishNotification = @"SDWebImageDownloadFinishNotification";
|
||||
|
||||
@interface SDWebImageDownloaderOperation () <NSURLSessionTaskDelegate>
|
||||
@interface SDWebImageDownloaderOperation ()
|
||||
|
||||
@property (copy, nonatomic) SDWebImageDownloaderProgressBlock progressBlock;
|
||||
@property (copy, nonatomic) SDWebImageDownloaderCompletedBlock completedBlock;
|
||||
|
@ -27,8 +27,13 @@ NSString *const SDWebImageDownloadFinishNotification = @"SDWebImageDownloadFinis
|
|||
@property (assign, nonatomic, getter = isFinished) BOOL finished;
|
||||
@property (strong, nonatomic) NSMutableData *imageData;
|
||||
|
||||
@property (strong, nonatomic) NSURLSession *session;
|
||||
@property (strong, nonatomic) NSURLSessionDataTask *dataTask;
|
||||
// This is weak because it is injected by whoever manages this session. If this gets nil-ed out, we won't be able to run
|
||||
// the task associated with this operation
|
||||
@property (weak, nonatomic) NSURLSession *unownedSession;
|
||||
// This is set if we're using not using an injected NSURLSession. We're responsible of invalidating this one
|
||||
@property (strong, nonatomic) NSURLSession *ownedSession;
|
||||
|
||||
@property (strong, nonatomic, readwrite) NSURLSessionTask *dataTask;
|
||||
|
||||
@property (strong, atomic) NSThread *thread;
|
||||
|
||||
|
@ -52,6 +57,21 @@ NSString *const SDWebImageDownloadFinishNotification = @"SDWebImageDownloadFinis
|
|||
progress:(SDWebImageDownloaderProgressBlock)progressBlock
|
||||
completed:(SDWebImageDownloaderCompletedBlock)completedBlock
|
||||
cancelled:(SDWebImageNoParamsBlock)cancelBlock {
|
||||
|
||||
return [self initWithRequest:request
|
||||
inSession:nil
|
||||
options:options
|
||||
progress:progressBlock
|
||||
completed:completedBlock
|
||||
cancelled:cancelBlock];
|
||||
}
|
||||
|
||||
- (id)initWithRequest:(NSURLRequest *)request
|
||||
inSession:(NSURLSession *)session
|
||||
options:(SDWebImageDownloaderOptions)options
|
||||
progress:(SDWebImageDownloaderProgressBlock)progressBlock
|
||||
completed:(SDWebImageDownloaderCompletedBlock)completedBlock
|
||||
cancelled:(SDWebImageNoParamsBlock)cancelBlock {
|
||||
if ((self = [super init])) {
|
||||
_request = request;
|
||||
_shouldDecompressImages = YES;
|
||||
|
@ -62,6 +82,7 @@ NSString *const SDWebImageDownloadFinishNotification = @"SDWebImageDownloadFinis
|
|||
_executing = NO;
|
||||
_finished = NO;
|
||||
_expectedSize = 0;
|
||||
_unownedSession = session;
|
||||
responseFromCached = YES; // Initially wrong until `- URLSession:dataTask:willCacheResponse:completionHandler: is called or not called
|
||||
}
|
||||
return self;
|
||||
|
@ -93,20 +114,24 @@ NSString *const SDWebImageDownloadFinishNotification = @"SDWebImageDownloadFinis
|
|||
}];
|
||||
}
|
||||
#endif
|
||||
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
sessionConfig.timeoutIntervalForRequest = 15;
|
||||
|
||||
/**
|
||||
* Create the session for this task
|
||||
* We send nil as delegate queue so that the session creates a serial operation queue for performing all delegate
|
||||
* method calls and completion handler calls.
|
||||
*/
|
||||
self.session = [NSURLSession sessionWithConfiguration:sessionConfig
|
||||
delegate:self
|
||||
delegateQueue:nil];
|
||||
NSURLSession *session = self.unownedSession;
|
||||
if (!self.unownedSession) {
|
||||
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
sessionConfig.timeoutIntervalForRequest = 15;
|
||||
|
||||
/**
|
||||
* Create the session for this task
|
||||
* We send nil as delegate queue so that the session creates a serial operation queue for performing all delegate
|
||||
* method calls and completion handler calls.
|
||||
*/
|
||||
self.ownedSession = [NSURLSession sessionWithConfiguration:sessionConfig
|
||||
delegate:self
|
||||
delegateQueue:nil];
|
||||
session = self.ownedSession;
|
||||
}
|
||||
|
||||
self.dataTask = [session dataTaskWithRequest:self.request];
|
||||
self.executing = YES;
|
||||
self.dataTask = [self.session dataTaskWithRequest:self.request];
|
||||
self.thread = [NSThread currentThread];
|
||||
}
|
||||
|
||||
|
@ -188,8 +213,10 @@ NSString *const SDWebImageDownloadFinishNotification = @"SDWebImageDownloadFinis
|
|||
self.dataTask = nil;
|
||||
self.imageData = nil;
|
||||
self.thread = nil;
|
||||
[self.session invalidateAndCancel];
|
||||
self.session = nil;
|
||||
if (self.ownedSession) {
|
||||
[self.ownedSession invalidateAndCancel];
|
||||
self.ownedSession = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setFinished:(BOOL)finished {
|
||||
|
@ -208,7 +235,7 @@ NSString *const SDWebImageDownloadFinishNotification = @"SDWebImageDownloadFinis
|
|||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark NSURLSessionTaskDelegate
|
||||
#pragma mark NSURLSessionDataDelegate
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
dataTask:(NSURLSessionDataTask *)dataTask
|
||||
|
@ -339,32 +366,24 @@ didReceiveResponse:(NSURLResponse *)response
|
|||
}
|
||||
}
|
||||
|
||||
+ (UIImageOrientation)orientationFromPropertyValue:(NSInteger)value {
|
||||
switch (value) {
|
||||
case 1:
|
||||
return UIImageOrientationUp;
|
||||
case 3:
|
||||
return UIImageOrientationDown;
|
||||
case 8:
|
||||
return UIImageOrientationLeft;
|
||||
case 6:
|
||||
return UIImageOrientationRight;
|
||||
case 2:
|
||||
return UIImageOrientationUpMirrored;
|
||||
case 4:
|
||||
return UIImageOrientationDownMirrored;
|
||||
case 5:
|
||||
return UIImageOrientationLeftMirrored;
|
||||
case 7:
|
||||
return UIImageOrientationRightMirrored;
|
||||
default:
|
||||
return UIImageOrientationUp;
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
dataTask:(NSURLSessionDataTask *)dataTask
|
||||
willCacheResponse:(NSCachedURLResponse *)proposedResponse
|
||||
completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler {
|
||||
|
||||
responseFromCached = NO; // If this method is called, it means the response wasn't read from cache
|
||||
NSCachedURLResponse *cachedResponse = proposedResponse;
|
||||
|
||||
if (self.request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData) {
|
||||
// Prevents caching of responses
|
||||
cachedResponse = nil;
|
||||
}
|
||||
if (completionHandler) {
|
||||
completionHandler(cachedResponse);
|
||||
}
|
||||
}
|
||||
|
||||
- (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image {
|
||||
return SDScaledImageForKey(key, image);
|
||||
}
|
||||
#pragma mark NSURLSessionTaskDelegate
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
|
||||
@synchronized(self) {
|
||||
|
@ -419,32 +438,7 @@ didReceiveResponse:(NSURLResponse *)response
|
|||
[self done];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
dataTask:(NSURLSessionDataTask *)dataTask
|
||||
willCacheResponse:(NSCachedURLResponse *)proposedResponse
|
||||
completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler {
|
||||
|
||||
responseFromCached = NO; // If this method is called, it means the response wasn't read from cache
|
||||
NSCachedURLResponse *cachedResponse = proposedResponse;
|
||||
|
||||
if (self.request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData) {
|
||||
// Prevents caching of responses
|
||||
cachedResponse = nil;
|
||||
}
|
||||
if (completionHandler) {
|
||||
completionHandler(cachedResponse);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)shouldContinueWhenAppEntersBackground {
|
||||
return self.options & SDWebImageDownloaderContinueInBackground;
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
task:(NSURLSessionTask *)task
|
||||
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
|
||||
NSURLCredential *credential))completionHandler {
|
||||
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
|
||||
|
||||
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
|
||||
__block NSURLCredential *credential = nil;
|
||||
|
@ -474,4 +468,37 @@ didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
|
|||
}
|
||||
}
|
||||
|
||||
#pragma mark Helper methods
|
||||
|
||||
+ (UIImageOrientation)orientationFromPropertyValue:(NSInteger)value {
|
||||
switch (value) {
|
||||
case 1:
|
||||
return UIImageOrientationUp;
|
||||
case 3:
|
||||
return UIImageOrientationDown;
|
||||
case 8:
|
||||
return UIImageOrientationLeft;
|
||||
case 6:
|
||||
return UIImageOrientationRight;
|
||||
case 2:
|
||||
return UIImageOrientationUpMirrored;
|
||||
case 4:
|
||||
return UIImageOrientationDownMirrored;
|
||||
case 5:
|
||||
return UIImageOrientationLeftMirrored;
|
||||
case 7:
|
||||
return UIImageOrientationRightMirrored;
|
||||
default:
|
||||
return UIImageOrientationUp;
|
||||
}
|
||||
}
|
||||
|
||||
- (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image {
|
||||
return SDScaledImageForKey(key, image);
|
||||
}
|
||||
|
||||
- (BOOL)shouldContinueWhenAppEntersBackground {
|
||||
return self.options & SDWebImageDownloaderContinueInBackground;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
Loading…
Reference in New Issue