UI-related tasks have to be performed on the main thread, so the
main queue is the only candidate for
UI task execution in GCD. We can use the dispatch_get_main_queue
function to get the
handle to the main dispatch queue.
There are two ways of dispatching tasks to the main queue. Both are asynchronous, letting your program continue even when the task is not yet executed:
dispatch_async
functionExecutes a block object on a dispatch queue.
dispatch_async_f
functionExecutes a C function on a dispatch queue.
The dispatch_sync
method
cannot be called on the main queue because it will
block the thread indefinitely and cause your application to deadlock.
All tasks submitted to the main queue through GCD must be submitted
asynchronously.
Let’s have a look at using the dispatch_async
function. It accepts two
parameters:
The dispatch queue on which the task has to be executed.
The block object to be sent to the dispatch queue for asynchronous execution.
Here is an example. This code will display an alert, in iOS, to the user, using the main queue:
dispatch_queue_t mainQueue = dispatch_get_main_queue(); dispatch_async(mainQueue, ^(void) { [[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"GCD", nil) message:NSLocalizedString(@"GCD is amazing!", nil) delegate:nil cancelButtonTitle:NSLocalizedString(@"OK", nil) otherButtonTitles:nil, nil] autorelease] show]; });
As you’ve noticed, the dispatch_async
GCD function has no parameters
or return value. The block object that is submitted to this function
must gather its own data in order to complete its task. In the code
snippet that we just saw, the alert view has all the values that it
needs to finish its task. However, this might not always be the case. In
such instances, you must make sure the block object submitted to GCD has
access in its scope to all the values that it requires.
Running this app in iOS Simulator, the user will get results similar to those shown in Figure 2-1.
This might not be that impressive. In fact, it is not impressive at all if you think about it. So what makes the main queue truly interesting? The answer is simple: when you are getting the maximum performance from GCD to do some heavy calculation on concurrent or serial threads, you might want to display the results to your user or move a component on the screen. For that, you must use the main queue, because it is UI-related work. The functions shown in this section are the only way to get out of a serial or a concurrent queue while still utilizing GCD to update your UI, so you can imagine how important it is.
Instead of submitting a block object for execution on the main
queue, you can submit a C function object. Submit all UI-related C
functions for execution in GCD to the dispatch_async_f
function. We can get the same
results as we got in Figure 2-1, using C functions
instead of block objects, with a few adjustments to our code.
As mentioned before, with the dispatch_async_f
function, we can submit a
pointer to an application-defined context, which can then be used by the C
function that gets called. So here is the plan: let’s create a structure
that holds values such as an alert view’s title, message, and
cancel-button’s title. When our app starts, we will put all the values in
this structure and pass it to our C function to display. Here is how we
are defining our structure:
typedef struct{ char *title; char *message; char *cancelButtonTitle; } AlertViewData;
Now let’s go and implement a C function that we will later call with
GCD. This C function should expect a parameter of type void *
, which we will then typecast to AlertViewData *
. In other words, we expect the
caller of this function to pass us a reference to the data for our alert
view, encapsulated inside the AlertViewData
structure:
void displayAlertView(void *paramContext){ AlertViewData *alertData = (AlertViewData *)paramContext; NSString *title = [NSString stringWithUTF8String:alertData->title]; NSString *message = [NSString stringWithUTF8String:alertData->message]; NSString *cancelButtonTitle = [NSString stringWithUTF8String:alertData->cancelButtonTitle]; [[[[UIAlertView alloc] initWithTitle:title message:message delegate:nil cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil, nil] autorelease] show]; free(alertData); }
The reason we are free
ing the
context passed to us in here instead of in the caller is that the caller
is going to execute this C function asynchronously and cannot know when
our C function will finish executing. Therefore, the caller has to
malloc
enough space for the AlertViewData
context and our displayAlertView
C function has to free that
space.
And now let’s call the displayAlertView
function on the main queue and
pass the context (the structure that holds the alert view’s data) to
it:
- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ dispatch_queue_t mainQueue = dispatch_get_main_queue(); AlertViewData *context = (AlertViewData *) malloc(sizeof(AlertViewData)); if (context != NULL){ context->title = "GCD"; context->message = "GCD is amazing."; context->cancelButtonTitle = "OK"; dispatch_async_f(mainQueue, (void *)context, displayAlertView); } // Override point for customization after application launch. [self.window makeKeyAndVisible]; return YES; }
If you invoke the currentThread
class method of the NSThread
class, you
will find out that the block objects or the C functions you dispatch to
the main queue are indeed running on the main thread:
- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ dispatch_queue_t mainQueue = dispatch_get_main_queue(); dispatch_async(mainQueue, ^(void) { NSLog(@"Current thread = %@", [NSThread currentThread]); NSLog(@"Main thread = %@", [NSThread mainThread]); }); // Override point for customization after application launch. [self.window makeKeyAndVisible]; return YES; }
The output of this code would be similar to that shown here:
Current thread = <NSThread: 0x4b0e4e0>{name = (null), num = 1} Main thread = <NSThread: 0x4b0e4e0>{name = (null), num = 1}
Now that you know how to perform UI-related tasks using GCD, it is time we moved to other subjects, such as performing tasks in parallel using concurrent queues (see Performing Non-UI-Related Tasks Synchronously and Performing Non-UI-Related Tasks Asynchronously) and mixing our code with UI-related code if need be.