From bb41dbc046eed4c122b8d137aa0a6946656f39d2 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 18 Jul 2018 12:49:22 +0800 Subject: [PATCH 1/3] Keep the information about image's original compressed format, for API which only contains image but not image data to process extra logic --- SDWebImage/NSData+ImageContentType.h | 16 ++++++++++++---- SDWebImage/NSData+ImageContentType.m | 23 +++++++++++++++++++++++ SDWebImage/SDWebImageCodersManager.m | 5 ++++- SDWebImage/SDWebImageCompat.m | 2 ++ SDWebImage/SDWebImageGIFCoder.m | 1 + SDWebImage/SDWebImageImageIOCoder.m | 3 +++ SDWebImage/SDWebImageWebPCoder.m | 3 +++ SDWebImage/UIImage+MultiFormat.h | 9 ++++++++- SDWebImage/UIImage+MultiFormat.m | 26 ++++++++++++++++++++++++-- 9 files changed, 80 insertions(+), 8 deletions(-) diff --git a/SDWebImage/NSData+ImageContentType.h b/SDWebImage/NSData+ImageContentType.h index 0ca226d8..d7e21428 100644 --- a/SDWebImage/NSData+ImageContentType.h +++ b/SDWebImage/NSData+ImageContentType.h @@ -32,11 +32,19 @@ typedef NS_ENUM(NSInteger, SDImageFormat) { + (SDImageFormat)sd_imageFormatForImageData:(nullable NSData *)data; /** - Convert SDImageFormat to UTType - - @param format Format as SDImageFormat - @return The UTType as CFStringRef + * Convert SDImageFormat to UTType + * + * @param format Format as SDImageFormat + * @return The UTType as CFStringRef */ + (nonnull CFStringRef)sd_UTTypeFromSDImageFormat:(SDImageFormat)format; +/** + * Convert UTTyppe to SDImageFormat + * + * @param uttype The UTType as CFStringRef + * @return The Format as SDImageFormat + */ ++ (SDImageFormat)sd_imageFormatFromUTType:(nonnull CFStringRef)uttype; + @end diff --git a/SDWebImage/NSData+ImageContentType.m b/SDWebImage/NSData+ImageContentType.m index 6a590611..64ea0a30 100644 --- a/SDWebImage/NSData+ImageContentType.m +++ b/SDWebImage/NSData+ImageContentType.m @@ -95,4 +95,27 @@ return UTType; } ++ (SDImageFormat)sd_imageFormatFromUTType:(CFStringRef)uttype { + if (!uttype) { + return SDImageFormatUndefined; + } + SDImageFormat imageFormat; + if (CFStringCompare(uttype, kUTTypeJPEG, 0) == kCFCompareEqualTo) { + imageFormat = SDImageFormatJPEG; + } else if (CFStringCompare(uttype, kUTTypePNG, 0) == kCFCompareEqualTo) { + imageFormat = SDImageFormatPNG; + } else if (CFStringCompare(uttype, kUTTypeGIF, 0) == kCFCompareEqualTo) { + imageFormat = SDImageFormatGIF; + } else if (CFStringCompare(uttype, kUTTypeTIFF, 0) == kCFCompareEqualTo) { + imageFormat = SDImageFormatTIFF; + } else if (CFStringCompare(uttype, kSDUTTypeWebP, 0) == kCFCompareEqualTo) { + imageFormat = SDImageFormatWebP; + } else if (CFStringCompare(uttype, kSDUTTypeHEIC, 0) == kCFCompareEqualTo) { + imageFormat = SDImageFormatHEIC; + } else { + imageFormat = SDImageFormatUndefined; + } + return imageFormat; +} + @end diff --git a/SDWebImage/SDWebImageCodersManager.m b/SDWebImage/SDWebImageCodersManager.m index 79c3bff5..883d1fdb 100644 --- a/SDWebImage/SDWebImageCodersManager.m +++ b/SDWebImage/SDWebImageCodersManager.m @@ -12,6 +12,7 @@ #ifdef SD_WEBP #import "SDWebImageWebPCoder.h" #endif +#import "UIImage+MultiFormat.h" #define LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); #define UNLOCK(lock) dispatch_semaphore_signal(lock); @@ -121,7 +122,9 @@ UNLOCK(self.codersLock); for (id coder in coders.reverseObjectEnumerator) { if ([coder canDecodeFromData:*data]) { - return [coder decompressedImageWithImage:image data:data options:optionsDict]; + UIImage *decompressedImage = [coder decompressedImageWithImage:image data:data options:optionsDict]; + decompressedImage.sd_imageFormat = image.sd_imageFormat; + return decompressedImage; } } return nil; diff --git a/SDWebImage/SDWebImageCompat.m b/SDWebImage/SDWebImageCompat.m index bb53495e..daee7fc9 100644 --- a/SDWebImage/SDWebImageCompat.m +++ b/SDWebImage/SDWebImageCompat.m @@ -35,6 +35,7 @@ inline UIImage *SDScaledImageForKey(NSString * _Nullable key, UIImage * _Nullabl UIImage *animatedImage = [UIImage animatedImageWithImages:scaledImages duration:image.duration]; if (animatedImage) { animatedImage.sd_imageLoopCount = image.sd_imageLoopCount; + animatedImage.sd_imageFormat = image.sd_imageFormat; } return animatedImage; } else { @@ -57,6 +58,7 @@ inline UIImage *SDScaledImageForKey(NSString * _Nullable key, UIImage * _Nullabl } UIImage *scaledImage = [[UIImage alloc] initWithCGImage:image.CGImage scale:scale orientation:image.imageOrientation]; + scaledImage.sd_imageFormat = image.sd_imageFormat; image = scaledImage; } return image; diff --git a/SDWebImage/SDWebImageGIFCoder.m b/SDWebImage/SDWebImageGIFCoder.m index b9ebf1a5..6707329b 100644 --- a/SDWebImage/SDWebImageGIFCoder.m +++ b/SDWebImage/SDWebImageGIFCoder.m @@ -81,6 +81,7 @@ animatedImage = [SDWebImageCoderHelper animatedImageWithFrames:frames]; animatedImage.sd_imageLoopCount = loopCount; + animatedImage.sd_imageFormat = SDImageFormatGIF; } CFRelease(source); diff --git a/SDWebImage/SDWebImageImageIOCoder.m b/SDWebImage/SDWebImageImageIOCoder.m index 0447ed3c..5f9d1118 100644 --- a/SDWebImage/SDWebImageImageIOCoder.m +++ b/SDWebImage/SDWebImageImageIOCoder.m @@ -11,6 +11,7 @@ #import "NSImage+WebCache.h" #import #import "NSData+ImageContentType.h" +#import "UIImage+MultiFormat.h" #if SD_UIKIT || SD_WATCH static const size_t kBytesPerPixel = 4; @@ -97,6 +98,7 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over } UIImage *image = [[UIImage alloc] initWithData:data]; + image.sd_imageFormat = [NSData sd_imageFormatForImageData:data]; return image; } @@ -146,6 +148,7 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over image = [[UIImage alloc] initWithCGImage:partialImageRef size:NSZeroSize]; #endif CGImageRelease(partialImageRef); + image.sd_imageFormat = [NSData sd_imageFormatForImageData:data]; } } diff --git a/SDWebImage/SDWebImageWebPCoder.m b/SDWebImage/SDWebImageWebPCoder.m index a57a4bad..7971d231 100644 --- a/SDWebImage/SDWebImageWebPCoder.m +++ b/SDWebImage/SDWebImageWebPCoder.m @@ -106,6 +106,7 @@ } WebPDemuxDelete(demuxer); CGContextRelease(canvas); + staticImage.sd_imageFormat = SDImageFormatWebP; return staticImage; } @@ -145,6 +146,7 @@ UIImage *animatedImage = [SDWebImageCoderHelper animatedImageWithFrames:frames]; animatedImage.sd_imageLoopCount = loopCount; + animatedImage.sd_imageFormat = SDImageFormatWebP; return animatedImage; } @@ -215,6 +217,7 @@ #else image = [[UIImage alloc] initWithCGImage:newImageRef size:NSZeroSize]; #endif + image.sd_imageFormat = SDImageFormatWebP; CGImageRelease(newImageRef); CGContextRelease(canvas); } diff --git a/SDWebImage/UIImage+MultiFormat.h b/SDWebImage/UIImage+MultiFormat.h index c0792d1b..5c6f473c 100644 --- a/SDWebImage/UIImage+MultiFormat.h +++ b/SDWebImage/UIImage+MultiFormat.h @@ -15,7 +15,7 @@ * UIKit: * For static image format, this value is always 0. * For animated image format, 0 means infinite looping. - * Note that because of the limitations of categories this property can get out of sync if you create another instance with CGImage or other methods. + * @note Note that because of the limitations of categories this property can get out of sync if you create another instance with CGImage or other methods. * AppKit: * NSImage currently only support animated via GIF imageRep unlike UIImage. * The getter of this property will get the loop count from GIF imageRep @@ -23,6 +23,13 @@ */ @property (nonatomic, assign) NSUInteger sd_imageLoopCount; +/** + * The image format represent the original compressed image data format. + * If you don't manually specify a format, this information is retrieve from CGImage using `CGImageGetUTType`, which may return nil for non-CG based image. At this time it will return `SDImageFormatUndefined` as default value. + * @note Note that because of the limitations of categories this property can get out of sync if you create another instance with CGImage or other methods. + */ +@property (nonatomic, assign) SDImageFormat sd_imageFormat; + + (nullable UIImage *)sd_imageWithData:(nullable NSData *)data; - (nullable NSData *)sd_imageData; - (nullable NSData *)sd_imageDataAsFormat:(SDImageFormat)imageFormat; diff --git a/SDWebImage/UIImage+MultiFormat.m b/SDWebImage/UIImage+MultiFormat.m index 664e0969..d3198d2d 100644 --- a/SDWebImage/UIImage+MultiFormat.m +++ b/SDWebImage/UIImage+MultiFormat.m @@ -7,9 +7,9 @@ */ #import "UIImage+MultiFormat.h" - -#import "objc/runtime.h" +#import "NSImage+WebCache.h" #import "SDWebImageCodersManager.h" +#import "objc/runtime.h" @implementation UIImage (MultiFormat) @@ -53,6 +53,28 @@ } #endif +- (SDImageFormat)sd_imageFormat { + SDImageFormat imageFormat = SDImageFormatUndefined; + NSNumber *value = objc_getAssociatedObject(self, @selector(sd_imageFormat)); + if ([value isKindOfClass:[NSNumber class]]) { + imageFormat = value.integerValue; + return imageFormat; + } + // Check CGImage's UTType, may return nil for non-Image/IO based image +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" + if (&CGImageGetUTType != NULL) { + CFStringRef uttype = CGImageGetUTType(self.CGImage); + imageFormat = [NSData sd_imageFormatFromUTType:uttype]; + } +#pragma clang diagnostic pop + return imageFormat; +} + +- (void)setSd_imageFormat:(SDImageFormat)sd_imageFormat { + objc_setAssociatedObject(self, @selector(sd_imageFormat), @(sd_imageFormat), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + + (nullable UIImage *)sd_imageWithData:(nullable NSData *)data { return [[SDWebImageCodersManager sharedInstance] decodedImageWithData:data]; } From 5e5258594404c8b1b92fda051f43e79677f65b9b Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 18 Jul 2018 13:05:35 +0800 Subject: [PATCH 2/3] Add one sync API for SDImageCache to directly get the image data from disk instead of image instance --- SDWebImage/SDImageCache.h | 11 +++++++++++ SDWebImage/SDImageCache.m | 14 +++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/SDWebImage/SDImageCache.h b/SDWebImage/SDImageCache.h index 9b07f47c..ce259897 100644 --- a/SDWebImage/SDImageCache.h +++ b/SDWebImage/SDImageCache.h @@ -178,6 +178,14 @@ typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger tot */ - (BOOL)diskImageDataExistsWithKey:(nullable NSString *)key; +/** + * Query the image data for the given key synchronously. + * + * @param key The unique key used to store the wanted image + * @return The image data for the given key, or nil if not found. + */ +- (nullable NSData *)diskImageDataForKey:(nullable NSString *)key; + /** * Operation that queries the cache asynchronously and call the completion when done. * @@ -203,6 +211,7 @@ typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger tot * Query the memory cache synchronously. * * @param key The unique key used to store the image + * @return The image for the given key, or nil if not found. */ - (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key; @@ -210,6 +219,7 @@ typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger tot * Query the disk cache synchronously. * * @param key The unique key used to store the image + * @return The image for the given key, or nil if not found. */ - (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key; @@ -217,6 +227,7 @@ typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger tot * Query the cache (memory and or disk) synchronously after checking the memory cache. * * @param key The unique key used to store the image + * @return The image for the given key, or nil if not found. */ - (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key; diff --git a/SDWebImage/SDImageCache.m b/SDWebImage/SDImageCache.m index 33592b14..8e116d18 100644 --- a/SDWebImage/SDImageCache.m +++ b/SDWebImage/SDImageCache.m @@ -399,6 +399,18 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { return exists; } +- (nullable NSData *)diskImageDataForKey:(nullable NSString *)key { + if (!key) { + return nil; + } + __block NSData *imageData = nil; + dispatch_sync(self.ioQueue, ^{ + imageData = [self diskImageDataBySearchingAllPathsForKey:key]; + }); + + return imageData; +} + - (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key { return [self.memCache objectForKey:key]; } @@ -459,7 +471,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { } - (nullable UIImage *)diskImageForKey:(nullable NSString *)key { - NSData *data = [self diskImageDataBySearchingAllPathsForKey:key]; + NSData *data = [self diskImageDataForKey:key]; return [self diskImageForKey:key data:data]; } From 3863052094e82d41997cf6a6420d6cbf62ca8631 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 18 Jul 2018 17:32:56 +0800 Subject: [PATCH 3/3] Fix that FLAnimatedImageView+WebCache display behavior for GIF images and other images format Fix FLAnimatedImageView will dispatch the completion block into next runloop even for non-GIF image Fix FLAnimatedImageView may not show animation even when the disk image data is GIF image --- .../FLAnimatedImageView+WebCache.m | 88 ++++++++++--------- SDWebImage/UIView+WebCache.h | 2 +- SDWebImage/UIView+WebCache.m | 30 ++----- 3 files changed, 53 insertions(+), 67 deletions(-) diff --git a/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.m b/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.m index 8af7a0eb..5fac836b 100644 --- a/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.m +++ b/SDWebImage/FLAnimatedImage/FLAnimatedImageView+WebCache.m @@ -14,6 +14,21 @@ #import "UIView+WebCache.h" #import "NSData+ImageContentType.h" #import "UIImageView+WebCache.h" +#import "UIImage+MultiFormat.h" + +static inline FLAnimatedImage * SDWebImageCreateFLAnimatedImage(FLAnimatedImageView *imageView, NSData *imageData) { + if ([NSData sd_imageFormatForImageData:imageData] != SDImageFormatGIF) { + return nil; + } + FLAnimatedImage *animatedImage; + // Compatibility in 4.x for lower version FLAnimatedImage. + if ([FLAnimatedImage respondsToSelector:@selector(initWithAnimatedGIFData:optimalFrameCacheSize:predrawingEnabled:)]) { + animatedImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:imageData optimalFrameCacheSize:imageView.sd_optimalFrameCacheSize predrawingEnabled:imageView.sd_predrawingEnabled]; + } else { + animatedImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:imageData]; + } + return animatedImage; +} @implementation UIImage (FLAnimatedImage) @@ -98,61 +113,52 @@ options:(SDWebImageOptions)options progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDExternalCompletionBlock)completedBlock { - dispatch_group_t group = dispatch_group_create(); __weak typeof(self)weakSelf = self; [self sd_internalSetImageWithURL:url placeholderImage:placeholder options:options operationKey:nil setImageBlock:^(UIImage *image, NSData *imageData) { - // We could not directlly create the animated image on bacakground queue because it's time consuming, by the time we set it back, the current runloop has passed and the placeholder has been rendered and then replaced with animated image, this cause a flashing. - // Previously we use a trick to firstly set the static poster image, then set animated image back to avoid flashing, but this trick fail when using with custom UIView transition. Core Animation will use the current layer state to do rendering, so even we later set it back, the transition will not update. (it's recommended to use `SDWebImageTransition` instead) - // So we have no choice to force store the FLAnimatedImage into memory cache using a associated object binding to UIImage instance. This consumed memory is adoptable and much smaller than `_UIAnimatedImage` for big GIF + __strong typeof(weakSelf)strongSelf = weakSelf; + if (!strongSelf) { + return; + } + // Step 1. Check memory cache (associate object) FLAnimatedImage *associatedAnimatedImage = image.sd_FLAnimatedImage; if (associatedAnimatedImage) { // Asscociated animated image exist - weakSelf.animatedImage = associatedAnimatedImage; - weakSelf.image = nil; - if (group) { - dispatch_group_leave(group); + strongSelf.animatedImage = associatedAnimatedImage; + strongSelf.image = nil; + return; + } + // Step 2. Check if original compressed image data is "GIF" + BOOL isGIF = (image.sd_imageFormat == SDImageFormatGIF || [NSData sd_imageFormatForImageData:imageData] == SDImageFormatGIF); + if (!isGIF) { + strongSelf.image = image; + strongSelf.animatedImage = nil; + return; + } + // Step 3. Check if data exist or query disk cache + if (!imageData) { + NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:url]; + imageData = [[SDImageCache sharedImageCache] diskImageDataForKey:key]; + } + // Step 4. Create FLAnimatedImage + FLAnimatedImage *animatedImage = SDWebImageCreateFLAnimatedImage(strongSelf, imageData); + // Step 5. Set animatedImage or normal image + if (animatedImage) { + if (strongSelf.sd_cacheFLAnimatedImage) { + image.sd_FLAnimatedImage = animatedImage; } - } else if ([NSData sd_imageFormatForImageData:imageData] == SDImageFormatGIF) { - // Firstly set the static poster image to avoid flashing - UIImage *posterImage = image.images ? image.images.firstObject : image; - weakSelf.image = posterImage; - weakSelf.animatedImage = nil; - // Secondly create FLAnimatedImage in global queue because it's time consuming, then set it back - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ - FLAnimatedImage *animatedImage; - // Compatibility in 4.x for lower version FLAnimatedImage. - if ([FLAnimatedImage respondsToSelector:@selector(initWithAnimatedGIFData:optimalFrameCacheSize:predrawingEnabled:)]) { - animatedImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:imageData optimalFrameCacheSize:weakSelf.sd_optimalFrameCacheSize predrawingEnabled:weakSelf.sd_predrawingEnabled]; - } else { - animatedImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:imageData]; - } - dispatch_async(dispatch_get_main_queue(), ^{ - if (weakSelf.sd_cacheFLAnimatedImage) { - image.sd_FLAnimatedImage = animatedImage; - } - weakSelf.animatedImage = animatedImage; - weakSelf.image = nil; - if (group) { - dispatch_group_leave(group); - } - }); - }); + strongSelf.animatedImage = animatedImage; + strongSelf.image = nil; } else { - // Not animated image - weakSelf.image = image; - weakSelf.animatedImage = nil; - if (group) { - dispatch_group_leave(group); - } + strongSelf.animatedImage = nil; + strongSelf.image = image; } } progress:progressBlock - completed:completedBlock - context:group ? @{SDWebImageInternalSetImageGroupKey : group} : nil]; + completed:completedBlock]; } @end diff --git a/SDWebImage/UIView+WebCache.h b/SDWebImage/UIView+WebCache.h index 85cb2c6d..026f1b3c 100644 --- a/SDWebImage/UIView+WebCache.h +++ b/SDWebImage/UIView+WebCache.h @@ -13,7 +13,7 @@ /** A Dispatch group to maintain setImageBlock and completionBlock. This key should be used only internally and may be changed in the future. (dispatch_group_t) */ -FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageInternalSetImageGroupKey; +FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageInternalSetImageGroupKey __deprecated_msg("Key Deprecated. Does nothing. This key should be used only internally"); /** A SDWebImageManager instance to control the image download and cache process using in UIImageView+WebCache category and likes. If not provided, use the shared manager (SDWebImageManager) */ diff --git a/SDWebImage/UIView+WebCache.m b/SDWebImage/UIView+WebCache.m index 8193cda6..953415b3 100644 --- a/SDWebImage/UIView+WebCache.m +++ b/SDWebImage/UIView+WebCache.m @@ -65,10 +65,6 @@ static char TAG_ACTIVITY_SHOW; objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); if (!(options & SDWebImageDelayPlaceholder)) { - if ([context valueForKey:SDWebImageInternalSetImageGroupKey]) { - dispatch_group_t group = [context valueForKey:SDWebImageInternalSetImageGroupKey]; - dispatch_group_enter(group); - } dispatch_main_async_safe(^{ [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock]; }); @@ -152,30 +148,14 @@ static char TAG_ACTIVITY_SHOW; transition = sself.sd_imageTransition; } #endif - if ([context valueForKey:SDWebImageInternalSetImageGroupKey]) { - dispatch_group_t group = [context valueForKey:SDWebImageInternalSetImageGroupKey]; - dispatch_group_enter(group); - dispatch_main_async_safe(^{ + dispatch_main_async_safe(^{ #if SD_UIKIT || SD_MAC - [sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:transition cacheType:cacheType imageURL:imageURL]; + [sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:transition cacheType:cacheType imageURL:imageURL]; #else - [sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock]; + [sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock]; #endif - }); - // ensure completion block is called after custom setImage process finish - dispatch_group_notify(group, dispatch_get_main_queue(), ^{ - callCompletedBlockClojure(); - }); - } else { - dispatch_main_async_safe(^{ -#if SD_UIKIT || SD_MAC - [sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:transition cacheType:cacheType imageURL:imageURL]; -#else - [sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock]; -#endif - callCompletedBlockClojure(); - }); - } + callCompletedBlockClojure(); + }); }]; [self sd_setImageLoadOperation:operation forKey:validOperationKey]; } else {