Adoption Curve Dot Net

Blocks in Objective-C (Without Tears)

| Comments

Blocks are a feature of Objective-C 2.0, and were introduced to iOS in version 4.0. They’re a fantastically-useful feature of the language, and they’re being used in more and more places as Apple introduces new classes into the iOS SDK. Apple clearly see blocks as being central to the way that iOS is going to evolve in the future.

Understanding what blocks are, and how they work, is key to being able to exploit the features of the SDK. At first glance blocks can seem a bit impenetrable – their syntax isn’t as clear as other parts of Objective-C, and there’s a certain amount of apparent black magic taking place behind the scenes.

But once you get to grips with blocks, you’ll quickly realise how powerful and flexible they can be; and you’ll be well-placed to deal with the evolution of Objective-C and the iOS SDK in the future.

Blocks in other languages

If you’ve come to Objective-C from other languages, you may well have come across the functionality of blocks under another name – for example, closures or lambdas. The basic operation of blocks is very similar to the way that closures are used in other languages – albeit that the Objective-C syntax perhaps isn’t as clear as it could be.

If block, closures or lambdas mean nothing to you yet – skip this section and I’ll start from the very beginning.

Otherwise, here’s a quick example that will be familiar if you’re a Rubyist:

[1, 2, 3, 4].each do |i|
  puts i
end

This uses the each operator to iterate across the array, and passes the results of the each operator into the i variable. The variable then gets printed out.

Objective-C blocks take the feature that’s been available as closures in other languages for some time, and exploits them as part of the iOS SDK.

Blocks – what are they good for?

Blocks are used in Objective-C for two main purposes:

  • As a way of encapsulating small chunks of code as an alternative to methods

  • As an alternative to callbacks or delegate methods.

Don’t worry if that doesn’t immediately make a lot of sense – it’ll become clearer as you look at blocks in more detail.

The syntax of blocks

Blocks are indicated by the block operator – the caret character, or ^. This denotes a block in the same way as the splat * operator denotes a pointer.

One of the most confusing aspects of blocks is the syntax. This is just my personal opinion, but the Objective-C block syntax is actually very un-Objective-C-like – it’s far more confusing than it could be. But that said, let’s take a look at the basic syntax of a block in an attempt to decode it.

Don’t worry if this doesn’t make any complete sense right now. I’ll unpack the meanings as we go through the next section.

Block literals

The simplest way of declaring a block literal looks like this:

^{
  // This is the body of the block literal
  NSLog(@”This is the body of the block literal”);
  // Do other stuff…
  // ...and some more stuff
}

The block is denoted by the ^ caret character, and the body of the block is enclosed between the { } braces.

Inside the block can be placed virtually any Objective-C code (with certain exceptions, which we’ll look at along the way.)

Block variables

We can also declare blocks as block variables – this is a bit like a normal variable, but instead of a value, the block variable contains – well, a block of code.

BOOL (^myBlock)(int) = ^(int theVariable) {
  // Code goes here
}

Unpacking that, we get:

You’d call that block with something like:

BOOL result = myBlock(5);

If you think this looks very much like a C function – well, you’d be right. In many ways, blocks behave and can be treated as functions.

It might help here to think back to the way that functions are declared in plain ‘ole C:

(int)myFunction(int parameterOne, int parameterTwo) {
  // code goes here
  return anInt;
}

If we were to write that C function as an Objective-C block, it would look like:

int (^myFunction)(int, int) = ^(int parameterOne, int parameterTwo) {
  // do something here
  return anInt;
}

Blocks can, of course, take more than one parameter:

BOOL(^myBigBlock)(int, float) = ^(int foo, float bar) {
  // something happens here
}

That would get called by

BOOL secondResult = myBigBlock(5, 3.1415);

Sometimes it helps to read the code out loud – I’d read this as:

“The block called myBigBlock returns a BOOL, and takes two parameters – an int parameter called foo and a float parameter called bar.”

You can see that block syntax deals with return values and parameters in a very similar way to C.

And of course, you can pass in Objective-C object as parameters (and return objects from the block):

(NSString *)(^myObjectBlock)(int, NSString *, id) = ^(int foo, NSString *barString, id bash) {
  // do stuff with the parameters
  return returnString;
}

I’d read this out loud as

myObjectBlock is a block that returns a pointer to an NSString, and takes three parameters – an int called foo, a pointer to an NSString called barString and a pointer to an id called bash.”

Putting it all together

To round this all off, let’s put it all together with a bigger example. It’s the Objective-C equivalent of nonsense verse, but it shows how everything fits together:

NSString *(^myBigBlock)(int, NSString *, NSArray *) = ^(int foo, NSString *barString, NSArray *bash){
  NSInteger count = [bash count];
  NSInteger result = count * foo;
  return [NSString stringWithFormat:@"You passed %@ in and the result is %d", barString, result];
};

NSString *theString = @"this is a string!";
NSArray *theArray = [NSArray arrayWithObjects:@"1", @"2", @"3", nil];
NSString *stringFromTheBlock = myBigBlock(5, theString, theArray);
NSLog(@"stringFromTheBlock = %@", stringFromTheBlock);

Here we’re declaring a block that returns a pointer to an NSString and takes three parameters – an int, and pointers to an NSString and an NSArray:

NSString *(^myBigBlock)(int, NSString *, NSArray *)

The block itself is defined inside the braces:

^(int foo, NSString *barString, NSArray *bash){
 NSInteger count = [bash count];
 NSInteger result = count * foo;
 return [NSString stringWithFormat:@"You passed %@ in and the result is %d", barString, result];
};

Using blocks in multiple places

If you’re going to use a block in multiple places, then best practice is to create a type for it:

typedef float (^MyBlockType)(int, float);

You can then define a block called firstBlock with:

MyBlockType firstBlock = ^(int foo, float bar){
  return foo * bar;
};

And used with something like:

float result = firstBlock(5, 1.3);

Types for blocks with Objective-C objects can be created in a similar way:

typedef NSString* (^MyObjectyBlock)(int, NSArray*, NSString*);

You’d define this with something along the lines of:

MyObjectyBlock secondBlock = ^(int foo, NSArray *theArray, NSString *theString){
  // something exciting happens here
  return aStringResult;
};

Assuming that you’ve previously declared an NSArray called fooArray and an NSString called barString, secondBlock would be called thus:

NSString *blockString = secondBlock(5, fooArray, barString);

Where next?

Having provided what I hope is a practical introduction, in the next post I’ll use the Apple documentation as a starting point to look at blocks and how they work in more detail.

Comments