The Wonderful World of Objective-C Blocks

I know, I know. Blocks have been lingering around ever since the iOS 4 days. So why write a post on them now? Because they’re awesome. duh. Well, that and I feel they are extremely hard to grasp for newcomers (I surely had a difficult time wrapping my head around them at first; not to mention they’re just plain ugly at times – which is a turn off for some)

Overview


For those of you who haven’t heard of them, blocks (sometimes called closures) provide an easy way to pass a function as an arguement to a method. Most often, this is used in a “completion handler” fashion. Let’s take a closer look:

[object doAwesomeTaskWithCompletion:^(BOOL success) {
    if(success) { NSLog(@"Awesome task complete!"); }
}];

It’s as easy as that. In this example, object would perform it’s awesome task, then call the completion block that we provide. Neat huh? If you’re wondering where the boolean “success” came from, object’s doAwesomeTaskWithCompletion: method provides it. As with everything, it didn’t just appear out of thin air; but more on that later.

Why Should I Learn Them?


If you’ve ever delved into Objective-C delegation, you know how messy it can make things. Not to mention the numerous lines of code needed to get delegate calls up and running. In a lot of cases, blocks can eliminate the need for delegation by providing inline callbacks. They have access to variables in your current scope, which just makes the life of a developer so much easier. In Objective-C, blocks are also treated as objects – meaning they can be stored and passed around like any other object you encounter. Long story short, they have the potential of tremendously improving the readability of your code, as-well-as shortening source files. What crazy lunatic would not want those two things?!

Blocks As Arguments


In the example above, object ran it’s awesome task, then called the “completion” block we provided. Let’s look at object’s doAwesomeTaskWithCompletion interface

- (void)doAwesomeTaskWithCompletion:(void(^)(BOOL success))completion;

Well that’s certainly not your typical method definition. In this case, our method takes a block object (named completion locally) of type void, and provides the boolean success. The caret ( ^ ), is what marks our argument as a block. Now let’s look at the implementation:

- (void)doAwesomeTaskWithCompletion:(void(^)(BOOL success))completion
{
    // Do some awesome stuff here!
    completion(true);
}

Easy enough. In a real world example, we would do some task, determine whether it was successful or not, then pass a boolean based on the task’s success to our completion object. Remember when I said the boolean “success” didn’t just appear out of thin air earlier? Well now you know exactly where it came from. I bet you feel special.

Again, to call this method we would do the following:

[object doAwesomeTaskWithCompletion:^(BOOL success) {
    if(success) { /* Do stuff here */ }
}];

Blocks As Variables


Because blocks are treated like objects in Objective-C, they can also assigned and stored for later. This is an extremely powerful feature of blocks, and can be used in many different scenarios. Consider this:

You have a parent UIViewfor example’s sake let’s just call it view (for the love of all that is holy, please refrain from actually naming your objects with such a non-descriptive naming schema. If you ever work with a other people, they will kill you. Not joking.) We also have a child UIView of view called childView; childView also has a UIButton in it called button.

If you’re lost it’s okay, I’m just trying to paint a scenario.

Now let’s say you don’t want to give direct access to your childView’s button; but you still need to allow an action to be assigned when an instance of your child UIView is created. To accomplish this, we can make a public block property on our child UIView. Check out the child UIView’s interface:

#import <UIKit/UIKit.h>

typedef void (^ButtonActionBlock)(void);

@interface ChildView : UIView
@property (nonatomic, copy) ButtonActionBlock actionBlock;
@end

First thing you probably noticed is the typedef statement. We need to define our own type of variable so our classes know what to expect from our block. Notice again the caret ( ^ ) that denotes a block, and the trailing “(void)” that represents what the block provides (In this case void or nothing). The rest is pretty standard. We create a property of our new ButtonActionBlock type so we have the freedom to assign an action later without directly interacting with the button. Now let’s look at the implementation:

#import "ChildView.h"

@interface ChildView ()
@property (nonatomic, strong) UIButton *button;
- (void)buttonTapped:(id)sender;
@end

@implementation ChildView

- (ChildView *)initWithFrame:(CGRect)frame
{
    if(self = [super initWithFrame:frame]) {
        _button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        [_button setTitle:@"Click Me!" forState:UIControlStateNormal];
        [_button addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
        [_button sizeToFit];
        _button.center = CGPointMake(100, 50);
        [self addSubview:_button];
    }
    return self;
}

- (void)buttonTapped:(id)sender
{
    if(_actionBlock) { _actionBlock(); }
}

@end

We create our private UIButton, and it’s respective private action which calls our actionBlock. When buttonTapped: is called, we check to make sure our actionBlock is not nil, then call it if applicable.

Now back in our parent UIView view, we would simply assign to childView’s actionBlock property like so:

childView.actionBlock = ^{
    NSLog(@"Button tapped!");
};

Simple huh? One thing to note, when a block provides no variables (or rather, when it is void) we can drop the parentheses and wrap our code in just standard brackets. We still need to include the caret to denote it as a block though!

Conclusion


This is by no means all that blocks are capable of, nor was this supposed to be an all-inclusive guide to them. I think this is a good starting point for those new to the concept. Getting comfortable with blocks is something that everyone should do. They’re small, efficient, pretty (well at least most of the time), and can make your life easier.