SDWebImageDownloader.m
5.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDWebImageDownloader.h"
#import "SDWebImageDownloaderOperation.h"
#import <ImageIO/ImageIO.h>
NSString *const SDWebImageDownloadStartNotification = @"SDWebImageDownloadStartNotification";
NSString *const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNotification";
NSString *const kProgressCallbackKey = @"completed";
NSString *const kCompletedCallbackKey = @"completed";
@interface SDWebImageDownloader ()
@property (strong, nonatomic) NSOperationQueue *downloadQueue;
@property (strong, nonatomic) NSMutableDictionary *URLCallbacks;
@end
@implementation SDWebImageDownloader
+ (void)initialize
{
// Bind SDNetworkActivityIndicator if available (download it here: http://github.com/rs/SDNetworkActivityIndicator )
// To use it, just add #import "SDNetworkActivityIndicator.h" in addition to the SDWebImage import
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
// Remove observer in case it was previously added.
[[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];
}
}
+ (SDWebImageDownloader *)sharedDownloader
{
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{instance = self.new;});
return instance;
}
- (id)init
{
if ((self = [super init]))
{
_downloadQueue = NSOperationQueue.new;
_downloadQueue.maxConcurrentOperationCount = 10;
_URLCallbacks = NSMutableDictionary.new;
}
return self;
}
- (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads
{
_downloadQueue.maxConcurrentOperationCount = maxConcurrentDownloads;
}
- (NSInteger)maxConcurrentDownloads
{
return _downloadQueue.maxConcurrentOperationCount;
}
- (NSOperation *)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(void (^)(NSUInteger, long long))progressBlock completed:(void (^)(UIImage *, NSError *, BOOL))completedBlock
{
__block SDWebImageDownloaderOperation *operation;
dispatch_async(dispatch_get_main_queue(), ^ // NSDictionary isn't thread safe
{
BOOL performDownload = NO;
if (!self.URLCallbacks[url])
{
self.URLCallbacks[url] = NSMutableArray.new;
performDownload = 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 (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];
}
});
return operation;
}
@end