Thursday, July 5, 2012

Blocks and Grand Central Dispatch

What are those so called blocks?

Blocks are chunks of codes which can be used for future execution. All right, blocks are more than that, but that's the basic idea. The good thing about blocks is that they can be used as method parameters, method return values you can pass them through objects and so on.

How does blocks look like?

Let's start with an example:

^(int number){return number > 100 ? YES : NO;};

Weird right? That's what I thought when I first saw it too. But it's not that hard. Let me explain. The little caret's (^) function is to declare that from that point we're dealing with a block. After it we have an optional part, the parameter(s). In our case an int named number. And within the curly brackets we have the code that will be executed.


That's pretty easy. If we want to assign it to a variable, that can be done this way:

BOOL(^isBiggerThanAHundred)(int) = (BOOL)^(int number){return number > 100 ? YES : NO;};

After this little declaration we can use it easily:

BOOL result = isBiggerThanAHundred(22);

As I said before the return value and the parameters are optional, so let's see an example for that case as well:

void(^spamLog)(void) = ^{NSLog(@"spam");}

The usage is the same as before:

spamLog();

Blocks are closures

Blocks are smart! Blocks can take a snapshot of their surroundings and encapsulate that state within themselves. This means that if you have a local variable defined before the block and you use it within the block, the block will be able to access it even if it's execution is delayed. More to that the value of the local variable within the block won't change.

NSData * date = [NSDate date];
void(^dateBlock)(void) = ^{
    NSLog(@"Date is %@", date);
}

The interesting part is that if you call the dateBlock(); method it will give you the current date in this example. But if you call it again a little later, you'll see exactly the same value because the block is not using the actual date variable we created there. It will use the snapshot it created in the specific time when it was created.

Why to use them?

First of all, you have to. More and more objects have methods that are working with blocks. For example UIView animations.

[UIView animateWithDuration:1.0f
                     animations:^{
                       someView.alpha = 1.0f;
                     }
                     completion:^(BOOL finished) {
                         if (finished) {
                           someOtherView.alpha = 0.0f;
                         }
                     }];

But there's more than that. You'll be able to pass through callback code chunks, you'll be able to give an executable code to another object with a snapshots of the block's scope and so on. And at least but not last it's worth using because of GCD.

Grand Central Dispatch


GCD is what makes blocks really awesome. It makes multithreading easy. With GCD you can use/create queues where you can submit blocks to be executed. There are three types of queues we can use:
  • Main
    Tasks will be executed serially on the app's main thread.
  • Concurrent
    Tasks will be fired in a FIFO order concurrently, and the submitted tasks can finish in any order.
  • Serial
    Execution in a FIFO order but only one task can run at a time.
The main queue is always there. It's the main thread you're application is running on. You can access it by calling dispatch_main.

You can use concurrent queues to execute blocks concurrently. Concurrent queues are global to your application. You can request global queues by calling dispatch_get_global_queue. Concurrent queues are created by the GCD itself and they are only different by their priority level. e.g.

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)

By default there are no serial queues provided by GCD, you have to create/manage them yourself.

dispatch_queue_t backgroundQueue = dispatch_queue_create("com.victo.someapplication.bgqueue", NULL);

Putting things together

At this point, probably we can create blocks, we can use queues given by GCD. So let's put these things together.

- (void)startAsync {
    dispatch_async(backgroundQueue, ^(void) {
        [self doSomethingAsync];
    });
}

- (void)doSomethingAsync {
    // run some code here (http request, file operation, etc...)
    dispatch_async(dispatch_get_main_queue(), ^(void) {
        [self refreshUI];
    });
}

What did we do above? when calling the startAsync method, it's using the backgroundQueue (declared before) to run our code asynchronously. After the doSomething method did what it wanted it will run another block of code on the main thread to refresh the UI.

By using blocks and GCD, you can forget NSOperation and NSInvocationOperation, in most cases you won't need to use NSThread any more. Isn't it fun? I think it's cool.

That's all for now. You can find more information at developer.apple.com about GCD and blocks if you need more.

No comments:

Post a Comment