123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- #import "SDWebImageDownloader.h"
- #import "SDWebImageDownloaderOperation.h"
- #import <ImageIO/ImageIO.h>
- @implementation SDWebImageDownloadToken
- @end
- @interface SDWebImageDownloader () <NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
- @property (strong, nonatomic, nonnull) NSOperationQueue *downloadQueue;
- @property (weak, nonatomic, nullable) NSOperation *lastAddedOperation;
- @property (assign, nonatomic, nullable) Class operationClass;
- @property (strong, nonatomic, nonnull) NSMutableDictionary<NSURL *, SDWebImageDownloaderOperation *> *URLOperations;
- @property (strong, nonatomic, nullable) SDHTTPHeadersMutableDictionary *HTTPHeaders;
- @property (SDDispatchQueueSetterSementics, nonatomic, nullable) dispatch_queue_t barrierQueue;
- @property (strong, nonatomic) NSURLSession *session;
- @end
- @implementation SDWebImageDownloader
- + (void)initialize {
-
-
- if (NSClassFromString(@"SDNetworkActivityIndicator")) {
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
- id activityIndicator = [NSClassFromString(@"SDNetworkActivityIndicator") performSelector:NSSelectorFromString(@"sharedActivityIndicator")];
- #pragma clang diagnostic pop
-
- [[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStartNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStopNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:activityIndicator
- selector:NSSelectorFromString(@"startActivity")
- name:SDWebImageDownloadStartNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:activityIndicator
- selector:NSSelectorFromString(@"stopActivity")
- name:SDWebImageDownloadStopNotification object:nil];
- }
- }
- + (nonnull instancetype)sharedDownloader {
- static dispatch_once_t once;
- static id instance;
- dispatch_once(&once, ^{
- instance = [self new];
- });
- return instance;
- }
- - (nonnull instancetype)init {
- return [self initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
- }
- - (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration {
- if ((self = [super init])) {
- _operationClass = [SDWebImageDownloaderOperation class];
- _shouldDecompressImages = YES;
- _executionOrder = SDWebImageDownloaderFIFOExecutionOrder;
- _downloadQueue = [NSOperationQueue new];
- _downloadQueue.maxConcurrentOperationCount = 6;
- _downloadQueue.name = @"com.hackemist.SDWebImageDownloader";
- _URLOperations = [NSMutableDictionary new];
- #ifdef SD_WEBP
- _HTTPHeaders = [@{@"Accept": @"image/webp,image/*;q=0.8"} mutableCopy];
- #else
- _HTTPHeaders = [@{@"Accept": @"image/*;q=0.8"} mutableCopy];
- #endif
- _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT);
- _downloadTimeout = 15.0;
- [self createNewSessionWithConfiguration:sessionConfiguration];
- }
- return self;
- }
- - (void)createNewSessionWithConfiguration:(NSURLSessionConfiguration *)sessionConfiguration {
- [self cancelAllDownloads];
- if (self.session) {
- [self.session invalidateAndCancel];
- }
- sessionConfiguration.timeoutIntervalForRequest = self.downloadTimeout;
-
- self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
- delegate:self
- delegateQueue:nil];
- }
- - (void)dealloc {
- [self.session invalidateAndCancel];
- self.session = nil;
- [self.downloadQueue cancelAllOperations];
- SDDispatchQueueRelease(_barrierQueue);
- }
- - (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)field {
- if (value) {
- self.HTTPHeaders[field] = value;
- } else {
- [self.HTTPHeaders removeObjectForKey:field];
- }
- }
- - (nullable NSString *)valueForHTTPHeaderField:(nullable NSString *)field {
- return self.HTTPHeaders[field];
- }
- - (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads {
- _downloadQueue.maxConcurrentOperationCount = maxConcurrentDownloads;
- }
- - (NSUInteger)currentDownloadCount {
- return _downloadQueue.operationCount;
- }
- - (NSInteger)maxConcurrentDownloads {
- return _downloadQueue.maxConcurrentOperationCount;
- }
- - (NSURLSessionConfiguration *)sessionConfiguration {
- return self.session.configuration;
- }
- - (void)setOperationClass:(nullable Class)operationClass {
- if (operationClass && [operationClass isSubclassOfClass:[NSOperation class]] && [operationClass conformsToProtocol:@protocol(SDWebImageDownloaderOperationInterface)]) {
- _operationClass = operationClass;
- } else {
- _operationClass = [SDWebImageDownloaderOperation class];
- }
- }
- - (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
- options:(SDWebImageDownloaderOptions)options
- progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
- completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
- __weak SDWebImageDownloader *wself = self;
- return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{
- __strong __typeof (wself) sself = wself;
- NSTimeInterval timeoutInterval = sself.downloadTimeout;
- if (timeoutInterval == 0.0) {
- timeoutInterval = 15.0;
- }
-
- NSURLRequestCachePolicy cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
- if (options & SDWebImageDownloaderUseNSURLCache) {
- if (options & SDWebImageDownloaderIgnoreCachedResponse) {
- cachePolicy = NSURLRequestReturnCacheDataDontLoad;
- } else {
- cachePolicy = NSURLRequestUseProtocolCachePolicy;
- }
- }
-
- NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:cachePolicy timeoutInterval:timeoutInterval];
-
- request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
- request.HTTPShouldUsePipelining = YES;
- if (sself.headersFilter) {
- request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]);
- }
- else {
- request.allHTTPHeaderFields = sself.HTTPHeaders;
- }
- SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];
- operation.shouldDecompressImages = sself.shouldDecompressImages;
-
- if (sself.urlCredential) {
- operation.credential = sself.urlCredential;
- } else if (sself.username && sself.password) {
- operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession];
- }
-
- if (options & SDWebImageDownloaderHighPriority) {
- operation.queuePriority = NSOperationQueuePriorityHigh;
- } else if (options & SDWebImageDownloaderLowPriority) {
- operation.queuePriority = NSOperationQueuePriorityLow;
- }
- [sself.downloadQueue addOperation:operation];
- if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
-
- [sself.lastAddedOperation addDependency:operation];
- sself.lastAddedOperation = operation;
- }
- return operation;
- }];
- }
- - (void)cancel:(nullable SDWebImageDownloadToken *)token {
- dispatch_barrier_async(self.barrierQueue, ^{
- SDWebImageDownloaderOperation *operation = self.URLOperations[token.url];
- BOOL canceled = [operation cancel:token.downloadOperationCancelToken];
- if (canceled) {
- [self.URLOperations removeObjectForKey:token.url];
- }
- });
- }
- - (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
- completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock
- forURL:(nullable NSURL *)url
- createCallback:(SDWebImageDownloaderOperation *(^)(void))createCallback {
-
- if (url == nil) {
- if (completedBlock != nil) {
- completedBlock(nil, nil, nil, NO);
- }
- return nil;
- }
- __block SDWebImageDownloadToken *token = nil;
- dispatch_barrier_sync(self.barrierQueue, ^{
- SDWebImageDownloaderOperation *operation = self.URLOperations[url];
- if (!operation) {
- operation = createCallback();
- self.URLOperations[url] = operation;
- __weak SDWebImageDownloaderOperation *woperation = operation;
- operation.completionBlock = ^{
- dispatch_barrier_sync(self.barrierQueue, ^{
- SDWebImageDownloaderOperation *soperation = woperation;
- if (!soperation) return;
- if (self.URLOperations[url] == soperation) {
- [self.URLOperations removeObjectForKey:url];
- };
- });
- };
- }
- id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
- token = [SDWebImageDownloadToken new];
- token.url = url;
- token.downloadOperationCancelToken = downloadOperationCancelToken;
- });
- return token;
- }
- - (void)setSuspended:(BOOL)suspended {
- self.downloadQueue.suspended = suspended;
- }
- - (void)cancelAllDownloads {
- [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 {
-
- SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask];
- [dataOperation URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler];
- }
- - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
-
- 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 {
-
- 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 {
-
- SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:task];
- [dataOperation URLSession:session task:task didCompleteWithError:error];
- }
- - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler {
-
- completionHandler(request);
- }
- - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
-
- SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:task];
- [dataOperation URLSession:session task:task didReceiveChallenge:challenge completionHandler:completionHandler];
- }
- @end
|