Authored by 朱小军

修改原先子线程查询图片URL的方式,子线程在suboperation的外部执行 review by 于良

@@ -10,7 +10,11 @@ @@ -10,7 +10,11 @@
10 #import "SDWebImageDownloaderOperation.h" 10 #import "SDWebImageDownloaderOperation.h"
11 #import <ImageIO/ImageIO.h> 11 #import <ImageIO/ImageIO.h>
12 12
  13 +#define AliCloudHttpDNS
  14 +
  15 +#ifdef AliCloudHttpDNS
13 #import <AlicloudHttpDNS/AlicloudHttpDNS.h> 16 #import <AlicloudHttpDNS/AlicloudHttpDNS.h>
  17 +#endif
14 18
15 static NSString *const kProgressCallbackKey = @"progress"; 19 static NSString *const kProgressCallbackKey = @"progress";
16 static NSString *const kCompletedCallbackKey = @"completed"; 20 static NSString *const kCompletedCallbackKey = @"completed";
@@ -82,7 +86,7 @@ static NSString *const kCompletedCallbackKey = @"completed"; @@ -82,7 +86,7 @@ static NSString *const kCompletedCallbackKey = @"completed";
82 #if DEBUG 86 #if DEBUG
83 _enableHttpDNS = YES; 87 _enableHttpDNS = YES;
84 #else 88 #else
85 - _enableHttpDNS = [[[NSUserDefaults standardUserDefaults] stringForKey:@"kUserConfigHttpDNS"] isEqualToString:@"2a90dfa0f37b92aaebf369e9a4d38ba4"]; 89 + _enableHttpDNS = YES;//[[[NSUserDefaults standardUserDefaults] stringForKey:@"kUserConfigHttpDNS"] isEqualToString:@"2a90dfa0f37b92aaebf369e9a4d38ba4"];
86 #endif 90 #endif
87 } 91 }
88 return self; 92 return self;
@@ -125,100 +129,101 @@ static NSString *const kCompletedCallbackKey = @"completed"; @@ -125,100 +129,101 @@ static NSString *const kCompletedCallbackKey = @"completed";
125 - (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock { 129 - (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock {
126 __block SDWebImageDownloaderOperation *operation; 130 __block SDWebImageDownloaderOperation *operation;
127 __weak __typeof(self)wself = self; 131 __weak __typeof(self)wself = self;
128 - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
129 - BOOL useAliDNS = NO;  
130 - NSString *imageURLHost = url.host;//获取图片URL的host  
131 - NSURL *newURL = url;  
132 - if (wself.enableHttpDNS && (imageURLHost.length > 0)) {  
133 - NSArray *ips = [[HttpDnsService sharedInstance] getIpsByHostAsync:imageURLHost];//同步请求ip地址列表  
134 - if ([ips isKindOfClass:[NSArray class]] && [ips respondsToSelector:@selector(count)] && ips.count > 0) {  
135 - NSString *ipAdress = [ips objectAtIndex:0];  
136 - NSString *urlString = [url absoluteString];  
137 - NSRange hostRange = [urlString rangeOfString:imageURLHost];  
138 - if ((NSNotFound != hostRange.location) && (ipAdress.length > 0)) {  
139 - useAliDNS = YES;  
140 - NSString *convertedURLString = [urlString stringByReplacingCharactersInRange:hostRange withString:ipAdress];//host转换之后的urlstring  
141 - newURL = [NSURL URLWithString:convertedURLString]; //替换原始url为转换后的值  
142 - }  
143 - }  
144 - }  
145 132
146 - [wself addProgressCallback:progressBlock andCompletedBlock:completedBlock forURL:url createCallback:^{  
147 - NSTimeInterval timeoutInterval = wself.downloadTimeout;  
148 - if (timeoutInterval == 0.0) {  
149 - timeoutInterval = 15.0; 133 + BOOL useAliDNS = NO;
  134 + NSString *imageURLHost = url.host;//获取图片URL的host
  135 + NSURL *newURL = url;
  136 +#ifdef AliCloudHttpDNS
  137 + if (self.enableHttpDNS && (imageURLHost.length > 0)) {
  138 + NSArray *ips = [[HttpDnsService sharedInstance] getIpsByHostAsync:imageURLHost];//同步请求ip地址列表
  139 + if ([ips isKindOfClass:[NSArray class]] && [ips respondsToSelector:@selector(count)] && ips.count > 0) {
  140 + NSString *ipAdress = [ips objectAtIndex:0];
  141 + NSString *urlString = [url absoluteString];
  142 + NSRange hostRange = [urlString rangeOfString:imageURLHost];
  143 + if ((NSNotFound != hostRange.location) && (ipAdress.length > 0)) {
  144 + useAliDNS = YES;
  145 + NSString *convertedURLString = [urlString stringByReplacingCharactersInRange:hostRange withString:ipAdress];//host转换之后的urlstring
  146 + newURL = [NSURL URLWithString:convertedURLString]; //替换原始url为转换后的值
150 } 147 }
  148 + }
  149 + }
  150 +#endif
  151 +
  152 + [self addProgressCallback:progressBlock andCompletedBlock:completedBlock forURL:url createCallback:^{
  153 + NSTimeInterval timeoutInterval = wself.downloadTimeout;
  154 + if (timeoutInterval == 0.0) {
  155 + timeoutInterval = 15.0;
  156 + }
151 157
152 - // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise  
153 - NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:newURL cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval];  
154 - request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);  
155 - request.HTTPShouldUsePipelining = YES;  
156 - if (wself.headersFilter) {  
157 - request.allHTTPHeaderFields = wself.headersFilter(newURL, [wself.HTTPHeaders copy]);  
158 - }  
159 - else {  
160 - request.allHTTPHeaderFields = wself.HTTPHeaders;  
161 - }  
162 - if (useAliDNS) { //如果使用了DNS功能,要设置httpheader的host字段的值,否则请求失败  
163 - [request setValue:imageURLHost forHTTPHeaderField:@"Host"];  
164 - }  
165 - operation = [[wself.operationClass alloc] initWithRequest:request  
166 - options:options  
167 - progress:^(NSInteger receivedSize, NSInteger expectedSize) {  
168 - SDWebImageDownloader *sself = wself;  
169 - if (!sself) return;  
170 - __block NSArray *callbacksForURL;  
171 - dispatch_sync(sself.barrierQueue, ^{  
172 - callbacksForURL = [sself.URLCallbacks[url] copy];  
173 - });  
174 - for (NSDictionary *callbacks in callbacksForURL) {  
175 - SDWebImageDownloaderProgressBlock callback = callbacks[kProgressCallbackKey];  
176 - if (callback) callback(receivedSize, expectedSize);  
177 - } 158 + // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise
  159 + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:newURL cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval];
  160 + request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
  161 + request.HTTPShouldUsePipelining = YES;
  162 + if (wself.headersFilter) {
  163 + request.allHTTPHeaderFields = wself.headersFilter(newURL, [wself.HTTPHeaders copy]);
  164 + }
  165 + else {
  166 + request.allHTTPHeaderFields = wself.HTTPHeaders;
  167 + }
  168 + if (useAliDNS) { //如果使用了DNS功能,要设置httpheader的host字段的值,否则请求失败
  169 + [request setValue:imageURLHost forHTTPHeaderField:@"Host"];
  170 + }
  171 + operation = [[wself.operationClass alloc] initWithRequest:request
  172 + options:options
  173 + progress:^(NSInteger receivedSize, NSInteger expectedSize) {
  174 + SDWebImageDownloader *sself = wself;
  175 + if (!sself) return;
  176 + __block NSArray *callbacksForURL;
  177 + dispatch_sync(sself.barrierQueue, ^{
  178 + callbacksForURL = [sself.URLCallbacks[url] copy];
  179 + });
  180 + for (NSDictionary *callbacks in callbacksForURL) {
  181 + SDWebImageDownloaderProgressBlock callback = callbacks[kProgressCallbackKey];
  182 + if (callback) callback(receivedSize, expectedSize);
178 } 183 }
179 - completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {  
180 - // NSLog(@"download image completed:url == %@,\nimage = %@",url,image);  
181 - SDWebImageDownloader *sself = wself;  
182 - if (!sself) return;  
183 - __block NSArray *callbacksForURL;  
184 - dispatch_barrier_sync(sself.barrierQueue, ^{  
185 - callbacksForURL = [sself.URLCallbacks[url] copy];  
186 - if (finished) {  
187 - [sself.URLCallbacks removeObjectForKey:url];  
188 - }  
189 - });  
190 - for (NSDictionary *callbacks in callbacksForURL) {  
191 - SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey];  
192 - if (callback) callback(image, data, error, finished); 184 + }
  185 + completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
  186 +// NSLog(@"download image completed:url == %@,\nimage = %@",url,image);
  187 + SDWebImageDownloader *sself = wself;
  188 + if (!sself) return;
  189 + __block NSArray *callbacksForURL;
  190 + dispatch_barrier_sync(sself.barrierQueue, ^{
  191 + callbacksForURL = [sself.URLCallbacks[url] copy];
  192 + if (finished) {
  193 + [sself.URLCallbacks removeObjectForKey:url];
193 } 194 }
  195 + });
  196 + for (NSDictionary *callbacks in callbacksForURL) {
  197 + SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey];
  198 + if (callback) callback(image, data, error, finished);
194 } 199 }
195 - cancelled:^{  
196 - SDWebImageDownloader *sself = wself;  
197 - if (!sself) return;  
198 - dispatch_barrier_async(sself.barrierQueue, ^{  
199 - [sself.URLCallbacks removeObjectForKey:url];  
200 - });  
201 - }];  
202 - operation.shouldDecompressImages = wself.shouldDecompressImages;  
203 -  
204 - if (wself.username && wself.password) {  
205 - operation.credential = [NSURLCredential credentialWithUser:wself.username password:wself.password persistence:NSURLCredentialPersistenceForSession];  
206 - }  
207 -  
208 - if (options & SDWebImageDownloaderHighPriority) {  
209 - operation.queuePriority = NSOperationQueuePriorityHigh;  
210 - } else if (options & SDWebImageDownloaderLowPriority) {  
211 - operation.queuePriority = NSOperationQueuePriorityLow;  
212 - } 200 + }
  201 + cancelled:^{
  202 + SDWebImageDownloader *sself = wself;
  203 + if (!sself) return;
  204 + dispatch_barrier_async(sself.barrierQueue, ^{
  205 + [sself.URLCallbacks removeObjectForKey:url];
  206 + });
  207 + }];
  208 + operation.shouldDecompressImages = wself.shouldDecompressImages;
  209 +
  210 + if (wself.username && wself.password) {
  211 + operation.credential = [NSURLCredential credentialWithUser:wself.username password:wself.password persistence:NSURLCredentialPersistenceForSession];
  212 + }
  213 +
  214 + if (options & SDWebImageDownloaderHighPriority) {
  215 + operation.queuePriority = NSOperationQueuePriorityHigh;
  216 + } else if (options & SDWebImageDownloaderLowPriority) {
  217 + operation.queuePriority = NSOperationQueuePriorityLow;
  218 + }
213 219
214 - [wself.downloadQueue addOperation:operation];  
215 - if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {  
216 - // Emulate LIFO execution order by systematically adding new operations as last operation's dependency  
217 - [wself.lastAddedOperation addDependency:operation];  
218 - wself.lastAddedOperation = operation;  
219 - }  
220 - }];  
221 - }); 220 + [wself.downloadQueue addOperation:operation];
  221 + if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
  222 + // Emulate LIFO execution order by systematically adding new operations as last operation's dependency
  223 + [wself.lastAddedOperation addDependency:operation];
  224 + wself.lastAddedOperation = operation;
  225 + }
  226 + }];
222 227
223 return operation; 228 return operation;
224 } 229 }
@@ -180,81 +180,88 @@ @@ -180,81 +180,88 @@
180 // ignore image read from NSURLCache if image if cached but force refreshing 180 // ignore image read from NSURLCache if image if cached but force refreshing
181 downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse; 181 downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
182 } 182 }
183 - id <SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) {  
184 - if (weakOperation.isCancelled) {  
185 - // Do nothing if the operation was cancelled  
186 - // See #699 for more details  
187 - // 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  
188 - }  
189 - else if (error) {  
190 - dispatch_main_sync_safe(^{  
191 - if (!weakOperation.isCancelled) {  
192 - completedBlock(nil, error, SDImageCacheTypeNone, finished, url);  
193 - }  
194 - });  
195 -  
196 - BOOL shouldBeFailedURLAlliOSVersion = (error.code != NSURLErrorNotConnectedToInternet && error.code != NSURLErrorCancelled && error.code != NSURLErrorTimedOut);  
197 - BOOL shouldBeFailedURLiOS7 = (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_6_1 && error.code != NSURLErrorInternationalRoamingOff && error.code != NSURLErrorCallIsActive && error.code != NSURLErrorDataNotAllowed);  
198 - if (shouldBeFailedURLAlliOSVersion || shouldBeFailedURLiOS7) {  
199 - @synchronized (self.failedURLs) {  
200 - [self.failedURLs addObject:url];  
201 - } 183 +
  184 + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  185 + id <SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) {
  186 +
  187 + SDWebImageCombinedOperation *ssOperation = weakOperation;
  188 +
  189 + if (ssOperation.isCancelled) {
  190 + // Do nothing if the operation was cancelled
  191 + // See #699 for more details
  192 + // 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
202 } 193 }
203 - }  
204 - else {  
205 - if ((options & SDWebImageRetryFailed)) {  
206 - @synchronized (self.failedURLs) {  
207 - [self.failedURLs removeObject:url]; 194 + else if (error) {
  195 + dispatch_main_sync_safe(^{
  196 + if (!ssOperation.isCancelled) {
  197 + completedBlock(nil, error, SDImageCacheTypeNone, finished, url);
  198 + }
  199 + });
  200 +
  201 + BOOL shouldBeFailedURLAlliOSVersion = (error.code != NSURLErrorNotConnectedToInternet && error.code != NSURLErrorCancelled && error.code != NSURLErrorTimedOut);
  202 + BOOL shouldBeFailedURLiOS7 = (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_6_1 && error.code != NSURLErrorInternationalRoamingOff && error.code != NSURLErrorCallIsActive && error.code != NSURLErrorDataNotAllowed);
  203 + if (shouldBeFailedURLAlliOSVersion || shouldBeFailedURLiOS7) {
  204 + @synchronized (self.failedURLs) {
  205 + [self.failedURLs addObject:url];
  206 + }
208 } 207 }
209 } 208 }
210 -  
211 - BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);  
212 -  
213 - if (options & SDWebImageRefreshCached && image && !downloadedImage) {  
214 - // Image refresh hit the NSURLCache cache, do not call the completion block  
215 - }  
216 - else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {  
217 - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{  
218 - UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];  
219 -  
220 - if (transformedImage && finished) {  
221 - BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];  
222 - [self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : data) forKey:key toDisk:cacheOnDisk]; 209 + else {
  210 + if ((options & SDWebImageRetryFailed)) {
  211 + @synchronized (self.failedURLs) {
  212 + [self.failedURLs removeObject:url];
223 } 213 }
224 - 214 + }
  215 +
  216 + BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);
  217 +
  218 + if (options & SDWebImageRefreshCached && image && !downloadedImage) {
  219 + // Image refresh hit the NSURLCache cache, do not call the completion block
  220 + }
  221 + else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
  222 + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
  223 + UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];
  224 +
  225 + if (transformedImage && finished) {
  226 + BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
  227 + [self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : data) forKey:key toDisk:cacheOnDisk];
  228 + }
  229 +
  230 + dispatch_main_sync_safe(^{
  231 + if (!ssOperation.isCancelled) {
  232 + completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url);
  233 + }
  234 + });
  235 + });
  236 + }
  237 + else {
  238 + if (downloadedImage && finished) {
  239 + [self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk];
  240 + }
  241 +
225 dispatch_main_sync_safe(^{ 242 dispatch_main_sync_safe(^{
226 - if (!weakOperation.isCancelled) {  
227 - completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url); 243 + if (!ssOperation.isCancelled) {
  244 + completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url);
228 } 245 }
229 }); 246 });
230 - }); 247 + }
231 } 248 }
232 - else {  
233 - if (downloadedImage && finished) {  
234 - [self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk]; 249 +
  250 + if (finished) {
  251 + @synchronized (self.runningOperations) {
  252 + [self.runningOperations removeObject:operation];
235 } 253 }
236 -  
237 - dispatch_main_sync_safe(^{  
238 - if (!weakOperation.isCancelled) {  
239 - completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url);  
240 - }  
241 - });  
242 } 254 }
243 - }  
244 -  
245 - if (finished) { 255 + }];
  256 + operation.cancelBlock = ^{
  257 + [subOperation cancel];
  258 +
246 @synchronized (self.runningOperations) { 259 @synchronized (self.runningOperations) {
247 - [self.runningOperations removeObject:operation]; 260 + [self.runningOperations removeObject:weakOperation];
248 } 261 }
249 - }  
250 - }];  
251 - operation.cancelBlock = ^{  
252 - [subOperation cancel];  
253 -  
254 - @synchronized (self.runningOperations) {  
255 - [self.runningOperations removeObject:weakOperation];  
256 - }  
257 - }; 262 + };
  263 + });
  264 +
258 } 265 }
259 else if (image) { 266 else if (image) {
260 dispatch_main_sync_safe(^{ 267 dispatch_main_sync_safe(^{