A brief introduction to libdispatch, and why you should be using it

|

GCD (libdispatch) is a technology that Apple introduced with OS X Snow Leopard and iOS 4.0; we’ve had access to it on iOS for over a year now (and Mac OS X for 2 years!). Most of the marketing we all heard about was better thread efficiency, better use of multiple cores and blah blah blah who cares? Yes, these things are great, and they will make a difference to how well your code runs. But if I’m honest, the part of GCD that really makes a difference to me, is just how much easier it makes my life as a programmer. I constantly find myself finishing up some threaded programming with libdispatch, and thinking: concurrency just shouldn’t be this easy.

The aim of this post is to demonstrate this fact to you, teach you how to use libdispatch, and possibly garner addresses for threatening letters should you not start using it immediately.

First, let’s look at a simple concurrent example using the previous best solution: NSOperation.

[code lang=”c”]- (void)startSomeBackgroundMethod {

id someArgumentOne = [self something];
id someArgumentTwo = [self somethingElse];
NSArray *arguments = [NSArray arrayWithObjects:someArgumentOne, someArgumentTwo, nil];

NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(someBackgroundMethod:) object:arguments];
NSOperationQueue *operationQueue = [self operationQueue];
[operationQueue addOperation:operation];
[operation release];
}

– (NSOperationQueue *)operationQueue {

if (_operationQueue != nil)
return _operationQueue;

_operationQueue = [[NSOperationQueue alloc] init];

return _operationQueue;
}

– (void)someBackgroundMethod:(NSArray *)arguments {

id argumentOne = [arguments objectAtIndex:0];
id argumentTwo = [arguments objectAtIndex:1];

//
// do some stuff
//

id result = …;

[self performSelectorOnMainThread:@selector(finishedBackgroundMethod:) withObject:result waitUntilDone:NO];
}

– (void)finishedBackgroundMethod:(id)result {

//
// do something with the result on the main thread
//
}
[/code]

So let’s review this code. We have some operation that needs to be done in the background, that operation takes 2 arguments. Once the operation completes, it informs the main thread that it’s finished. We also have a shared operation queue, which is loaded lazily.

Now, we’ll do the same thing using GCD. Check this:

[code lang=”c”]- (void)startSomeBackgroundMethod {

id someArgumentOne = [self something];
id someArgumentTwo = [self somethingElse];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

//
// do some stuff
//

id result = …;

dispatch_async(dispatch_get_main_queue(), ^{

//
// do something with the result on the main thread
//
});
});
}
[/code]

How much cleaner is that?

  • We don’t have to have 3 methods to do one job (4 if you count the queue).
  • We don’t have to wrap/unwrap the arguments in/from an array.
  • We don’t have to explicitly pass the arguments into the background thread.
  • We don’t have to manage an operation queue.
  • We don’t have to use selectors.
  • We have to write less code!

All this for free. Well, pretty much free; the only learning curve that came with GCD is blocks.

Blocks are a great tool, and it would be beneficial for any Objective-C programmer to learn how to use them properly. Many of the APIs you know and love in the iOS SDK have block based alternatives, that read better inline and simplify your development cycle. I often use them as an alternative for writing a new function, like mini-functions. However, blocks are out of the scope of this post. All you need to know about blocks in order to use libdispatch, is that any code you want executed using GCD needs to be preceded by a caret (^) and contained within curly braces. Just like in the example above.

There are 3 calls that you need to know how to use in libdispatch:

[code lang=”c”]dispatch_get_global_queue(dispatch_queue_priority_t priority, unsigned long flags);

#define dispatch_get_main_queue() (&_dispatch_main_q)

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
[/code]

dispatch_get_global_queue(dispatch_queue_priority_t priority, unsigned long flags) returns an operation queue from GCD. The first argument is a priority argument, but you will mostly be using DISPATCH_QUEUE_PRIORITY_DEFAULT, and the second argument is reserved for future use, so should always be 0.

dispatch_get_main_queue() is a convenience macro for returning the operation queue for the main thread. This is the queue that you will use when you want to jump back onto the main thread from a background thread.

dispatch_async(dispatch_queue_t queue, dispatch_block_t block) is the business end of libdispatch. Simply pass it a queue and a block of code to execute, and it will handle the rest.

That’s it, that’s all you need to know to get started. Personally, I think one of the best features of using GCD and blocks is the simplicity of passing arguments into the background. Back in the days of NSOperation I used to spend hours trying to use NSArray to wrap arguments that may or may not be nil (hint: [NSNull null]), which resulted in long if statements and generally messy code. With GCD, you can just use any variables in the current scope, and the runtime will manage moving them into the background and making sure they aren’t released prematurely. It’s like Christmas.

As the titles dictates, this is merely an introduction into using libdispatch. There are caveats to watch out for regarding block retain cycles and other nasty stuff, but these are usually special cases. The main thing you need to consider is that any work you’re doing inside that block is in the background, which means no UIKit code, NSManagedObjectContext considerations and such like.

Now go forth and, erm, stop using NSOperation.