views:

302

answers:

1

Hi all,

I've looked at some of the presentations form WWDC 2010 and also read most of the documents on blocks and concurrency and have a couple of questions regarding using blocks with serial queues in Grand Central Dispatch. I have an iOS 4 project that has a scrollview and a dictionary of image information - urls to the images and so on. I want to use GCD and blocks to download the images and put them in my scrollview thus not blocking the main thread. I have writen the following code which seems to work:

for (NSDictionary* dict in images)
 {

      dispatch_async(image_queue, ^{

           NSString* urlString = [dict objectForKey:@"url"];
           NSURL* url = [NSURL URLWithString:urlString];
           NSData* imageData = [[NSData alloc] initWithContentsOfURL:url];
           UIImage* image = [UIImage imageWithData:imageData];
                    UIImageView* imageView = // initialize imageView with image;      

           dispatch_async(dispatch_get_main_queue(), ^{

                [self.scrollView addSubview:imageView];

           });

           [imageData release];

      });



 }

I have two questions:

  1. According to the concurrency guide I should not capture variables from the enclosing scope that are non scalar types - in my code I capture dict which is an NSDictionary* object. If I am not allowed to capture it, how should I then write the code? Does a block only capture variables from the enclosing scope that are actually used?

  2. What happens if I leave the current ViewController before all the images are fetched through the serial dispatch queue? I don't think that they are aware that the ViewController that created them is gone so what happens when they execute the completion handler where I insert the image views into my scrollview on the main thread? Does it cause an error or what? And how can I cancel any remaining operations on the serial queue when my ViewController disappears?

Best regards,

+2  A: 
  1. While it's a niggling point, this is important to understanding what the concurrency guide is trying to tell you: Pointers are scalar types. So you can capture pointers inside of blocks all you want... BUT you have to be cognizant of the lifetime of the memory they point to! NSDictionary * is-a-kind-of id, and when you reference an id in a block, the runtime takes responsibility for retaining the id if the block is copied (which it is by dispatch_async()) and then releasing it when the block itself is deallocated. And yes, a block only captures variables that are referenced within it.

  2. Since you now know that the async block has done a retain on self, it should be clear(er) that (modulo memory management errors) your ViewController can't "disappear" until the block is done. So it's not going to crash -- but you're correct to note that you really want a way to cancel this kind of async work when you're not actually planning to use the results anymore. One simple-but-effective pattern is to put a test at the beginning of your async block that checks to see if the work should still be done.

Kaelin Colclasure