Refactor current manager's internal implementation for that `loadImageWithURL:`. Separate to 3 parts (context preprocess, cache process, download process). Also introduce `SDWebImageFromLoaderOnly` feature to ignore cache at all
This is useful for some custom image loader where user does not need memory cache at all.
This commit is contained in:
parent
bf85c53b77
commit
cfd68422dc
|
@ -145,31 +145,36 @@ typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
|
|||
SDWebImageQueryDiskSync = 1 << 14,
|
||||
|
||||
/**
|
||||
* By default, when the cache missed, the image is download from the network. This flag can prevent network to load from cache only.
|
||||
* By default, when the cache missed, the image is load from the loader. This flag can prevent this to load from cache only.
|
||||
*/
|
||||
SDWebImageFromCacheOnly = 1 << 15,
|
||||
|
||||
/**
|
||||
* By default, we query the cache before the image is load from the loader. This flag can prevent this to load from loader only.
|
||||
*/
|
||||
SDWebImageFromLoaderOnly = 1 << 16,
|
||||
|
||||
/**
|
||||
* By default, when you use `SDWebImageTransition` to do some view transition after the image load finished, this transition is only applied for image download from the network. This mask can force to apply view transition for memory and disk cache as well.
|
||||
*/
|
||||
SDWebImageForceTransition = 1 << 16,
|
||||
SDWebImageForceTransition = 1 << 17,
|
||||
|
||||
/**
|
||||
* By default, we will decode the image in the background during cache query and download from the network. This can help to improve performance because when rendering image on the screen, it need to be firstly decoded. But this happen on the main queue by Core Animation.
|
||||
* However, this process may increase the memory usage as well. If you are experiencing a issue due to excessive memory consumption, This flag can prevent decode the image.
|
||||
*/
|
||||
SDWebImageAvoidDecodeImage = 1 << 17,
|
||||
SDWebImageAvoidDecodeImage = 1 << 18,
|
||||
|
||||
/**
|
||||
* By default, we decode the animated image. This flag can force decode the first frame only and produece the static image.
|
||||
*/
|
||||
SDWebImageDecodeFirstFrameOnly = 1 << 18,
|
||||
SDWebImageDecodeFirstFrameOnly = 1 << 19,
|
||||
|
||||
/**
|
||||
* By default, for `SDAnimatedImage`, we decode the animated image frame during rendering to reduce memory usage. However, you can specify to preload all frames into memory to reduce CPU usage when the animated image is shared by lots of imageViews.
|
||||
* This will actually trigger `preloadAllAnimatedImageFrames` in the background queue(Disk Cache & Download only).
|
||||
*/
|
||||
SDWebImagePreloadAllFrames = 1 << 19
|
||||
SDWebImagePreloadAllFrames = 1 << 20
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -33,7 +33,8 @@ typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
|
|||
|
||||
/**
|
||||
* Call completion block with nil image/imageData if the image was read from NSURLCache
|
||||
* (to be combined with `SDWebImageDownloaderUseNSURLCache`).
|
||||
* And the error code is `SDWebImageErrorCacheNotModified`
|
||||
* This flag should be combined with `SDWebImageDownloaderUseNSURLCache`.
|
||||
*/
|
||||
SDWebImageDownloaderIgnoreCachedResponse = 1 << 3,
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;
|
|||
@property (copy, nonatomic, nullable) NSData *cachedData; // for `SDWebImageDownloaderIgnoreCachedResponse`
|
||||
@property (assign, nonatomic, readwrite) NSUInteger expectedSize;
|
||||
@property (strong, nonatomic, nullable, readwrite) NSURLResponse *response;
|
||||
@property (strong, nonatomic, nullable) NSError *responseError;
|
||||
|
||||
// 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
|
||||
|
@ -298,11 +299,15 @@ didReceiveResponse:(NSURLResponse *)response
|
|||
self.expectedSize = expected;
|
||||
self.response = response;
|
||||
NSInteger statusCode = [response respondsToSelector:@selector(statusCode)] ? ((NSHTTPURLResponse *)response).statusCode : 200;
|
||||
BOOL valid = statusCode < 400;
|
||||
//'304 Not Modified' is an exceptional one. It should be treated as cancelled if no cache data
|
||||
BOOL valid = statusCode >= 200 && statusCode < 400;
|
||||
if (!valid) {
|
||||
self.responseError = [NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorInvalidDownloadStatusCode userInfo:@{SDWebImageErrorDownloadStatusCodeKey : @(statusCode)}];
|
||||
}
|
||||
//'304 Not Modified' is an exceptional one
|
||||
//URLSession current behavior will return 200 status code when the server respond 304 and URLCache hit. But this is not a standard behavior and we just add a check
|
||||
if (statusCode == 304 && !self.cachedData) {
|
||||
valid = NO;
|
||||
self.responseError = [NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCacheNotModified userInfo:nil];
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
|
@ -386,6 +391,10 @@ didReceiveResponse:(NSURLResponse *)response
|
|||
|
||||
// make sure to call `[self done]` to mark operation as finished
|
||||
if (error) {
|
||||
// custom error instead of URLSession error
|
||||
if (self.responseError) {
|
||||
error = self.responseError;
|
||||
}
|
||||
[self callCompletionBlocksWithError:error];
|
||||
[self done];
|
||||
} else {
|
||||
|
@ -396,8 +405,9 @@ didReceiveResponse:(NSURLResponse *)response
|
|||
* then we should check if the cached data is equal to image data
|
||||
*/
|
||||
if (self.options & SDWebImageDownloaderIgnoreCachedResponse && [self.cachedData isEqualToData:imageData]) {
|
||||
// call completion block with nil
|
||||
[self callCompletionBlocksWithImage:nil imageData:nil error:nil finished:YES];
|
||||
self.responseError = [NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCacheNotModified userInfo:nil];
|
||||
// call completion block with not modified error
|
||||
[self callCompletionBlocksWithError:self.responseError];
|
||||
[self done];
|
||||
} else {
|
||||
// decode the image in coder queue
|
||||
|
|
|
@ -11,8 +11,12 @@
|
|||
|
||||
FOUNDATION_EXPORT NSErrorDomain const _Nonnull SDWebImageErrorDomain;
|
||||
|
||||
FOUNDATION_EXPORT NSErrorUserInfoKey const _Nonnull SDWebImageErrorDownloadStatusCodeKey;
|
||||
|
||||
typedef NS_ERROR_ENUM(SDWebImageErrorDomain, SDWebImageError) {
|
||||
SDWebImageErrorInvalidURL = 1000, // The URL is invalid, such as nil URL or corrupted URL
|
||||
SDWebImageErrorBadImageData = 1001, // The image data can not be decoded to image, or the image data is empty
|
||||
SDWebImageErrorCacheNotModified = 1002, // The remote location specify that the cached image is not modified, such as the HTTP response 304 code. It's useful for `SDWebImageRefreshCached`
|
||||
SDWebImageErrorInvalidDownloadOperation = 2000, // The image download operation is invalid, such as nil operation or unexpected error occur when operation initialized
|
||||
SDWebImageErrorInvalidDownloadStatusCode = 2001, // The image downloda response a invalid status code. You can check the status code in error's userInfo under `SDWebImageErrorDownloadStatusCodeKey`
|
||||
};
|
||||
|
|
|
@ -10,3 +10,4 @@
|
|||
#import "SDWebImageError.h"
|
||||
|
||||
NSErrorDomain const _Nonnull SDWebImageErrorDomain = @"SDWebImageErrorDomain";
|
||||
NSErrorUserInfoKey const _Nonnull SDWebImageErrorDownloadStatusCodeKey = @"SDWebImageErrorDownloadStatusCodeKey";
|
||||
|
|
|
@ -151,171 +151,11 @@ static id<SDWebImageLoader> _defaultImageLoader;
|
|||
[self.runningOperations addObject:operation];
|
||||
}
|
||||
|
||||
// Image transformer
|
||||
id<SDWebImageTransformer> transformer;
|
||||
if ([context valueForKey:SDWebImageContextCustomTransformer]) {
|
||||
transformer = [context valueForKey:SDWebImageContextCustomTransformer];
|
||||
} else if (self.transformer) {
|
||||
// Transformer from manager
|
||||
transformer = self.transformer;
|
||||
SDWebImageMutableContext *mutableContext;
|
||||
if (context) {
|
||||
mutableContext = [context mutableCopy];
|
||||
} else {
|
||||
mutableContext = [NSMutableDictionary dictionary];
|
||||
}
|
||||
[mutableContext setValue:transformer forKey:SDWebImageContextCustomTransformer];
|
||||
context = [mutableContext copy];
|
||||
}
|
||||
// Cache key filter
|
||||
id<SDWebImageCacheKeyFilter> cacheKeyFilter;
|
||||
if ([context valueForKey:SDWebImageContextCacheKeyFilter]) {
|
||||
cacheKeyFilter = [context valueForKey:SDWebImageContextCacheKeyFilter];
|
||||
} else {
|
||||
cacheKeyFilter = self.cacheKeyFilter;
|
||||
}
|
||||
NSString *key = [self cacheKeyForURL:url cacheKeyFilter:cacheKeyFilter];
|
||||
// Cache serializer
|
||||
id<SDWebImageCacheSerializer> cacheSerializer;
|
||||
if ([context valueForKey:SDWebImageContextCacheSerializer]) {
|
||||
cacheSerializer = [context valueForKey:SDWebImageContextCacheSerializer];
|
||||
} else {
|
||||
cacheSerializer = self.cacheSerializer;
|
||||
}
|
||||
// Preprocess the context arg to provide the default value from manager
|
||||
context = [self processedContextWithContext:context];
|
||||
|
||||
__weak SDWebImageCombinedOperation *weakOperation = operation;
|
||||
operation.cacheOperation = [self.imageCache queryImageForKey:key options:options context:context completion:^(UIImage * _Nullable cachedImage, NSData * _Nullable cachedData, SDImageCacheType cacheType) {
|
||||
__strong __typeof(weakOperation) strongOperation = weakOperation;
|
||||
if (!strongOperation || strongOperation.isCancelled) {
|
||||
[self safelyRemoveOperationFromRunning:strongOperation];
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether we should download image from network
|
||||
BOOL shouldDownload = (!(options & SDWebImageFromCacheOnly))
|
||||
&& (!cachedImage || options & SDWebImageRefreshCached)
|
||||
&& (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url]);
|
||||
// Check whether image downloader support target URL
|
||||
shouldDownload &= [self.imageLoader canLoadWithURL:url];
|
||||
if (shouldDownload) {
|
||||
SDWebImageContext *downloadContext = context;
|
||||
if (cacheKeyFilter) {
|
||||
// Pass the cache key filter to the image loader.
|
||||
SDWebImageMutableContext *mutableContext;
|
||||
if (downloadContext) {
|
||||
mutableContext = [downloadContext mutableCopy];
|
||||
} else {
|
||||
mutableContext = [NSMutableDictionary dictionary];
|
||||
}
|
||||
[mutableContext setValue:cacheKeyFilter forKey:SDWebImageContextCacheKeyFilter];
|
||||
downloadContext = [mutableContext copy];
|
||||
}
|
||||
if (cachedImage && options & SDWebImageRefreshCached) {
|
||||
// If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image
|
||||
// AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
|
||||
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
|
||||
// Pass the cached image to the image loader. The image loader should check whether the remote image is equal to the cached image.
|
||||
SDWebImageMutableContext *mutableContext;
|
||||
if (downloadContext) {
|
||||
mutableContext = [downloadContext mutableCopy];
|
||||
} else {
|
||||
mutableContext = [NSMutableDictionary dictionary];
|
||||
}
|
||||
[mutableContext setValue:cachedImage forKey:SDWebImageContextLoaderCachedImage];
|
||||
downloadContext = [mutableContext copy];
|
||||
}
|
||||
|
||||
// `SDWebImageCombinedOperation` -> `SDWebImageDownloadToken` -> `downloadOperationCancelToken`, which is a `SDCallbacksDictionary` and retain the completed block below, so we need weak-strong again to avoid retain cycle
|
||||
__weak typeof(strongOperation) weakSubOperation = strongOperation;
|
||||
strongOperation.downloadOperation = [self.imageLoader loadImageWithURL:url options:options context:downloadContext progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {
|
||||
__strong typeof(weakSubOperation) strongSubOperation = weakSubOperation;
|
||||
if (!strongSubOperation || strongSubOperation.isCancelled) {
|
||||
// Do nothing if the operation was cancelled
|
||||
// See #699 for more details
|
||||
// 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
|
||||
} else if (error) {
|
||||
[self callCompletionBlockForOperation:strongSubOperation completion:completedBlock error:error url:url];
|
||||
BOOL shouldBlockFailedURL;
|
||||
// Check whether we should block failed url
|
||||
if ([self.delegate respondsToSelector:@selector(imageManager:shouldBlockFailedURL:withError:)]) {
|
||||
shouldBlockFailedURL = [self.delegate imageManager:self shouldBlockFailedURL:url withError:error];
|
||||
} else {
|
||||
shouldBlockFailedURL = ( error.code != NSURLErrorNotConnectedToInternet
|
||||
&& error.code != NSURLErrorCancelled
|
||||
&& error.code != NSURLErrorTimedOut
|
||||
&& error.code != NSURLErrorInternationalRoamingOff
|
||||
&& error.code != NSURLErrorDataNotAllowed
|
||||
&& error.code != NSURLErrorCannotFindHost
|
||||
&& error.code != NSURLErrorCannotConnectToHost
|
||||
&& error.code != NSURLErrorNetworkConnectionLost);
|
||||
}
|
||||
|
||||
if (shouldBlockFailedURL) {
|
||||
@synchronized (self.failedURLs) {
|
||||
[self.failedURLs addObject:url];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((options & SDWebImageRetryFailed)) {
|
||||
@synchronized (self.failedURLs) {
|
||||
[self.failedURLs removeObject:url];
|
||||
}
|
||||
}
|
||||
|
||||
SDImageCacheType storeCacheType = SDImageCacheTypeAll;
|
||||
if (options & SDWebImageCacheMemoryOnly) {
|
||||
storeCacheType = SDImageCacheTypeMemory;
|
||||
}
|
||||
if (options & SDWebImageRefreshCached && cachedImage && !downloadedImage) {
|
||||
// Image refresh hit the NSURLCache cache, do not call the completion block
|
||||
} else if (downloadedImage && (!downloadedImage.sd_isAnimated || (options & SDWebImageTransformAnimatedImage)) && transformer) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
||||
UIImage *transformedImage = [transformer transformedImageWithImage:downloadedImage forKey:key];
|
||||
if (transformedImage && finished) {
|
||||
NSString *transformerKey = [transformer transformerKey];
|
||||
NSString *cacheKey = SDTransformedKeyForKey(key, transformerKey);
|
||||
BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
|
||||
NSData *cacheData;
|
||||
// pass nil if the image was transformed, so we can recalculate the data from the image
|
||||
if (cacheSerializer) {
|
||||
cacheData = [cacheSerializer cacheDataWithImage:transformedImage originalData:(imageWasTransformed ? nil : downloadedData) imageURL:url];
|
||||
} else {
|
||||
cacheData = (imageWasTransformed ? nil : downloadedData);
|
||||
}
|
||||
[self.imageCache storeImage:transformedImage imageData:cacheData forKey:cacheKey cacheType:storeCacheType completion:nil];
|
||||
}
|
||||
|
||||
[self callCompletionBlockForOperation:strongSubOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
|
||||
});
|
||||
} else {
|
||||
if (downloadedImage && finished) {
|
||||
if (cacheSerializer) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
||||
NSData *cacheData = [cacheSerializer cacheDataWithImage:downloadedImage originalData:downloadedData imageURL:url];
|
||||
[self.imageCache storeImage:downloadedImage imageData:cacheData forKey:key cacheType:storeCacheType completion:nil];
|
||||
});
|
||||
} else {
|
||||
[self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key cacheType:storeCacheType completion:nil];
|
||||
}
|
||||
}
|
||||
[self callCompletionBlockForOperation:strongSubOperation completion:completedBlock image:downloadedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
|
||||
}
|
||||
}
|
||||
|
||||
if (finished) {
|
||||
[self safelyRemoveOperationFromRunning:strongSubOperation];
|
||||
}
|
||||
}];
|
||||
} else if (cachedImage) {
|
||||
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
|
||||
[self safelyRemoveOperationFromRunning:strongOperation];
|
||||
} else {
|
||||
// Image not in cache and download disallowed by delegate
|
||||
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];
|
||||
[self safelyRemoveOperationFromRunning:strongOperation];
|
||||
}
|
||||
}];
|
||||
// Start the entry to load image from cache
|
||||
[self callCacheProcessForOperation:operation url:url options:options context:context progress:progressBlock completed:completedBlock];
|
||||
|
||||
return operation;
|
||||
}
|
||||
|
@ -336,6 +176,162 @@ static id<SDWebImageLoader> _defaultImageLoader;
|
|||
return isRunning;
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)callCacheProcessForOperation:(nonnull SDWebImageCombinedOperation *)operation
|
||||
url:(nullable NSURL *)url
|
||||
options:(SDWebImageOptions)options
|
||||
context:(nullable SDWebImageContext *)context
|
||||
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
|
||||
completed:(nullable SDInternalCompletionBlock)completedBlock {
|
||||
// Check whether we should query cache
|
||||
BOOL shouldQueryCache = (options & SDWebImageFromLoaderOnly) == 0;
|
||||
if (shouldQueryCache) {
|
||||
id<SDWebImageCacheKeyFilter> cacheKeyFilter = [context valueForKey:SDWebImageContextCacheKeyFilter];
|
||||
NSString *key = [self cacheKeyForURL:url cacheKeyFilter:cacheKeyFilter];
|
||||
__weak SDWebImageCombinedOperation *weakOperation = operation;
|
||||
operation.cacheOperation = [self.imageCache queryImageForKey:key options:options context:context completion:^(UIImage * _Nullable cachedImage, NSData * _Nullable cachedData, SDImageCacheType cacheType) {
|
||||
__strong __typeof(weakOperation) strongOperation = weakOperation;
|
||||
if (!strongOperation || strongOperation.isCancelled) {
|
||||
[self safelyRemoveOperationFromRunning:strongOperation];
|
||||
return;
|
||||
}
|
||||
// Continue download process
|
||||
[self callDownloadProcessForOperation:strongOperation url:url options:options context:context cachedImage:cachedImage cachedData:cachedData cacheType:cacheType progress:progressBlock completed:completedBlock];
|
||||
}];
|
||||
} else {
|
||||
// Continue download process
|
||||
[self callDownloadProcessForOperation:operation url:url options:options context:context cachedImage:nil cachedData:nil cacheType:SDImageCacheTypeNone progress:progressBlock completed:completedBlock];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)callDownloadProcessForOperation:(nonnull SDWebImageCombinedOperation *)operation
|
||||
url:(nullable NSURL *)url
|
||||
options:(SDWebImageOptions)options
|
||||
context:(SDWebImageContext *)context
|
||||
cachedImage:(nullable UIImage *)cachedImage
|
||||
cachedData:(nullable NSData *)cachedData
|
||||
cacheType:(SDImageCacheType)cacheType
|
||||
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
|
||||
completed:(nullable SDInternalCompletionBlock)completedBlock {
|
||||
// Check whether we should download image from network
|
||||
BOOL shouldDownload = (options & SDWebImageFromCacheOnly) == 0;
|
||||
shouldDownload &= (!cachedImage || options & SDWebImageRefreshCached);
|
||||
shouldDownload &= (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url]);
|
||||
shouldDownload &= [self.imageLoader canLoadWithURL:url];
|
||||
if (shouldDownload) {
|
||||
if (cachedImage && options & SDWebImageRefreshCached) {
|
||||
// If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image
|
||||
// AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
|
||||
// Pass the cached image to the image loader. The image loader should check whether the remote image is equal to the cached image.
|
||||
SDWebImageMutableContext *mutableContext;
|
||||
if (context) {
|
||||
mutableContext = [context mutableCopy];
|
||||
} else {
|
||||
mutableContext = [NSMutableDictionary dictionary];
|
||||
}
|
||||
[mutableContext setValue:cachedImage forKey:SDWebImageContextLoaderCachedImage];
|
||||
context = [mutableContext copy];
|
||||
}
|
||||
|
||||
// `SDWebImageCombinedOperation` -> `SDWebImageDownloadToken` -> `downloadOperationCancelToken`, which is a `SDCallbacksDictionary` and retain the completed block below, so we need weak-strong again to avoid retain cycle
|
||||
__weak typeof(operation) weakOperation = operation;
|
||||
operation.downloadOperation = [self.imageLoader loadImageWithURL:url options:options context:context progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {
|
||||
__strong typeof(weakOperation) strongOperation = weakOperation;
|
||||
if (!strongOperation || strongOperation.isCancelled) {
|
||||
// Do nothing if the operation was cancelled
|
||||
// See #699 for more details
|
||||
// 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
|
||||
} else if (cachedImage && options & SDWebImageRefreshCached && [error.domain isEqualToString:SDWebImageErrorDomain] && error.code == SDWebImageErrorCacheNotModified) {
|
||||
// Image refresh hit the NSURLCache cache, do not call the completion block
|
||||
} else if (error) {
|
||||
[self callCompletionBlockForOperation:strongOperation completion:completedBlock error:error url:url];
|
||||
BOOL shouldBlockFailedURL;
|
||||
// Check whether we should block failed url
|
||||
if ([self.delegate respondsToSelector:@selector(imageManager:shouldBlockFailedURL:withError:)]) {
|
||||
shouldBlockFailedURL = [self.delegate imageManager:self shouldBlockFailedURL:url withError:error];
|
||||
} else {
|
||||
shouldBlockFailedURL = ( error.code != NSURLErrorNotConnectedToInternet
|
||||
&& error.code != NSURLErrorCancelled
|
||||
&& error.code != NSURLErrorTimedOut
|
||||
&& error.code != NSURLErrorInternationalRoamingOff
|
||||
&& error.code != NSURLErrorDataNotAllowed
|
||||
&& error.code != NSURLErrorCannotFindHost
|
||||
&& error.code != NSURLErrorCannotConnectToHost
|
||||
&& error.code != NSURLErrorNetworkConnectionLost);
|
||||
}
|
||||
|
||||
if (shouldBlockFailedURL) {
|
||||
@synchronized (self.failedURLs) {
|
||||
[self.failedURLs addObject:url];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ((options & SDWebImageRetryFailed)) {
|
||||
@synchronized (self.failedURLs) {
|
||||
[self.failedURLs removeObject:url];
|
||||
}
|
||||
}
|
||||
|
||||
SDImageCacheType storeCacheType = SDImageCacheTypeAll;
|
||||
if (options & SDWebImageCacheMemoryOnly) {
|
||||
storeCacheType = SDImageCacheTypeMemory;
|
||||
}
|
||||
id<SDWebImageCacheKeyFilter> cacheKeyFilter = [context valueForKey:SDWebImageContextCacheKeyFilter];
|
||||
NSString *key = [self cacheKeyForURL:url cacheKeyFilter:cacheKeyFilter];
|
||||
id<SDWebImageTransformer> transformer = [context valueForKey:SDWebImageContextCustomTransformer];
|
||||
id<SDWebImageCacheSerializer> cacheSerializer = [context valueForKey:SDWebImageContextCacheKeyFilter];
|
||||
if (downloadedImage && (!downloadedImage.sd_isAnimated || (options & SDWebImageTransformAnimatedImage)) && transformer) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
||||
UIImage *transformedImage = [transformer transformedImageWithImage:downloadedImage forKey:key];
|
||||
if (transformedImage && finished) {
|
||||
NSString *transformerKey = [transformer transformerKey];
|
||||
NSString *cacheKey = SDTransformedKeyForKey(key, transformerKey);
|
||||
BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
|
||||
NSData *cacheData;
|
||||
// pass nil if the image was transformed, so we can recalculate the data from the image
|
||||
if (cacheSerializer) {
|
||||
cacheData = [cacheSerializer cacheDataWithImage:transformedImage originalData:(imageWasTransformed ? nil : downloadedData) imageURL:url];
|
||||
} else {
|
||||
cacheData = (imageWasTransformed ? nil : downloadedData);
|
||||
}
|
||||
[self.imageCache storeImage:transformedImage imageData:cacheData forKey:cacheKey cacheType:storeCacheType completion:nil];
|
||||
}
|
||||
|
||||
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
|
||||
});
|
||||
} else {
|
||||
if (downloadedImage && finished) {
|
||||
if (cacheSerializer) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
||||
NSData *cacheData = [cacheSerializer cacheDataWithImage:downloadedImage originalData:downloadedData imageURL:url];
|
||||
[self.imageCache storeImage:downloadedImage imageData:cacheData forKey:key cacheType:storeCacheType completion:nil];
|
||||
});
|
||||
} else {
|
||||
[self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key cacheType:storeCacheType completion:nil];
|
||||
}
|
||||
}
|
||||
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:downloadedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
|
||||
}
|
||||
}
|
||||
|
||||
if (finished) {
|
||||
[self safelyRemoveOperationFromRunning:strongOperation];
|
||||
}
|
||||
}];
|
||||
} else if (cachedImage) {
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
|
||||
[self safelyRemoveOperationFromRunning:operation];
|
||||
} else {
|
||||
// Image not in cache and download disallowed by delegate
|
||||
[self callCompletionBlockForOperation:operation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];
|
||||
[self safelyRemoveOperationFromRunning:operation];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Helper
|
||||
|
||||
- (void)safelyRemoveOperationFromRunning:(nullable SDWebImageCombinedOperation*)operation {
|
||||
@synchronized (self.runningOperations) {
|
||||
if (operation) {
|
||||
|
@ -366,6 +362,33 @@ static id<SDWebImageLoader> _defaultImageLoader;
|
|||
});
|
||||
}
|
||||
|
||||
- (SDWebImageContext *)processedContextWithContext:(SDWebImageContext *)context {
|
||||
SDWebImageMutableContext *mutableContext = [SDWebImageMutableContext dictionary];
|
||||
|
||||
// Image Transformer from manager
|
||||
if (![context valueForKey:SDWebImageContextCustomTransformer]) {
|
||||
id<SDWebImageTransformer> transformer = self.transformer;
|
||||
[mutableContext setValue:transformer forKey:SDWebImageContextCustomTransformer];
|
||||
}
|
||||
// Cache key filter from manager
|
||||
if (![context valueForKey:SDWebImageContextCacheKeyFilter]) {
|
||||
id<SDWebImageCacheKeyFilter> cacheKeyFilter = self.cacheKeyFilter;
|
||||
[mutableContext setValue:cacheKeyFilter forKey:SDWebImageContextCacheKeyFilter];
|
||||
}
|
||||
// Cache serializer from manager
|
||||
if (![context valueForKey:SDWebImageContextCacheSerializer]) {
|
||||
id<SDWebImageCacheSerializer> cacheSerializer = self.cacheSerializer;
|
||||
[mutableContext setValue:cacheSerializer forKey:SDWebImageContextCacheSerializer];
|
||||
}
|
||||
|
||||
if (mutableContext.count == 0) {
|
||||
return context;
|
||||
} else {
|
||||
[mutableContext addEntriesFromDictionary:context];
|
||||
return [mutableContext copy];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue