Skip to content

Commit 50e10e6

Browse files
committed
Safely access _requests dictionary across threads
1 parent 04ed460 commit 50e10e6

File tree

1 file changed

+42
-29
lines changed

1 file changed

+42
-29
lines changed

FastImageCache/FastImageCache/FastImageCache/FICImageCache.m

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -200,13 +200,20 @@ - (BOOL)_retrieveImageForEntity:(id <FICEntity>)entity withFormatName:(NSString
200200

201201
if (sourceImageURL != nil) {
202202
// We check to see if this image is already being fetched.
203-
NSMutableDictionary *requestDictionary = [_requests objectForKey:sourceImageURL];
204-
if (requestDictionary == nil) {
205-
// If we're here, then we aren't currently fetching this image.
206-
NSMutableDictionary *requestDictionary = [NSMutableDictionary dictionary];
207-
[_requests setObject:requestDictionary forKey:sourceImageURL];
203+
BOOL needsToFetch = NO;
204+
@synchronized (_requests) {
205+
NSMutableDictionary *requestDictionary = [_requests objectForKey:sourceImageURL];
206+
if (requestDictionary == nil) {
207+
// If we're here, then we aren't currently fetching this image.
208+
requestDictionary = [NSMutableDictionary dictionary];
209+
[_requests setObject:requestDictionary forKey:sourceImageURL];
210+
needsToFetch = YES;
211+
}
208212

209213
_FICAddCompletionBlockForEntity(formatName, requestDictionary, entity, completionBlock);
214+
}
215+
216+
if (needsToFetch) {
210217
UIImage *image;
211218
if ([entity respondsToSelector:@selector(imageForFormat:)]){
212219
FICImageFormat *format = [self formatWithName:formatName];
@@ -217,12 +224,9 @@ - (BOOL)_retrieveImageForEntity:(id <FICEntity>)entity withFormatName:(NSString
217224
[self _imageDidLoad:image forURL:sourceImageURL];
218225
} else if (_delegateImplementsWantsSourceImageForEntityWithFormatNameCompletionBlock){
219226
[_delegate imageCache:self wantsSourceImageForEntity:entity withFormatName:formatName completionBlock:^(UIImage *sourceImage) {
220-
[self _imageDidLoad:sourceImage forURL:sourceImageURL];
227+
[self _imageDidLoad:sourceImage forURL:sourceImageURL];
221228
}];
222229
}
223-
} else {
224-
// We have an existing request dictionary, which means this URL is currently being fetched.
225-
_FICAddCompletionBlockForEntity(formatName, requestDictionary, entity, completionBlock);
226230
}
227231
} else {
228232
NSString *message = [NSString stringWithFormat:@"*** FIC Error: %s entity %@ returned a nil source image URL for image format %@.", __PRETTY_FUNCTION__, entity, formatName];
@@ -239,7 +243,13 @@ - (BOOL)_retrieveImageForEntity:(id <FICEntity>)entity withFormatName:(NSString
239243
}
240244

241245
- (void)_imageDidLoad:(UIImage *)image forURL:(NSURL *)URL {
242-
NSDictionary *requestDictionary = [_requests objectForKey:URL];
246+
NSDictionary *requestDictionary;
247+
@synchronized (_requests) {
248+
requestDictionary = [_requests objectForKey:URL];
249+
[_requests removeObjectForKey:URL];
250+
// Now safe to use requestsDictionary outside the lock, because we've taken ownership from _requests
251+
}
252+
243253
if (requestDictionary != nil) {
244254
for (NSMutableDictionary *entityDictionary in [requestDictionary allValues]) {
245255
id <FICEntity> entity = [entityDictionary objectForKey:FICImageCacheEntityKey];
@@ -259,8 +269,6 @@ - (void)_imageDidLoad:(UIImage *)image forURL:(NSURL *)URL {
259269
}
260270
}
261271
}
262-
263-
[_requests removeObjectForKey:URL];
264272
}
265273

266274
static void _FICAddCompletionBlockForEntity(NSString *formatName, NSMutableDictionary *entityRequestsDictionary, id <FICEntity> entity, FICImageCacheCompletionBlock completionBlock) {
@@ -435,27 +443,32 @@ - (void)deleteImageForEntity:(id <FICEntity>)entity withFormatName:(NSString *)f
435443

436444
- (void)cancelImageRetrievalForEntity:(id <FICEntity>)entity withFormatName:(NSString *)formatName {
437445
NSURL *sourceImageURL = [entity sourceImageURLWithFormatName:formatName];
438-
NSMutableDictionary *requestDictionary = [_requests objectForKey:sourceImageURL];
439-
if (requestDictionary) {
440-
NSString *entityUUID = [entity UUID];
441-
NSMutableDictionary *entityRequestsDictionary = [requestDictionary objectForKey:entityUUID];
442-
if (entityRequestsDictionary) {
443-
NSMutableDictionary *completionBlocksDictionary = [entityRequestsDictionary objectForKey:FICImageCacheCompletionBlocksKey];
444-
[completionBlocksDictionary removeObjectForKey:formatName];
445-
446-
if ([completionBlocksDictionary count] == 0) {
447-
[requestDictionary removeObjectForKey:entityUUID];
448-
}
449-
450-
if ([requestDictionary count] == 0) {
451-
[_requests removeObjectForKey:sourceImageURL];
452-
453-
if (_delegateImplementsCancelImageLoadingForEntityWithFormatName) {
454-
[_delegate imageCache:self cancelImageLoadingForEntity:entity withFormatName:formatName];
446+
NSString *entityUUID = [entity UUID];
447+
448+
BOOL cancelImageLoadingForEntity = NO;
449+
@synchronized (_requests) {
450+
NSMutableDictionary *requestDictionary = [_requests objectForKey:sourceImageURL];
451+
if (requestDictionary) {
452+
NSMutableDictionary *entityRequestsDictionary = [requestDictionary objectForKey:entityUUID];
453+
if (entityRequestsDictionary) {
454+
NSMutableDictionary *completionBlocksDictionary = [entityRequestsDictionary objectForKey:FICImageCacheCompletionBlocksKey];
455+
[completionBlocksDictionary removeObjectForKey:formatName];
456+
457+
if ([completionBlocksDictionary count] == 0) {
458+
[requestDictionary removeObjectForKey:entityUUID];
459+
}
460+
461+
if ([requestDictionary count] == 0) {
462+
[_requests removeObjectForKey:sourceImageURL];
463+
cancelImageLoadingForEntity = YES;
455464
}
456465
}
457466
}
458467
}
468+
469+
if (cancelImageLoadingForEntity && _delegateImplementsCancelImageLoadingForEntityWithFormatName) {
470+
[_delegate imageCache:self cancelImageLoadingForEntity:entity withFormatName:formatName];
471+
}
459472
}
460473

461474
- (void)reset {

0 commit comments

Comments
 (0)