Skip to content

Commit 6e4fbaf

Browse files
committed
#625 In order to fix the deadlock, reviewed the [SDImageCache diskImageExistsWithKey:] method. Based on the Apple doc for NSFileManager, using the defaultManager without the dispatch on the ioQueue to avoid the deadlocks. This instance is thread safe. Also created an async variant of this method [SDImageCache diskImageExistsWithKey:completion:]
For consistency, added async methods in `SDWebImageManager` `cachedImageExistsForURL:completion:` and `diskImageExistsForURL:completion:`
1 parent 0b47342 commit 6e4fbaf

File tree

4 files changed

+105
-7
lines changed

4 files changed

+105
-7
lines changed

SDWebImage/SDImageCache.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ typedef NS_ENUM(NSInteger, SDImageCacheType) {
2626

2727
typedef void(^SDWebImageQueryCompletedBlock)(UIImage *image, SDImageCacheType cacheType);
2828

29+
typedef void(^SDWebImageCheckCacheCompletionBlock)(BOOL isInCache);
30+
2931
/**
3032
* SDImageCache maintains a memory cache and an optional disk cache. Disk cache write operations are performed
3133
* asynchronous so it doesn’t add unnecessary latency to the UI.
@@ -198,7 +200,20 @@ typedef void(^SDWebImageQueryCompletedBlock)(UIImage *image, SDImageCacheType ca
198200
- (void)calculateSizeWithCompletionBlock:(void (^)(NSUInteger fileCount, NSUInteger totalSize))completionBlock;
199201

200202
/**
201-
* Check if image exists in cache already
203+
* Async check if image exists in disk cache already (does not load the image)
204+
*
205+
* @param key the key describing the url
206+
* @param completionBlock the block to be executed when the check is done.
207+
* @note the completion block will be always executed on the main queue
208+
*/
209+
- (void)diskImageExistsWithKey:(NSString *)key completion:(SDWebImageCheckCacheCompletionBlock)completionBlock;
210+
211+
/**
212+
* Check if image exists in disk cache already (does not load the image)
213+
*
214+
* @param key the key describing the url
215+
*
216+
* @return YES if an image exists for the given key
202217
*/
203218
- (BOOL)diskImageExistsWithKey:(NSString *)key;
204219

SDWebImage/SDImageCache.m

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,14 +200,26 @@ - (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk {
200200
}
201201

202202
- (BOOL)diskImageExistsWithKey:(NSString *)key {
203-
__block BOOL exists = NO;
204-
dispatch_sync(_ioQueue, ^{
205-
exists = [_fileManager fileExistsAtPath:[self defaultCachePathForKey:key]];
206-
});
207-
203+
BOOL exists = NO;
204+
205+
// this is an exception to access the filemanager on another queue than ioQueue, but we are using the shared instance
206+
// from apple docs on NSFileManager: The methods of the shared NSFileManager object can be called from multiple threads safely.
207+
exists = [[NSFileManager defaultManager] fileExistsAtPath:[self defaultCachePathForKey:key]];
208+
208209
return exists;
209210
}
210211

212+
- (void)diskImageExistsWithKey:(NSString *)key completion:(SDWebImageCheckCacheCompletionBlock)completionBlock {
213+
dispatch_async(_ioQueue, ^{
214+
BOOL exists = [_fileManager fileExistsAtPath:[self defaultCachePathForKey:key]];
215+
if (completionBlock) {
216+
dispatch_async(dispatch_get_main_queue(), ^{
217+
completionBlock(exists);
218+
});
219+
}
220+
});
221+
}
222+
211223
- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key {
212224
return [self.memCache objectForKey:key];
213225
}

SDWebImage/SDWebImageManager.h

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,11 +214,46 @@ SDWebImageManager *manager = [SDWebImageManager sharedManager];
214214
- (BOOL)isRunning;
215215

216216
/**
217-
* Check if image has already been cached
217+
* Check if image has already been cached
218+
*
219+
* @param url image url
220+
*
221+
* @return if the image was already cached
218222
*/
219223
- (BOOL)cachedImageExistsForURL:(NSURL *)url;
224+
225+
/**
226+
* Check if image has already been cached on disk only
227+
*
228+
* @param url image url
229+
*
230+
* @return if the image was already cached (disk only)
231+
*/
220232
- (BOOL)diskImageExistsForURL:(NSURL *)url;
221233

234+
/**
235+
* Async check if image has already been cached
236+
*
237+
* @param url image url
238+
* @param completionBlock the block to be executed when the check is finished
239+
*
240+
* @note the completion block is always executed on the main queue
241+
*/
242+
- (void)cachedImageExistsForURL:(NSURL *)url
243+
completion:(SDWebImageCheckCacheCompletionBlock)completionBlock;
244+
245+
/**
246+
* Async check if image has already been cached on disk only
247+
*
248+
* @param url image url
249+
* @param completionBlock the block to be executed when the check is finished
250+
*
251+
* @note the completion block is always executed on the main queue
252+
*/
253+
- (void)diskImageExistsForURL:(NSURL *)url
254+
completion:(SDWebImageCheckCacheCompletionBlock)completionBlock;
255+
256+
222257
/**
223258
*Return the cache key for a given URL
224259
*/

SDWebImage/SDWebImageManager.m

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,42 @@ - (BOOL)diskImageExistsForURL:(NSURL *)url {
7171
return [self.imageCache diskImageExistsWithKey:key];
7272
}
7373

74+
- (void)cachedImageExistsForURL:(NSURL *)url
75+
completion:(SDWebImageCheckCacheCompletionBlock)completionBlock {
76+
NSString *key = [self cacheKeyForURL:url];
77+
78+
BOOL isInMemoryCache = ([self.imageCache imageFromMemoryCacheForKey:key] != nil);
79+
80+
if (isInMemoryCache) {
81+
// making sure we call the completion block on the main queue
82+
dispatch_async(dispatch_get_main_queue(), ^{
83+
if (completionBlock) {
84+
completionBlock(YES);
85+
}
86+
});
87+
return;
88+
}
89+
90+
[self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) {
91+
// the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch
92+
if (completionBlock) {
93+
completionBlock(isInDiskCache);
94+
}
95+
}];
96+
}
97+
98+
- (void)diskImageExistsForURL:(NSURL *)url
99+
completion:(SDWebImageCheckCacheCompletionBlock)completionBlock {
100+
NSString *key = [self cacheKeyForURL:url];
101+
102+
[self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) {
103+
// the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch
104+
if (completionBlock) {
105+
completionBlock(isInDiskCache);
106+
}
107+
}];
108+
}
109+
74110
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
75111
options:(SDWebImageOptions)options
76112
progress:(SDWebImageDownloaderProgressBlock)progressBlock

0 commit comments

Comments
 (0)