@@ -25,7 +25,6 @@ @interface FICImageCache () {
2525 NSMutableDictionary *_formats;
2626 NSMutableDictionary *_imageTables;
2727 NSMutableDictionary *_requests;
28- __weak id <FICImageCacheDelegate> _delegate;
2928
3029 BOOL _delegateImplementsWantsSourceImageForEntityWithFormatNameCompletionBlock;
3130 BOOL _delegateImplementsShouldProcessAllFormatsInFamilyForEntity;
@@ -54,18 +53,15 @@ - (void)setDelegate:(id<FICImageCacheDelegate>)delegate {
5453 }
5554}
5655
57- static FICImageCache *__imageCache = nil ;
58-
5956#pragma mark - Object Lifecycle
6057
6158+ (instancetype )sharedImageCache {
62- if (__imageCache == nil ) {
63- static dispatch_once_t onceToken;
64- dispatch_once (&onceToken, ^{
65- __imageCache = [[[self class ] alloc ] init ];
66- });
67- }
68-
59+ static dispatch_once_t onceToken;
60+ static FICImageCache *__imageCache = nil ;
61+ dispatch_once (&onceToken, ^{
62+ __imageCache = [[[self class ] alloc ] init ];
63+ });
64+
6965 return __imageCache;
7066}
7167
@@ -204,13 +200,20 @@ - (BOOL)_retrieveImageForEntity:(id <FICEntity>)entity withFormatName:(NSString
204200
205201 if (sourceImageURL != nil ) {
206202 // We check to see if this image is already being fetched.
207- NSMutableDictionary *requestDictionary = [_requests objectForKey: sourceImageURL];
208- if (requestDictionary == nil ) {
209- // If we're here, then we aren't currently fetching this image.
210- NSMutableDictionary *requestDictionary = [NSMutableDictionary dictionary ];
211- [_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+ }
212212
213213 _FICAddCompletionBlockForEntity (formatName, requestDictionary, entity, completionBlock);
214+ }
215+
216+ if (needsToFetch) {
214217 UIImage *image;
215218 if ([entity respondsToSelector: @selector (imageForFormat: )]){
216219 FICImageFormat *format = [self formatWithName: formatName];
@@ -221,12 +224,9 @@ - (BOOL)_retrieveImageForEntity:(id <FICEntity>)entity withFormatName:(NSString
221224 [self _imageDidLoad: image forURL: sourceImageURL];
222225 } else if (_delegateImplementsWantsSourceImageForEntityWithFormatNameCompletionBlock){
223226 [_delegate imageCache: self wantsSourceImageForEntity: entity withFormatName: formatName completionBlock: ^(UIImage *sourceImage) {
224- [self _imageDidLoad: sourceImage forURL: sourceImageURL];
227+ [self _imageDidLoad: sourceImage forURL: sourceImageURL];
225228 }];
226229 }
227- } else {
228- // We have an existing request dictionary, which means this URL is currently being fetched.
229- _FICAddCompletionBlockForEntity (formatName, requestDictionary, entity, completionBlock);
230230 }
231231 } else {
232232 NSString *message = [NSString stringWithFormat: @" *** FIC Error: %s entity %@ returned a nil source image URL for image format %@ ." , __PRETTY_FUNCTION__, entity, formatName];
@@ -243,7 +243,13 @@ - (BOOL)_retrieveImageForEntity:(id <FICEntity>)entity withFormatName:(NSString
243243}
244244
245245- (void )_imageDidLoad : (UIImage *)image forURL : (NSURL *)URL {
246- 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+
247253 if (requestDictionary != nil ) {
248254 for (NSMutableDictionary *entityDictionary in [requestDictionary allValues ]) {
249255 id <FICEntity> entity = [entityDictionary objectForKey: FICImageCacheEntityKey];
@@ -263,8 +269,6 @@ - (void)_imageDidLoad:(UIImage *)image forURL:(NSURL *)URL {
263269 }
264270 }
265271 }
266-
267- [_requests removeObjectForKey: URL];
268272}
269273
270274static void _FICAddCompletionBlockForEntity (NSString *formatName, NSMutableDictionary *entityRequestsDictionary, id <FICEntity> entity, FICImageCacheCompletionBlock completionBlock) {
@@ -439,27 +443,32 @@ - (void)deleteImageForEntity:(id <FICEntity>)entity withFormatName:(NSString *)f
439443
440444- (void )cancelImageRetrievalForEntity : (id <FICEntity>)entity withFormatName : (NSString *)formatName {
441445 NSURL *sourceImageURL = [entity sourceImageURLWithFormatName: formatName];
442- NSMutableDictionary *requestDictionary = [_requests objectForKey: sourceImageURL];
443- if (requestDictionary) {
444- NSString *entityUUID = [entity UUID ];
445- NSMutableDictionary *entityRequestsDictionary = [requestDictionary objectForKey: entityUUID];
446- if (entityRequestsDictionary) {
447- NSMutableDictionary *completionBlocksDictionary = [entityRequestsDictionary objectForKey: FICImageCacheCompletionBlocksKey];
448- [completionBlocksDictionary removeObjectForKey: formatName];
449-
450- if ([completionBlocksDictionary count ] == 0 ) {
451- [requestDictionary removeObjectForKey: entityUUID];
452- }
453-
454- if ([requestDictionary count ] == 0 ) {
455- [_requests removeObjectForKey: sourceImageURL];
456-
457- if (_delegateImplementsCancelImageLoadingForEntityWithFormatName) {
458- [_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 ;
459464 }
460465 }
461466 }
462467 }
468+
469+ if (cancelImageLoadingForEntity && _delegateImplementsCancelImageLoadingForEntityWithFormatName) {
470+ [_delegate imageCache: self cancelImageLoadingForEntity: entity withFormatName: formatName];
471+ }
463472}
464473
465474- (void )reset {
0 commit comments