#625 In order to fix the deadlock, reviewed the `[SDImageCache diskImageExistsWi…
…thKey:]` 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:`
Showing
4 changed files
with
105 additions
and
7 deletions
@@ -26,6 +26,8 @@ typedef NS_ENUM(NSInteger, SDImageCacheType) { | @@ -26,6 +26,8 @@ typedef NS_ENUM(NSInteger, SDImageCacheType) { | ||
26 | 26 | ||
27 | typedef void(^SDWebImageQueryCompletedBlock)(UIImage *image, SDImageCacheType cacheType); | 27 | typedef void(^SDWebImageQueryCompletedBlock)(UIImage *image, SDImageCacheType cacheType); |
28 | 28 | ||
29 | +typedef void(^SDWebImageCheckCacheCompletionBlock)(BOOL isInCache); | ||
30 | + | ||
29 | /** | 31 | /** |
30 | * SDImageCache maintains a memory cache and an optional disk cache. Disk cache write operations are performed | 32 | * SDImageCache maintains a memory cache and an optional disk cache. Disk cache write operations are performed |
31 | * asynchronous so it doesn’t add unnecessary latency to the UI. | 33 | * asynchronous so it doesn’t add unnecessary latency to the UI. |
@@ -198,7 +200,20 @@ typedef void(^SDWebImageQueryCompletedBlock)(UIImage *image, SDImageCacheType ca | @@ -198,7 +200,20 @@ typedef void(^SDWebImageQueryCompletedBlock)(UIImage *image, SDImageCacheType ca | ||
198 | - (void)calculateSizeWithCompletionBlock:(void (^)(NSUInteger fileCount, NSUInteger totalSize))completionBlock; | 200 | - (void)calculateSizeWithCompletionBlock:(void (^)(NSUInteger fileCount, NSUInteger totalSize))completionBlock; |
199 | 201 | ||
200 | /** | 202 | /** |
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 | ||
202 | */ | 217 | */ |
203 | - (BOOL)diskImageExistsWithKey:(NSString *)key; | 218 | - (BOOL)diskImageExistsWithKey:(NSString *)key; |
204 | 219 |
@@ -200,14 +200,26 @@ BOOL ImageDataHasPNGPreffix(NSData *data) { | @@ -200,14 +200,26 @@ BOOL ImageDataHasPNGPreffix(NSData *data) { | ||
200 | } | 200 | } |
201 | 201 | ||
202 | - (BOOL)diskImageExistsWithKey:(NSString *)key { | 202 | - (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 | + | ||
208 | return exists; | 209 | return exists; |
209 | } | 210 | } |
210 | 211 | ||
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 | + | ||
211 | - (UIImage *)imageFromMemoryCacheForKey:(NSString *)key { | 223 | - (UIImage *)imageFromMemoryCacheForKey:(NSString *)key { |
212 | return [self.memCache objectForKey:key]; | 224 | return [self.memCache objectForKey:key]; |
213 | } | 225 | } |
@@ -214,12 +214,47 @@ SDWebImageManager *manager = [SDWebImageManager sharedManager]; | @@ -214,12 +214,47 @@ SDWebImageManager *manager = [SDWebImageManager sharedManager]; | ||
214 | - (BOOL)isRunning; | 214 | - (BOOL)isRunning; |
215 | 215 | ||
216 | /** | 216 | /** |
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 | ||
218 | */ | 222 | */ |
219 | - (BOOL)cachedImageExistsForURL:(NSURL *)url; | 223 | - (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 | + */ | ||
220 | - (BOOL)diskImageExistsForURL:(NSURL *)url; | 232 | - (BOOL)diskImageExistsForURL:(NSURL *)url; |
221 | 233 | ||
222 | /** | 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 | + | ||
257 | +/** | ||
223 | *Return the cache key for a given URL | 258 | *Return the cache key for a given URL |
224 | */ | 259 | */ |
225 | - (NSString *)cacheKeyForURL:(NSURL *)url; | 260 | - (NSString *)cacheKeyForURL:(NSURL *)url; |
@@ -71,6 +71,42 @@ | @@ -71,6 +71,42 @@ | ||
71 | return [self.imageCache diskImageExistsWithKey:key]; | 71 | return [self.imageCache diskImageExistsWithKey:key]; |
72 | } | 72 | } |
73 | 73 | ||
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 | + | ||
74 | - (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url | 110 | - (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url |
75 | options:(SDWebImageOptions)options | 111 | options:(SDWebImageOptions)options |
76 | progress:(SDWebImageDownloaderProgressBlock)progressBlock | 112 | progress:(SDWebImageDownloaderProgressBlock)progressBlock |
-
Please register or login to post a comment