I’m trying to load thumbnail images from a remote site onto a UITableView. I want to do this asynchronously, and I want to implement a poorman’s cache for the thumbnail images. Here’s my code snippet (I’ll describe the problematic behavior below):
@property (nonatomic, strong) NSMutableDictionary *thumbnailsCache;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
// ...after obtaining the cell:
NSString *thumbnailCacheKey = [NSString stringWithFormat:@"cache%d", indexPath.row];
if (![[self.thumbnailsCache allKeys] containsObject:thumbnailCacheKey]) {
// thumbnail for this row is not found in cache, so get it from remote website
__block NSData *image = nil;
dispatch_queue_t imageQueue = dispatch_queue_create("queueForCellImage", NULL);
dispatch_async(imageQueue, ^{
NSString *thumbnailURL = myCustomFunctionGetThumbnailURL:indexPath.row;
image = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:thumbnailURL]];
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = [UIImage imageWithData:image];
});
});
dispatch_release(imageQueue);
[self.thumbnailsCache setObject:image forKey:thumbnailCacheKey];
} else {
// thumbnail is in cache
NSData *image = [self.thumbnailsCache objectForKey:thumbnailCacheKey];
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = [UIImage imageWithData:image];
});
}
So here are the problematic behaviors:
-
When the UITableView loads, thumbnails don’t show up on the initial set of cells. Only when a cell moves off screen then moves back on does the thumbnail show up.
-
Cache isn’t working at all. From what I can tell, it fails to save the thumbnail to cache altogether. That is, this line fails:
[self.thumbnailsCache setObject:image forKey:thumbnailCacheKey];
-
The GCD queue is getting created/released for each cell. Furthermore, the queue name is the same every time. Is this bad practice?
I’d appreciate you guys pointing out anything you see that is wrong, or even any general approach comments. Thanks.
Update:
-
RESOLVED: I added a call to reloadRowsAtIndexPaths and now the thumbnail images load on initial rows that display
-
RESOLVED: The reason it was failing is because it was adding the image object to the dictionary before the other thread completed setting that object. I created an instance method to add object to the property dictionary, so that I can call it from inside the block, ensuring it gets added after the image object is set.
1) The reason no initial image is showing is because the cell is rendered with image = nil, and so it intelligently hides the image view.
2) Did you try moving this line inside your block ?
3) This is just a way to differentiate the queues to debug, and get info from the console, if you app crashes then you can see the name of the queue. This shouldn’t be a problem you having the same name for this, since it does the same operation. You wouldn’t want to use this name in another table view if you have the same logic.