Authored by DreamPiggy
Committed by GitHub

Merge pull request #2190 from dreampiggy/fix_check_io_queue

Add sync version API diskImageDataExistsWithKey and keep thread-safe. Add diskCacheWritingOptions
@@ -172,6 +172,13 @@ typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger tot @@ -172,6 +172,13 @@ typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger tot
172 - (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock; 172 - (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
173 173
174 /** 174 /**
  175 + * Sync check if image data exists in disk cache already (does not load the image)
  176 + *
  177 + * @param key the key describing the url
  178 + */
  179 +- (BOOL)diskImageDataExistsWithKey:(nullable NSString *)key;
  180 +
  181 +/**
175 * Operation that queries the cache asynchronously and call the completion when done. 182 * Operation that queries the cache asynchronously and call the completion when done.
176 * 183 *
177 * @param key The unique key used to store the wanted image 184 * @param key The unique key used to store the wanted image
@@ -106,14 +106,6 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { @@ -106,14 +106,6 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
106 [[NSNotificationCenter defaultCenter] removeObserver:self]; 106 [[NSNotificationCenter defaultCenter] removeObserver:self];
107 } 107 }
108 108
109 -- (void)checkIfQueueIsIOQueue {  
110 - const char *currentQueueLabel = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);  
111 - const char *ioQueueLabel = dispatch_queue_get_label(self.ioQueue);  
112 - if (strcmp(currentQueueLabel, ioQueueLabel) != 0) {  
113 - NSLog(@"This method should be called from the ioQueue");  
114 - }  
115 -}  
116 -  
117 #pragma mark - Cache paths 109 #pragma mark - Cache paths
118 110
119 - (void)addReadOnlyCachePath:(nonnull NSString *)path { 111 - (void)addReadOnlyCachePath:(nonnull NSString *)path {
@@ -201,7 +193,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { @@ -201,7 +193,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
201 } 193 }
202 data = [[SDWebImageCodersManager sharedInstance] encodedDataWithImage:image format:format]; 194 data = [[SDWebImageCodersManager sharedInstance] encodedDataWithImage:image format:format];
203 } 195 }
204 - [self storeImageDataToDisk:data forKey:key]; 196 + [self _storeImageDataToDisk:data forKey:key];
205 } 197 }
206 198
207 if (completionBlock) { 199 if (completionBlock) {
@@ -221,8 +213,16 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { @@ -221,8 +213,16 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
221 if (!imageData || !key) { 213 if (!imageData || !key) {
222 return; 214 return;
223 } 215 }
224 -  
225 - [self checkIfQueueIsIOQueue]; 216 + dispatch_sync(self.ioQueue, ^{
  217 + [self _storeImageDataToDisk:imageData forKey:key];
  218 + });
  219 +}
  220 +
  221 +// Make sure to call form io queue by caller
  222 +- (void)_storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key {
  223 + if (!imageData || !key) {
  224 + return;
  225 + }
226 226
227 if (![_fileManager fileExistsAtPath:_diskCachePath]) { 227 if (![_fileManager fileExistsAtPath:_diskCachePath]) {
228 [_fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL]; 228 [_fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
@@ -233,7 +233,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { @@ -233,7 +233,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
233 // transform to NSUrl 233 // transform to NSUrl
234 NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey]; 234 NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];
235 235
236 - [_fileManager createFileAtPath:cachePathForKey contents:imageData attributes:nil]; 236 + [imageData writeToURL:fileURL options:self.config.diskCacheWritingOptions error:nil];
237 237
238 // disable iCloud backup 238 // disable iCloud backup
239 if (self.config.shouldDisableiCloud) { 239 if (self.config.shouldDisableiCloud) {
@@ -244,15 +244,8 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { @@ -244,15 +244,8 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
244 #pragma mark - Query and Retrieve Ops 244 #pragma mark - Query and Retrieve Ops
245 245
246 - (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock { 246 - (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock {
247 - dispatch_async(_ioQueue, ^{  
248 - BOOL exists = [_fileManager fileExistsAtPath:[self defaultCachePathForKey:key]];  
249 -  
250 - // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name  
251 - // checking the key with and without the extension  
252 - if (!exists) {  
253 - exists = [_fileManager fileExistsAtPath:[self defaultCachePathForKey:key].stringByDeletingPathExtension];  
254 - }  
255 - 247 + dispatch_async(self.ioQueue, ^{
  248 + BOOL exists = [self _diskImageDataExistsWithKey:key];
256 if (completionBlock) { 249 if (completionBlock) {
257 dispatch_async(dispatch_get_main_queue(), ^{ 250 dispatch_async(dispatch_get_main_queue(), ^{
258 completionBlock(exists); 251 completionBlock(exists);
@@ -261,6 +254,34 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) { @@ -261,6 +254,34 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
261 }); 254 });
262 } 255 }
263 256
  257 +- (BOOL)diskImageDataExistsWithKey:(nullable NSString *)key {
  258 + if (!key) {
  259 + return NO;
  260 + }
  261 + __block BOOL exists = NO;
  262 + dispatch_sync(self.ioQueue, ^{
  263 + exists = [self _diskImageDataExistsWithKey:key];
  264 + });
  265 +
  266 + return exists;
  267 +}
  268 +
  269 +// Make sure to call form io queue by caller
  270 +- (BOOL)_diskImageDataExistsWithKey:(nullable NSString *)key {
  271 + if (!key) {
  272 + return NO;
  273 + }
  274 + BOOL exists = [_fileManager fileExistsAtPath:[self defaultCachePathForKey:key]];
  275 +
  276 + // fallback because of https://github.com/rs/SDWebImage/pull/976 that added the extension to the disk file name
  277 + // checking the key with and without the extension
  278 + if (!exists) {
  279 + exists = [_fileManager fileExistsAtPath:[self defaultCachePathForKey:key].stringByDeletingPathExtension];
  280 + }
  281 +
  282 + return exists;
  283 +}
  284 +
264 - (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key { 285 - (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key {
265 return [self.memCache objectForKey:key]; 286 return [self.memCache objectForKey:key];
266 } 287 }
@@ -29,11 +29,17 @@ @@ -29,11 +29,17 @@
29 29
30 /** 30 /**
31 * The reading options while reading cache from disk. 31 * The reading options while reading cache from disk.
32 - * Defaults to 0. You can set this to mapped file to improve performance. 32 + * Defaults to 0. You can set this to `NSDataReadingMappedIfSafe` to improve performance.
33 */ 33 */
34 @property (assign, nonatomic) NSDataReadingOptions diskCacheReadingOptions; 34 @property (assign, nonatomic) NSDataReadingOptions diskCacheReadingOptions;
35 35
36 /** 36 /**
  37 + * The writing options while writing cache to disk.
  38 + * Defaults to `NSDataWritingAtomic`. You can set this to `NSDataWritingWithoutOverwriting` to prevent overwriting an existing file.
  39 + */
  40 +@property (assign, nonatomic) NSDataWritingOptions diskCacheWritingOptions;
  41 +
  42 +/**
37 * The maximum length of time to keep an image in the cache, in seconds. 43 * The maximum length of time to keep an image in the cache, in seconds.
38 */ 44 */
39 @property (assign, nonatomic) NSInteger maxCacheAge; 45 @property (assign, nonatomic) NSInteger maxCacheAge;
@@ -18,6 +18,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week @@ -18,6 +18,7 @@ static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
18 _shouldDisableiCloud = YES; 18 _shouldDisableiCloud = YES;
19 _shouldCacheImagesInMemory = YES; 19 _shouldCacheImagesInMemory = YES;
20 _diskCacheReadingOptions = 0; 20 _diskCacheReadingOptions = 0;
  21 + _diskCacheWritingOptions = NSDataWritingAtomic;
21 _maxCacheAge = kDefaultCacheMaxCacheAge; 22 _maxCacheAge = kDefaultCacheMaxCacheAge;
22 _maxCacheSize = 0; 23 _maxCacheSize = 0;
23 } 24 }