Blocks & iOS: Introduction to Grand Central Dispatch

Grand Central Dispatch, or GCD, is an extremely important feature introduced in iOS 4.0, OS X 10.6 and later versions. It's used to distribute the computation across multiple cores dispatching any number of threads needed and  it is optimized to work with devices with multi-core processors.

When using GCD, you don’t need to handle any multiple threads your self. GCD technology will take care of that for you, so you can get to focus on the task itself. GCD works with blocks, which are blocks of code that you need to execute in the background, in low, default or high priority queue, in a serial or concurrent order.

Using GCD in your application will bring a great benefit of all the features mentioned above without the hassle of managing the threads yourself and knowing that the GCD technology will be utilizing multi-core processors found in iOS devices today. Before we dig into GCD itself we’ll need to be comfortable using blocks.

Blocks are a C-level syntactic and runtime feature that let you pass a function as an argument, store it, or execute it on multiple threads. Blocks look very similar to functions, instead of the name of the function replace it with ^ and you’re good to go.

To learn more about blocks visit:

http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/00_Introduction.html

So you can use a block for example as an argument in a method like:


- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block

where the block parameter is expecting an object, idx and bool pointer.

Here is one example of how to use a block for enumerating on an array:


NSArray *inputArray = [NSArray arrayWithObjects:@"1",@"2",@"3",@"4",[NSObject new],@"a",@"b", nil];

[inputArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){

if (![obj isKindOfClass:[NSString class]]) {

NSLog(@"found the first different Object: %@",obj);

*stop = YES;

}

else {

NSLog(@"%@ at index:%d", obj, idx);

}

return;

}];


So the output will be:


1 at index:0

2 at index:1

3 at index:2

4 at index:3

found the first different Object:


So now that we know more about blocks, I’ll show you how you can add them to a serial queue. Adding blocks to a serial queues ensures that these blocks of code will be executed one after the other. If one block is taking too long, the rest of the blocks will have to wait until its done. Serial blocks are useful in many cases where ensuring the order of execution is critical.

So our block will have some definitions like:


static void (^counterBlock)(int,char, NSUInteger , GCDMaster*) = ^(int count, char letter, NSUInteger blockNumber, GCDMaster *delegate){

… block does some work here

}


So this block is named counterBlock and it taks arguments: chat, NSUInteger and an object typed GCDMaster. So the arguments in this block are passed in from the method calling the block. As you have seen earlier, you can specify any configuration of a block just like you would do with functions.

Now lets talk about Dispatch Queues. Apple’s definition of Dispatch Queues is:

Dispatch queues are a C-based mechanism for executing custom tasks. A dispatch queue executes tasks either serially or concurrently but always in a first-in, first-out order. (In other words, a dispatch queue always dequeues and starts tasks in the same order in which they were added to the queue.) A serial dispatch queue runs only one task at a time, waiting until that task is complete before dequeuing and starting a new one. By contrast, a concurrent dispatch queue starts as many tasks as it can without waiting for already started tasks to finish.

So we want to dispatch a few of these blocks on our serial Queue. Let's start by creating a serial queue:


serialQueue = dispatch_queue_create("com.3Pillar.SerialQueue", NULL);


This will define a serial queue with a reverse domain name so we can identify our queues. Next we will add 4 of these blocks with different arguments to run serially on the queue:


dispatch_async(serialQueue, ^{counterBlock(20000 ,'a' ,0, self);});dispatch_async(serialQueue, ^{counterBlock(1000  ,'A' ,1 ,self);});dispatch_async(serialQueue, ^{counterBlock(20000 ,'0' ,2 ,self);});dispatch_async(serialQueue, ^{counterBlock(2000  ,'Z' ,3 ,self);});

What we did now is we added 4 blocks to our queue “serialQueue” with various different arguments. These blocks will be executed one after the other. When block 1 is finished 2 is started and so forth. So if we have a log message when the block start and end it will look like this:


==block 1 started ==

==block 1 ended ==

==block 2 started ==

==block 2 ended ==

==block 3 started ==


But if we would want to execute the blocks concurrently and we don’t have the need to run them sequentially then we’ll need to create a concurrent queue. Apple provides you with a number of concurrent queues that you can access from you’re application. These queues are global and they have 3 priorities DISPATCH_QUEUE_PRIORITY_HIGH, DISPATCH_QUEUE_PRIORITY_DEFAULT and DISPATCH_QUEUE_PRIORITY_LOW priority.

To get a concurrent queue:


dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);


Then we add the blocks to the queue the same way as before:


dispatch_async(concurrentQueue, ^{counterBlock(20000 ,'a' ,0, self);});dispatch_async(concurrentQueue, ^{counterBlock(1000  ,'A' ,1 ,self);});

dispatch_async(concurrentQueue, ^{counterBlock(20000 ,'0' ,2 ,self);});

 

dispatch_async(concurrentQueue, ^{counterBlock(2000  ,'Z' ,3 ,self);});

 


GCD runs these blocks concurrently, there is no way to expect which one executes first.

So the output would be random like this:


==block 2 started ==

==block 3 started ==

==block 3 ended ==

==block 1 started ==


Once the Queue is running you can suspend it and resume it by calling the appropriate methods dispatch_suspend and dispatch_resume. Note that the calls to dispatch_suspend and dispatch_resume must be balanced. Please feel free to run the attached project with all the example given in this blog for you’re self and feel free to experiment with GCD and make you’re self familiar with it.

Once you start getting comfortable working with blocks and using queues you will be able to introduce many performance improvements to your code and send heavy operations back to a GCD queue while freeing your UI to move freely and keep the user experience smooth.

 

This post was also published on Eddie's personal blog, which can be accessed here: http://objective-coder.blogspot.com/2012/12/introduction-to-grand-central-dispatch.html