Skip to content

Commit 43ebd60

Browse files
committed
Fixes a bug that causes image retrieval to never complete if it depends on an source image URL whose request is already in flight.
The bug is that are many scenarios in which _processImage:forEntity:withFormatName:formatName:completionBlocksDictionary:completionBlocksDictionary fails to call all completion blocks. Here are the simplest repro steps. - Formats A and B (neither of which are in a family) - Entity X - Both X(A) and X(B) rely on the same source image url - Retrieve X(A) and then X(B) prior to source image becoming available - When source image becomes available the image is processed and completion blocks called for A, but not B. The fix is to redo the calculation of the list of formats that should be processed when a source image becomes available.
1 parent b1c5f8d commit 43ebd60

1 file changed

Lines changed: 59 additions & 30 deletions

File tree

FastImageCache/FICImageCache.m

Lines changed: 59 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ - (void)_imageDidLoad:(UIImage *)image forURL:(NSURL *)URL {
248248
NSString *formatName = [entityDictionary objectForKey:FICImageCacheFormatKey];
249249
NSDictionary *completionBlocksDictionary = [entityDictionary objectForKey:FICImageCacheCompletionBlocksKey];
250250
if (image != nil){
251-
[self _processImage:image forEntity:entity withFormatName:formatName completionBlocksDictionary:completionBlocksDictionary];
251+
[self _processImage:image forEntity:entity completionBlocksDictionary:completionBlocksDictionary];
252252
} else {
253253
NSArray *completionBlocks = [completionBlocksDictionary objectForKey:formatName];
254254
if (completionBlocks != nil) {
@@ -312,41 +312,18 @@ - (void)setImage:(UIImage *)image forEntity:(id <FICEntity>)entity withFormatNam
312312
if (imageTable) {
313313
[imageTable deleteEntryForEntityUUID:entityUUID];
314314

315-
[self _processImage:image forEntity:entity withFormatName:formatName completionBlocksDictionary:completionBlocksDictionary];
315+
[self _processImage:image forEntity:entity completionBlocksDictionary:completionBlocksDictionary];
316316
} else {
317317
[self _logMessage:[NSString stringWithFormat:@"*** FIC Error: %s Couldn't find image table with format name %@", __PRETTY_FUNCTION__, formatName]];
318318
}
319319
}
320320
}
321321

322-
- (void)_processImage:(UIImage *)image forEntity:(id <FICEntity>)entity withFormatName:(NSString *)formatName completionBlocksDictionary:(NSDictionary *)completionBlocksDictionary {
323-
FICImageFormat *imageFormat = [_formats objectForKey:formatName];
324-
NSString *formatFamily = [imageFormat family];
325-
NSString *entityUUID = [entity UUID];
326-
NSString *sourceImageUUID = [entity sourceImageUUID];
327-
328-
if (formatFamily != nil) {
329-
BOOL shouldProcessAllFormatsInFamily = YES;
330-
if (_delegateImplementsShouldProcessAllFormatsInFamilyForEntity) {
331-
shouldProcessAllFormatsInFamily = [_delegate imageCache:self shouldProcessAllFormatsInFamily:formatFamily forEntity:entity];
332-
}
333-
// All of the formats in a given family use the same source asset, so once we have that source asset, we can generate all of the family's formats.
334-
for (FICImageTable *table in [_imageTables allValues]) {
335-
FICImageFormat *imageFormat = [table imageFormat];
336-
NSString *tableFormatFamily = [imageFormat family];
337-
if ([formatFamily isEqualToString:tableFormatFamily]) {
338-
NSArray *completionBlocks = [completionBlocksDictionary objectForKey:[imageFormat name]];
339-
340-
BOOL imageExistsForEntity = [table entryExistsForEntityUUID:entityUUID sourceImageUUID:sourceImageUUID];
341-
BOOL shouldProcessFamilyFormat = shouldProcessAllFormatsInFamily && imageExistsForEntity == NO;
342-
if (shouldProcessFamilyFormat || [completionBlocks count] > 0) {
343-
[self _processImage:image forEntity:entity imageTable:table completionBlocks:completionBlocks];
344-
}
345-
}
346-
}
347-
} else {
348-
FICImageTable *imageTable = [_imageTables objectForKey:formatName];
349-
NSArray *completionBlocks = [completionBlocksDictionary objectForKey:formatName];
322+
- (void)_processImage:(UIImage *)image forEntity:(id <FICEntity>)entity completionBlocksDictionary:(NSDictionary *)completionBlocksDictionary {
323+
for (NSString *formatToProcess in [self formatsToProcessForCompletionBlocks:completionBlocksDictionary
324+
entity:entity]) {
325+
FICImageTable *imageTable = [_imageTables objectForKey:formatToProcess];
326+
NSArray *completionBlocks = [completionBlocksDictionary objectForKey:formatToProcess];
350327
[self _processImage:image forEntity:entity imageTable:imageTable completionBlocks:completionBlocks];
351328
}
352329
}
@@ -386,6 +363,58 @@ - (void)_processImage:(UIImage *)image forEntity:(id <FICEntity>)entity imageTab
386363
}
387364
}
388365

366+
- (NSSet *)formatsToProcessForCompletionBlocks:(NSDictionary *)completionBlocksDictionary entity:(id <FICEntity>)entity {
367+
// At the very least, we must process all formats with pending completion blocks
368+
NSMutableSet *formatsToProcess = [NSMutableSet setWithArray:completionBlocksDictionary.allKeys];
369+
370+
// Get the list of format families included by the formats we have to process
371+
NSMutableSet *families;
372+
for (NSString *formatToProcess in formatsToProcess) {
373+
FICImageTable *imageTable = _imageTables[formatToProcess];
374+
FICImageFormat *imageFormat = imageTable.imageFormat;
375+
NSString *tableFormatFamily = imageFormat.family;
376+
if (tableFormatFamily) {
377+
if (!families) {
378+
families = [NSMutableSet set];
379+
}
380+
[families addObject:tableFormatFamily];
381+
}
382+
}
383+
384+
// The delegate can override the list of families to process
385+
if (_delegateImplementsShouldProcessAllFormatsInFamilyForEntity) {
386+
[families minusSet:[families objectsPassingTest:^BOOL(NSString *familyName, BOOL *stop) {
387+
return ![_delegate imageCache:self shouldProcessAllFormatsInFamily:familyName forEntity:entity];
388+
}]];
389+
}
390+
391+
// Ensure that all formats from all of those families are included in the list
392+
if (families.count) {
393+
for (FICImageTable *table in _imageTables.allValues) {
394+
FICImageFormat *imageFormat = table.imageFormat;
395+
NSString *imageFormatName = imageFormat.name;
396+
// If we're already processing this format, keep looking
397+
if ([formatsToProcess containsObject:imageFormatName]) {
398+
continue;
399+
}
400+
401+
// If this format isn't included in any referenced family, keep looking
402+
if (![families containsObject:imageFormat.family]) {
403+
continue;
404+
}
405+
406+
// If the image already exists, keep going
407+
if ([table entryExistsForEntityUUID:entity.UUID sourceImageUUID:entity.sourceImageUUID]) {
408+
continue;
409+
}
410+
411+
[formatsToProcess addObject:imageFormatName];
412+
}
413+
}
414+
415+
return formatsToProcess;
416+
}
417+
389418
#pragma mark - Checking for Image Existence
390419

391420
- (BOOL)imageExistsForEntity:(id <FICEntity>)entity withFormatName:(NSString *)formatName {

0 commit comments

Comments
 (0)