diff --git a/SDWebImage/SDImageCache.h b/SDWebImage/SDImageCache.h index d3c92c17..6003595a 100644 --- a/SDWebImage/SDImageCache.h +++ b/SDWebImage/SDImageCache.h @@ -171,6 +171,13 @@ typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger tot */ - (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock; +/** + * Sync check if image data exists in disk cache already (does not load the image) + * + * @param key the key describing the url + */ +- (BOOL)diskImageDataExistsWithKey:(nullable NSString *)key; + /** * Operation that queries the cache asynchronously and call the completion when done. * diff --git a/SDWebImage/SDImageCache.m b/SDWebImage/SDImageCache.m index 6b926186..162122fb 100644 --- a/SDWebImage/SDImageCache.m +++ b/SDWebImage/SDImageCache.m @@ -106,14 +106,6 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { [[NSNotificationCenter defaultCenter] removeObserver:self]; } -- (void)checkIfQueueIsIOQueue { - const char *currentQueueLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL); - const char *ioQueueLabel = dispatch_queue_get_label(self.ioQueue); - if (strcmp(currentQueueLabel, ioQueueLabel) != 0) { - NSLog(@"This method should be called from the ioQueue"); - } -} - #pragma mark - Cache paths - (void)addReadOnlyCachePath:(nonnull NSString *)path { @@ -201,7 +193,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { } data = [[SDWebImageCodersManager sharedInstance] encodedDataWithImage:image format:format]; } - [self storeImageDataToDisk:data forKey:key]; + [self _storeImageDataToDisk:data forKey:key]; } if (completionBlock) { @@ -221,8 +213,16 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { if (!imageData || !key) { return; } - - [self checkIfQueueIsIOQueue]; + dispatch_sync(self.ioQueue, ^{ + [self _storeImageDataToDisk:imageData forKey:key]; + }); +} + +// Make sure to call form io queue by caller +- (void)_storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key { + if (!imageData || !key) { + return; + } if (![_fileManager fileExistsAtPath:_diskCachePath]) { [_fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL]; @@ -233,7 +233,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { // transform to NSUrl NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey]; - [_fileManager createFileAtPath:cachePathForKey contents:imageData attributes:nil]; + [imageData writeToURL:fileURL options:self.config.diskCacheWritingOptions error:nil]; // disable iCloud backup if (self.config.shouldDisableiCloud) { @@ -244,15 +244,8 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { #pragma mark - Query and Retrieve Ops - (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock { - dispatch_async(_ioQueue, ^{ - BOOL exists = [_fileManager fileExistsAtPath:[self defaultCachePathForKey:key]]; - - // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name - // checking the key with and without the extension - if (!exists) { - exists = [_fileManager fileExistsAtPath:[self defaultCachePathForKey:key].stringByDeletingPathExtension]; - } - + dispatch_async(self.ioQueue, ^{ + BOOL exists = [self _diskImageDataExistsWithKey:key]; if (completionBlock) { dispatch_async(dispatch_get_main_queue(), ^{ completionBlock(exists); @@ -261,6 +254,34 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { }); } +- (BOOL)diskImageDataExistsWithKey:(nullable NSString *)key { + if (!key) { + return NO; + } + __block BOOL exists = NO; + dispatch_sync(self.ioQueue, ^{ + exists = [self _diskImageDataExistsWithKey:key]; + }); + + return exists; +} + +// Make sure to call form io queue by caller +- (BOOL)_diskImageDataExistsWithKey:(nullable NSString *)key { + if (!key) { + return NO; + } + BOOL exists = [_fileManager fileExistsAtPath:[self defaultCachePathForKey:key]]; + + // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name + // checking the key with and without the extension + if (!exists) { + exists = [_fileManager fileExistsAtPath:[self defaultCachePathForKey:key].stringByDeletingPathExtension]; + } + + return exists; +} + - (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key { return [self.memCache objectForKey:key]; } diff --git a/SDWebImage/SDImageCacheConfig.h b/SDWebImage/SDImageCacheConfig.h index 20f2d108..d3cb5421 100644 --- a/SDWebImage/SDImageCacheConfig.h +++ b/SDWebImage/SDImageCacheConfig.h @@ -29,10 +29,16 @@ /** * The reading options while reading cache from disk. - * Defaults to 0. You can set this to mapped file to improve performance. + * Defaults to 0. You can set this to `NSDataReadingMappedIfSafe` to improve performance. */ @property (assign, nonatomic) NSDataReadingOptions diskCacheReadingOptions; +/** + * The writing options while writing cache to disk. + * Defaults to `NSDataWritingAtomic`. You can set this to `NSDataWritingWithoutOverwriting` to prevent overwriting an existing file. + */ +@property (assign, nonatomic) NSDataWritingOptions diskCacheWritingOptions; + /** * The maximum length of time to keep an image in the cache, in seconds. */ diff --git a/SDWebImage/SDImageCacheConfig.m b/SDWebImage/SDImageCacheConfig.m index 7a5af6cb..923506d0 100644 --- a/SDWebImage/SDImageCacheConfig.m +++ b/SDWebImage/SDImageCacheConfig.m @@ -18,6 +18,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week _shouldDisableiCloud = YES; _shouldCacheImagesInMemory = YES; _diskCacheReadingOptions = 0; + _diskCacheWritingOptions = NSDataWritingAtomic; _maxCacheAge = kDefaultCacheMaxCacheAge; _maxCacheSize = 0; }