iOS Programming Recipe 27: Implementing Blocks Part II

Last time we left off in our block discussion I had gone over some of the basics of blocks. How to implement them, call them, and gave a bit of insight on why they are cool. Now, there is one really big benefit to using blocks and that is Grand Central Dispatch. There are of course many other neat things you can do with blocks and we’ll briefly cover a few of those too.

Assumptions

So What Is Grand Central Dispatch?

Today, I’ll use wikipedia as my crutch:

Grand Central Dispatch (GCD) is a technology developed by Apple Inc. to optimize application support for systems with multi-core processors and other symmetric multiprocessing systems.

Ok this doesn’t tell us a whole bunch, but what it does tell us is that it is a technology developed for multi-core processors. All you need to know is GCD is a set of tools that Apple has provided that allow us to perform blocks serially or concurrently on various queues (which map to either the main thread or another GCD managed thread). We also have the option of doing this either synchronously or asynchronously.

Enought Talk, Let’s See it in Action!

In Recipe 25 , the first part of our blocks discussion, we left off with a simple completion handler that notified us when a method was done counting to 10000. This time we’re gonna kick it up a notch and move this counter over to another thread. We’ll do this with GCD. This time, instead of logging the string “Ten Thousand Counts Finished” to the screen, we’ll create a label to handle this.

To give you some appreciation, we’ll first take a look at what happens without GCD. I just created a simple single view application for this with a single .xib file. On my layout, I added a text view with 10 paragraphs of greeking courtesy of duckisland.com. I also added a text label at the top. The finished view looks pretty simple:

Recipe 27 Image 1

Go ahead and create an outlet for the label and title it “countsFinished”.

Next , modify they viewDidLoad method as follows:


- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    int x = 1;
    while (x < 10001) {

        NSLog(@"%i", x);
        x++;
    }
        self.countsFinished.text = @"Ten Thousand Counts Finished";

}

Now if you run this now, you’ll see the output window counting away but the simulator screen will be completely black and unresponsive. This is because the thread has to wait for the count to stop before it can set the text label and continue loading the UI elements. In short, this is bad. Now Let’s do it a better way.

Replace the code in the viewDidLoad method with the following:


dispatch_queue_t countQueue = dispatch_queue_create("counter", NULL);
dispatch_async(countQueue, ^{

    int x = 1;
    while (x < 10001) {

        NSLog(@"%i", x);
        x++;
    }

    self.countsFinished.text = @"Ten Thousand Counts Finished";

});

Now if you run it, You’ll see that it counts fine as you would expect. When it’s done counting however, The label still says “Label”. This is because you can only call UIKit on the main thread.

To fix it, You’ll need to move it back on to the main thread. now change the code again as follows:


dispatch_queue_t countQueue = dispatch_queue_create("counter", NULL);
dispatch_async(countQueue, ^{

    int x = 1;
    while (x < 10001) {

        NSLog(@"%i", x);
        x++;
    }
    dispatch_async(dispatch_get_main_queue(), ^{

self.countsFinished.text = @"Ten Thousand Counts Finished";
    });
});

Here, we are using “dispatch_get_main_queue” to get the main thread where we can now make our UIKit call.

Now if you run it, You’ll notice that once the count is finished, the label updates. You will also notice that the view is responsive and allows you to scroll through the text view while you wait for the count to finish. Your finished view should now look like the following:

Recipe 27 image 2

This is a simple example, but you can see how this would be very useful when it comes to loading images or data say in a tableview being populated by a twitter feed. You can load your tableview and be able to scroll through items already loaded while the application is waiting to download the rest of the information from the web.

A Note on Concurrency

Now for the sake of simplicity we opted to use a serial task to run our block on another thread. This was denoted by NULL in our dispatch_Queue_create() method. Serial just means that the processes are first in first out or FIFO. In other words, the second block doesn’t start on any thread until the first block is done.

Conversely, we could do this concurrently (replace NULL with DISPATCH_QUEUE_CONCURRENT). For this example it doesn’t make much sense since we only have one block. Suppose we had two blocks however, then they would start in order (dequeue) but they wouldn’t necessarily finish in order. This property makes them useful for handling a lot of data such as three seperate calls to a server that each load information that is independent of each other.

Using BLocks for Enumeration

Blocks can also be helpful when it comes to enumeration, You can call a block method for both an NSDictionary and NSArray. First we’ll show you an example using the NSDictionary. You can go ahead and tack this into your viewDidLoad method if you would like as I have done in the following bit of code:


NSDictionary *dictionaryOfColors = [NSDictionary dictionaryWithObjectsAndKeys:@"Red",@"color 1",@"green",@"color 2",@"blue",@"color 3",@"cyan",@"color 4", nil];

NSLog(@"Enumerated Colors:");
[dictionaryOfColors enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    if([key isEqual: @"color 3"])
    {

        NSLog(@"Hey we found blue");
    }

    NSLog(@"Key %@ is %@ color",key,obj);
}];

Now if you run it, you should see something similar to this in the output window:


2013-07-26 12:37:44.190 funWithBlocks[19237:1e03] 1
2013-07-26 12:37:44.190 funWithBlocks[19237:c07] Enumerated Colors:
2013-07-26 12:37:44.192 funWithBlocks[19237:1e03] 2
2013-07-26 12:37:44.192 funWithBlocks[19237:c07] Key color 2 is green color
2013-07-26 12:37:44.192 funWithBlocks[19237:1e03] 3
2013-07-26 12:37:44.192 funWithBlocks[19237:c07] Hey we found blue
2013-07-26 12:37:44.193 funWithBlocks[19237:1e03] 4
2013-07-26 12:37:44.193 funWithBlocks[19237:c07] Key color 3 is blue color
2013-07-26 12:37:44.194 funWithBlocks[19237:1e03] 5
2013-07-26 12:37:44.194 funWithBlocks[19237:c07] Key color 1 is Red color
2013-07-26 12:37:44.194 funWithBlocks[19237:1e03] 6
2013-07-26 12:37:44.194 funWithBlocks[19237:c07] Key color 4 is cyan color
2013-07-26 12:37:44.195 funWithBlocks[19237:1e03] 7

This is because we left our previous block call in that is counting on a seperate thread and reporting it’s NSLog values at the same time. I purposely left this in there to demonstrate fully that we are in fact working with two different threads.

If you would like a bit cleaner output, comment out the NSLog counter output:


        //NSLog(@"%i", x);

Then your output should look more like the following;


2013-07-26 12:43:28.024 funWithBlocks[21007:c07] Enumerated Colors:
2013-07-26 12:43:28.026 funWithBlocks[21007:c07] Key color 2 is green color
2013-07-26 12:43:28.026 funWithBlocks[21007:c07] Hey we found blue
2013-07-26 12:43:28.026 funWithBlocks[21007:c07] Key color 3 is blue color
2013-07-26 12:43:28.027 funWithBlocks[21007:c07] Key color 1 is Red color
2013-07-26 12:43:28.027 funWithBlocks[21007:c07] Key color 4 is cyan color

You can also escape the enumeration by setting the BOOL “stop” in our example to YES.


key, id obj, BOOL *stop) {
    if([key isEqual: @"color 3"])
    {

        NSLog(@"Hey we found blue");
        *stop = YES;
    }

Which yields a similar result but stops once it sees blue.

2013-07-27 23:45:31.063 funWithBlocks[43974:c07] Enumerated Colors:
2013-07-27 23:45:31.064 funWithBlocks[43974:c07] Key color 2 is green color
2013-07-27 23:45:31.065 funWithBlocks[43974:c07] Hey we found blue
2013-07-27 23:45:31.065 funWithBlocks[43974:c07] Key color 3 is blue color

You can also use the block approach with arrays. The code would look like the following:


[anArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    if(idx == 1) {
        *stop = YES;
    }
    NSLog(@"Array object %@ at index: %d", obj, idx);
}];

Alright! So now you have a few new tools to work with! In a future recipe we’ll talk about NSOperation classes and NSOperationQueue. These give you even more tools to work with concurrency and multithreading. As always, feel free to leave a comment if you have any questions.

About Joe Hoffman

I am An Electrical Engineer that spends a lot of my off time doing web development and other programming. Currently I am trying to expand my knowledge with iOS and I find that writing tutorials helps me learn more thoroughly as well as provide some useful info to others.

Comments

  1. Lokendra says:

    the term “idx” is not working in xcode 5… what should we use in xcode 5??

    • Joe Hoffman says:

      Hi Lokendra!

      I’m not sure why idx would not be working as it’s just a variable. You can choose anything you want for this. perhaps you can use something that makes sense like “objectIndex”

Speak Your Mind

*

css.php
Privacy Policy