My GitHub page, containing various open-source libraries for Mac and iOS development, and some miscellaneous projects

Friday Q&A 2008-12-26

Welcome to another Friday Q&A. This week I thought I would take fellow amoeboid Jeff Johnson's suggestion and talk about blocks in Objective-C.

The word "blocks" is kind of ambiguous, so to clarify, I'm not talking about the compound statement structure which has existed in C since the beginning of time. I'm talking about a new addition to the language being created by Apple which adds anonymous functions to the language.

Since they're not available to the public in finished form yet, the discussion is going to be a bit imprecise in terms of syntax. But since I mainly want to talk about what they will do for us and not the absolute precise details of how to type them out, that's not a big problem. First let's see how they look:

x = ^ { printf ( "hello world

" ); }

x ();

x = ^ ( int a , char * b ){ printf ( "a is %d and b is %s" , a , b ); }

x ( 42 , "fork!" );

int a = 42 ; char * b = "fork!" ; x = ^ { printf ( "a is %d and b is %s" , a , b ); } x ();

int a = 42 ; char * b = "fork!" ; callblock ( ^ { printf ( "a is %d and b is %s" , a , b ); });

callblock()

That's a block. The funny caret before the braces is what distinguishes it from boring old compound statements. Now we can simply call this block like so:And the resulting code will print "hello world". Now let's introduce a couple of parameters:And then we can call this just the way you'd think:Now let's remove the parameters again:This illustrates one of the really interesting things about blocks: they can capture variables from their enclosing scope. This is not particularly interesting here (why didn't we just pass a and b when invoking it?) but it gets really interesting when we start passing the block around to other functions:When thefunction calls that block, the block will still get access to our local variables a and b even though we never passed them to the function explicitly.

We're just about done with the basics of what blocks are. One more quick example, a block that returns a value:

x = ^ ( int n ){ return n + 1 ; }; printf ( "%d

" , x ( 2 ));

This code will print "3". Note that there is no need to declare the type of the return value as the compiler can simply infer it from the return statement.

So what's the big deal? A major advantage of blocks is that they essentially allow you to write your own control structures in the language without having to alter the compiler. As one example, take the for(... in ...) syntax that appeared in Leopard. This syntax is a wonderful addition to the language. Previously we had to write a bunch of code just to iterate over an array:

NSEnumerator * enumerator = [ array objectEnumerator ]; id obj ; while (( obj = [ enumerator nextObject ])) // finally we can do something with obj

for ( id obj in array )

my_for ( array , ^ ( id obj ){ /* loop body goes here */ });

[ array do :^ ( id obj ){ /* loop body goes here */ }];

-do:

And the new syntax cuts this down to a single line:Which is great. The only trouble is that we went years and years without it. We had to wait for Apple to add it for us. With blocks, no more! You don't get quite the same syntax, but you can get the same convenience with a method you wrote entirely yourself:Or in a perhaps slightly stranger but much more interesting object-oriented form:The implementation of themethod is left up to the reader, but rest assured that it's relatively simple.

As another example, consider the @synchronized directive. This could be redone using blocks too:

[ obj synchronized : ^ { /* this is protected by the lock */ }];

for/in

@synchronized

OK, you say, I get it, but what's the big deal? After all,andare already part of the language, why would you rewrite them?

Of course you wouldn't. That would be silly. Those examples serve only to illustrate the idea: that you can build your own control structures. But of course it's only interesting to build control structures that are new! So here are some ideas.

Open a file and ensure that it gets closed when you're done: [[ NSFileHandle fileHandleForReadingAtPath : path ] closeWhenDone : ^ ( NSFileHandle * handle ){ /* use handle here */ }];

Build a new array by working with the objects of an existing one: newArray = [ existingArray map : ^ ( id obj ){ return [ obj stringByAppendingString : @"suffix" ]; }];

Filter the contents of an array: newArray = [ existingArray filter : ^ ( id obj ){ return [ obj hasPrefix : @"my" ]; }];

Main thread synchronization: /* threaded code */ PerformOnMainThread ( ^ { /* synchronized code */ }); /* more threaded code */

Delayed execution: PerformWithDelay ( 5.0 , ^ { /* will run 5 seconds later */ });

Parallel enumeration: [ array doParallelized : ^ ( id obj ){ /* will get executed on all of your CPU cores at once */ }];

And many other examples abound.

Another place where blocks will make things much nicer is when dealing with callbacks. If you've ever written much Cocoa code you've probably had to write a sheet callback, and it's a pain in the ass. If you need to pass variables through to the other side then it gets really frustrating with code like this:

- ( void ) method { int foo ; NSString * bar ; /* do some work with those variables */ NSDictionary * ctx = [[ NSDictionary alloc ] initWithObjectsAndKeys : [ NSNumber numberWithInt : foo ], @"foo" , bar , @"bar" , nil ]; [ NSApp beginSheet : sheet modalForWindow : window modalDelegate : self didEndSelector : @selector ( methodSheetDidEnd : returnCode : contextInfo :) contextInfo : ctx ]; } - ( void ) methodSheetDidEnd :( NSWindow * ) sheet returnCode :( int ) code contextInfo :( void * ) ctx { NSDictionary * ctxDict = ctx ; [ ctxDict autorelease ]; int foo = [[ ctxDict objectforKey : @"foo" ] intValue ]; NSString * bar = [ ctxDict objectForKey : @"bar" ]; /* do some more stuff with those variables }

- ( void ) method { int foo ; NSString * bar ; /* do some work with those variables */ [ sheet beginSheetModalForWindow : window didEndBlock : ^ ( int code ){ /* do stuff with foo */ /* do stuff with bar */ /* do stuff with code, or sheet, or window, or anything */ }]; }

Wow! What a pain that is. Since I removed all the stuff that does work, nearly everything that remains is just boilerplate. Horrible boilerplate whose only purpose is to tell the sheet who to call, and to pack up local information in a way that the sheet can give it back to you later on. Now let's imagine we were redoing this API using blocks and see how it would look:Isn't that great? All that horrible boilerplate just flies right out the window. Code flow suddenly becomes completely logical, you can read it top to bottom, and you can access any local variables you please.

Let's take another example, sorting an array with a custom comparison function using some variables that you pass in. NSArray has functionality for this, with the -sortedArrayUsingFunction:context: method. The old-style code is annoying, and I'm not going to write it. It's much like the sheet method above. You have to define a separate function, way outside of your code where it's not really visible. You have to set up the context to pass into it. If you're passing more than one thing then you have to pass a dictionary (and unpack it) or a pointer to a struct. Now here's the blocks version of a custom comparator:

sorted = [ array sortedArrayUsingBlock : ^ ( id a , id b ){ /* compare, use local variables to decide what to do, run wild */ }];

And that's all there is to it.

Callbacks are one of the most powerful things in C and Objective-C but in many situations their use can be extremely difficult and unnatural. Blocks promise to allow callbacks and custom control constructs to be created and used in a much more natural fashion.

So far I've only shown examples of using a blocks API, but how about creating one? Well, it's a little worse, but not much. The only problematic thing is that the syntax for declaring a block type is kind of ugly, as it's modeled after function pointer syntax. But it's not too bad, and the rest is nice and simple. For example, here's how you could write that -map: method from above:

- ( NSArray * ) map :( id ( ^ )( id )) block { // takes an id, returns an id NSMutableArray * ret = [ NSMutableArray array ]; for ( id obj in self ) [ ret addObject : block ( obj )]; return ret ; }

Pretty straightforward, especially considering the power it gives us.

Information on Apple's implementation of blocks is still a bit sparse. Some more details can be found in a mailing list post to the Clang development list. For more purely conceptual ideas on how blocks can be used, check out the Smalltalk language, where blocks are used for virtually every control structure right down to if/then and basic loops. Here's hoping that blocks allow for some major changes in how we work on Snow Leopard!

That wraps it up for this Friday Q&A. Be sure to come back next week for another round. Keep those suggestions coming. Post suggestions in the comments or e-mail them directly. I may use your name unless you tell me otherwise, so say so if you want to remain anonymous.

Did you enjoy this article? I'm selling whole books full of them! Volumes II and III are now out! They're available as ePub, PDF, print, and on iBooks and Kindle. Click here for more information

Comments:

Add your thoughts, post a comment:

Spam and off-topic posts will be deleted without notice. Culprits may be publicly humiliated at my sole discretion.

JavaScript is required to submit comments due to anti-spam measures. Please enable JavaScript and reload the page.