Blocks

With 10.6 apple introduced blocks to objective-c, a language feature primarily intended for use with Grand Central Dispatch.

However it can be very useful in other areas of coding. And if you’re not using 10.6 yet, those nice chaps at Plausible Labs have back ported the feature for the iphone, ipad and 10.5 with plblocks.

For those who don’t know ruby or smalltalk or lisp etc… a block is closure or anonymous function. I think it would be best to jump straight in with a simple example because describing closures is harder than using them.

I often find myself needing to loop over every element of an array except the last. This means I’m often typing something like this

for (int i = 0; i < [array count] - 1; i++) {

   NSSomething *object = [array objectAtIndex:i];

   [object doSomething];

   // ...
}

What I tend to do is forget to subtract 1 from the count. So I end up looping over all the items, which is a not what I want and is a mistake that can be frustratingly hard to spot.

It would be nice to type this once and the reuse the code. But how?

I could write a c function that accepts the NSArray and a callback function. The function would loop over the NSArray invoking the callback or each item.

To make it a little more like objective-c, I could extend NSArray with an instance method that accepts a selector.
The method loops over itself  invoking the selector for each item in the array.

Both these solutions have the same problem. I have to declare and define a new function/method and then move the logic into them. Away from where I want it.

This is where blocks become very useful. Lets extend NSArray with a new category method that accepts a block, which it invokes for each item in the array.


@interface NSArray (Blocks)
 -(void) all_butlast:(void(^)(id)) block;
@end

@implementation NSArray (Blocks)
-(void) all_butlast:(void(^)(id)) block {
    for (int i = 0; i < [self count] - 1; i++) {
        block([self objectAtIndex:i]);
    }
}
@end

So how do I go about using this new method?


NSMutableArray *data = [[NSMutableArray alloc] init];

for (NSUInteger i = 0; i < 100; i++) {
    [data addObject:[NSNumber numberWithInt:i]];
}

[data all_butlast:^(id member) {
    NSLog(@"%@\n", member);
}];

So here I get the best of both worlds. I reuse the all_butlast last code, and I get to declare my block inline exactly where I want it.

For readability I also use a typedef for the block declaration.


typedef void (^Block)(id);

@interface NSArray (Blocks)
-(void) all_butlast:(Block) block;
@end

@implementation NSArray (Blocks)
-(void) all_butlast:(Block) block {
    for (int i = 0; i < [self count] - 1; i++) {
        block([self objectAtIndex:i]);
    }
}
@end

Yoink!

Cor fanks for reading. Boing!