I’m asynchronously loading images onto cells in a UITableView. Code looks like:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// after getting the cell..
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *imageUrl = [someMethodToGetImageUrl];
NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL imageUrl]];
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = [UIImage imageWithData:imageData];
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
});
});
}
My question pertains to what happens if this tableView is deallocated (e.g. popped off the navigationController stack) after it fires off the dispatch, but before the thread completes to try to set the cell’s image. The cell would also be deallocated, and trying to do stuff to that cell would cause a crash, no?
I’d been getting crashes with the code above. If I segue into this tableView then immediately back out, I get a crash on the line:
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
If I change it to:
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
the crashes go away, which doesn’t really make sense to me. Can someone explain to me why this is the case? Thanks.
Any routine which may keep a block beyond its original scope needs to copy it.
dispatch_async()does.When a block is copied, it retains any object pointer variable it references. If the block implicitly accesses
selfin the form of instance variables, it retainsself. It holds those references until it is itself released.In your example,
cell,imageData,indexPath, andtableVieware all retained until the blocks finish.