Blocks and Multithreading in iOS

When developing for iOS, there are several ways to deal with multithreading. You can create your own threads and do whatever you please, but there are various alternatives that will make your life much easier. In Snow Leopard, Grand Central Dispatch (GCD) was introduced, and it is the easiest and most efficient way to handle multithreading.

Before we start talking about Grand Central Dispatch, you first need to know what blocks are, since we will use them for our multithreaded app.

Blocks

In Objective-C, a block is a block of code that can be passed as an argument and treated as a variable. Blocks start with the character caret ^, followed by the arguments in parentheses, and then the actual code between curly braces.

NSDictionary *dictionary = [[NSDictionary alloc] init];
    [dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
        self.label.text = value;
        if ([@"Last" isEqualToString:value]) {
            *stop = YES;
        }
    }];

Local variables declared outside the block can be accessed inside the block, although they are read-only. If you want to change its value, then you need to add __block to the variable declaration.

__block int totalIterations = 0;
NSDictionary *dictionary = [[NSDictionary alloc] init];
    [dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
        self.label.text = value;
        totalIterations++;
        if ([@"Last" isEqualToString:value]) {
            *stop = YES;
        }
    }];

Just like in C, you can use typedef to create a type of variable that will be used to store a whole block of code.

typedef BOOL (^block_type)(int iterations);

Now, you can declare a variable of type block_type:

block_type block;
block = ^(int iterations) {
    BOOL finished = NO;
   
    for (int i = 0; i < iterations; i++) {
        NSLog(@"Inside block");
    }
   
    finished = YES;
    return finished;
}

Finally, you can call this variable like this:

BOOL isFinished = block(10);

Blocks in iOS can be really hard, so don’t worry if you get confused at the beginning. Unfortunately, blocks are really useful and as a developer you will need to use them quite a lot. They are mainly used for:

  • Enumeration.
  • View Animations.
  • Sorting.
  • Notification.
  • Error handlers.
  • Completion handlers.
  • Grand Central Dispatch

Grand Central Dispatch

GCD is a C API that works with queues of operations, and provides and manages FIFO queues to which your application can submit tasks in the form of block objects. It is really powerful, since you just pass a block of code to a queue, and it will eventually get executed without blocking any other threads and without having to manage threads yourself.

There are three kinds of queues:

  • Main: the application’s main thread.
  • Concurrent: if the device has multiple processors, tasks run concurrently and may finish in any order.
  • Serial: tasks execute one at a time in FIFO order.

The main thread is where you can touch the UI, otherwise, your app may crash and will behave badly.

The most important functions are:

  • dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr): with this function you create a new queue. attr is used to specify whether it is a concurrent or a serial queue. Pass NULL to create a serial queue, and DISPATCH_QUEUE_CONCURRENT for a concurrent queue.
  • void dispatch_async(dispatch_queue_t queue,dispatch_block_t block): puts a block in a queue.
  • dispatch_queue_t dispatch_get_current_queue(): to get the current queue.
  • void dispatch_queue_retain(dispatch_queue_t): use this function after getting the current queue to retain it.
  • dispatch_queue_t dispatch_get_main_queue(): returns the main queue.
  • void dispatch_release(dispatch_queue_t): to release the queue once it has finished executing all the blocks.

Example

Now that you know everything you need about blocks and GCD, I will show you a quick example to get an image from a URL and download it without blocking the main thread. Once it has finished downloading it, we will assign it to the UIImageView’s image property.

Create your project, and from the storyboard, add a UIImageView to your UIView and embed it in a UIScrollView.

Don’t forget to declare two IBOutlets (imageView and scrollView) to link your code to the storyboard. The only thing you need to do to download an image from another thread, is to add this to your view controller’s implementation file:

- (void)viewWillAppear:(BOOL)animated
{
    dispatch_queue_t downloadQueue = dispatch_queue_create("image", NULL);
    dispatch_async(downloadQueue, ^{
         NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://www.example.com/example.png"]];
         dispatch_async(dispatch_get_main_queue(), ^{
             UIImage *image = [UIImage imageWithData:imageData];
             self.imageView.image = image;
             self.imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height);
             self.scrollView.contentSize = image.size;
         });
    });
    dispatch_release(downloadQueue);
}

Conclusion

Although blocks may be difficult to understand at first, once you get used to them you will find them very useful.

As for GDC, it really helps us, developers, to implement multithreading in a very easy way. If you have any doubts, please leave a comment!

Comment policy: Respectful and beneficial comments are welcome with full open hands. However, all comments are manually moderated and those that doesn't relate with what the passage is saying or offensive comments would be deleted. Thanks for understanding!

Leave a Reply

Your email address will not be published. Required fields are marked *