Blocks are a great way to encapsulate reusable units of code that still require a degree of customization. They let you abstract out common patterns, leaving behind more concise code that yields to the programmer for the details. But blocks are a relatively new concept for Obj-C developers, and it isn’t always clear when you can put them to use. Small refactorings are a great place to start. They’ll improve the quality of your code and accustom you to the quirky syntax.

Code that calls, Refactor me!

I recently came across one such perfect opportunity for putting blocks to use. The code initializes an array with NSNumber objects. The required number of objects is not known at compile time, so the array must be dynamically allocated:

modelArray = [[NSMutableArray alloc] init];
for (int index = 1; index <= totalPages; index++) {
  [modelArray addObject:[NSNumber numberWithInteger:index]];
}

This is poor style for three reasons. First, initializing this array is necessary but the programmer is exposing the details in a method that doesn’t care about them. This detracts from the purpose of the method.

Second, and most egregiously, modelArray should not be mutable. Code elsewhere depends on this array being set once and never changed. Leaving it mutable opens the program to bugs. Our original programmer could simply copy the array when he’s finished, but then we’re still leaving the implementation details.

Third, the code is using a general pattern that will have to be copied and ever so slightly modified anywhere else it’s used. This makes it a perfect target for a blocks based refactoring.

Identify the pattern

Our first step is to identify the pattern. We’re initializing an array, we’re iterating through a loop, and we’re adding objects to the array that depend on their position in it. We want an array initialization method that adds objects to an array, but the objects must reflect their position in the array. That’s the general pattern.

But we want the user of our API to create those objects himself. That’s the details. In our case it’s numbers, but it might be strings, other arrays, dictionaries or anything else. It’s this point that essentially identifies the pattern and also reveals why we want to use blocks for our implementation.

Put the pattern and the details together, and we’re going to need initialization code that can pass the index to the user, get back a new object, and insert that object into the array at the right place, for every index i up to some count n. It sounds like we need a function that the user provides and that our initialization method can call for each index i. Which is exactly what a block is.

Blocks, briefly

Blocks are functional units of code that you can treat like values. Like values, you can store them in variables, keep them around for later use, copy them and pass them to other methods. But like functions, you can call them, they can take arguments, and they can return a value.

A function that takes a block as an argument is called a higher order function. In effect, you are passing a function to a function. The receiving function can call the block as it needs it, possibly many times, each time passing different arguments and receiving a return value back.

Initializing an array with a block

Hopefully the picture is starting to come together. Our array initialization function will take a block that itself takes a single parameter: the current index. Inside that block, the user will create a new object that uses the index and return it. Our function will accept that object and insert it into the array at the current index. Our function also needs a parameter for the size of the array n, which indicates how many times the block will be called.

Because this is code that initializes an NSArray, we want it to be a method on the NSArray class. And because we’re in a dynamic language, we can simply open up NSArray with a category and add our method to it. Here’s the header file:

#import <Foundation/Foundation.h>

@interface NSArray (BlocksInit)
- (id) initWithCount:(NSUInteger)count block:(id(^)(NSUInteger index))block;
@end

The block parameter is that funky looking (id(^)(NSUInteger index))block bit. The syntax begins with the return type of the block, id in our case. The following caret identifies this as a block type. Finally we have the parameters as a comma separated list of types and optionally names. In our case, the block takes a single parameter of type NSUInteger. The whole thing is wrapped in parenthesis.

Our implementation file is straightforward:

#import "NSArray+BlocksInit.h"

@implementation NSArray (BlocksInit)

- (id) initWithCount:(NSUInteger)count block:(id(^)(NSUInteger index))block
{
  NSMutableArray *array = [[NSMutableArray alloc] init];
  for ( NSUInteger i = 0; i &lt; count; i++ ) {
    id element = block(i);
    if (element) [array addObject:element];
  }
  return [array copy];
}

@end

The code is almost identical to the original material we’re refactoring, with the exception that instead of creating an NSNumber object ourselves with index i, we call the block with i as the parameter for each index i from 0 to count. Then all we need to do is check that we didn’t get nil back and insert it into the array. We might modify that code so that we insert an NSNull placeholder value instead or raise an exception. When we’re finished, we return a copy of the array, which will be immutable.

Using the code

Using this code is more straightforward than creating it, partly because Xcode does a fine job of auto-completing for us. Don’t forget to include the header to our NSArray category, and then replace the original implementation with this:

modelArray = [[NSArray alloc] initWithCount:totalPages+1 block:^id(NSUInteger index) {
  return [NSNumber numberWithInteger:index+1];
}];

You can see that we’re calling our initWithCount:block: method and passing in a block that is a single line. All it does is create an NSNumber that wraps the current index and return it, but this single line of code will be called n times with a different value for index each time. Again we have that odd bit of syntax, all the more confusing for reversing the order of the return type and caret and getting rid of some of the parenthesis.

It’s also worth mentioning the use of return inside the block. Because a block is a function, we’re returning the value from the block, not from the method in which we’re using the block.

The improvements are apparent. We no longer have a mutable array lying around. Our code to initialize the array is now effectively a single line. We’ve hidden the implementation details inside a category on NSArray, improving the method that calls this code, and we’ve created a re-usable method for initializing arrays that can be appropriated elsewhere. What more could you ask of a refactoring?

In summary

Blocks are a powerful feature of modern Obj-C. But they can be confusing, and it’s not always apparent where they can be put to use. Start with small improvements such as this one where you can incorporate blocks into simple refactorings. Although it may not seem like much, little improvements like this add up and can significantly improve the readability and density of your code. And you’ll be learning blocks along the way.

Leave a Reply