Questions about GCD iOS 4

I watched some presentations from WWDC 2010, and also read most of the documents on blocks and concurrency and asked a couple of questions regarding the use of blocks with sequential queues in Grand Central Dispatch. I have an iOS 4 project that has scrollview and a dictionary of image information - links to images, etc. I want to use GCD and blocks to load images and put them in my scrollview without blocking the main stream. I wrote the following code that 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:

  • According to the concurrency manual, I should not grab variables from the enclosing scope, which are non-scalar types - in my code I take a dict, which is an NSDictionary * object. If I can’t capture it, how can I write the code? Does the block only capture variables from the scope that is actually used?

  • What happens if I leave the current ViewController before all the images are sent to the sequential send queue? I don’t think they know that the ViewController that created them has disappeared, and what happens when they execute the completion handler, when I insert images into my scrollview in the main thread? Does this cause an error or what? And how can I cancel any remaining operations in a sequential queue when my ViewController disappears?

Yours faithfully,

+6
objective-c iphone objective-c-blocks grand-central-dispatch
source share
4 answers
  • Although this is a mistake, it is important to understand what the concurrency guide will tell you: Pointers are scalar types. That way, you can grab the pointers inside the blocks, all you need ... BUT you must be aware of the whole life of the memory they are pointing to! NSDictionary * is-a-kind-id, and when you refer to an identifier in a block, the runtime takes responsibility for keeping the identifier if the block is copied (which it is dispatch_async ()) and then frees it when the block itself is freed . And yes, the block only captures the variables referenced inside it.

  • Since you now know that the asynchronous block made a hold on itself, it should be clear (er) that (memory module control errors) your ViewController cannot “disappear” until the block is executed. This way, it won’t cause a crash - but you correctly noticed that you really need a way to cancel this kind of asynchronous operation when you are not really planning to use the results anymore. One simple but effective template is to put a test at the beginning of your asynchronous block, which checks if the work should be done.

+10
source share

Since others answered your two questions, I would like to comment on your code and recommend that you not use GCD for network requests such as images. The main problem is that they will all be executed simultaneously. Depending on the number of downloads, you can create too many simultaneous connections that can ruin your cellular connection, and the user may end up making a mistake if the images do not start to appear while they are fighting for a precious network.

Try using an NSOperationQueue with a maxConcurrentOperationCount value of 2 or 3. This will allow you to queue a potentially infinite number of network requests, but no more than a few are executed in parallel. Since you can access the network status of the device, you can conditionally increase it to say 8 to connect to Wi-Fi.

And the second problem with GCD is that canceling pending operations is a bit cumbersome. If your user enters the view controller and then throws it back, depending on how you programmed your code, the GCD blocks save the view controller, prevent it from being released, and virtually all network operations should end until the controller can (so it makes no sense to cancel connections in dealloc ).

I found out that it is difficult to track the proxy server and quickly navigate inside the view and back. It was really terrible to see how the pending connections caused about 20 seconds of additional network damage due to my unsuspecting application.

tl; dr uses the queue for the network, GCD for updating / scaling / updating the GUI.

+3
source share

@Kaelin Colclasure: for the first question, most likely the general state problem in a multi-threaded application: for the integral type, you have a copy by value (which also applies to pointers), but when you use the object that the pointer refers to, you will have all problems associated with the lack of blocking (the kind of variation on the life problem of the object that you mentioned here).

+1
source share

Here is the practical difference for your first point:

If I pass the scalar to the block used in the GCD queue, then inside the block I work with a copy of the source data. Changes will not be visible outside the block. (There are warnings here, the __block modifier, but overall this is correct.)

If I pass, say, NSMutableDictionary into a block, the changes in the dictionary will be visible outside the block - this is because you saved the link to the dictionary and did not take a deep copy.

In any case, memory management is performed for you, that is, the copied or copied variable is saved.

Since you cannot change the contents of an NSDictionary after it is initialized, you will probably find that the blocks automatically do the “Right Thing” for you.

At the second point, memory management is pretty automatic, unless you need to work with a copy of a mutable object.

0
source share

All Articles