What a Block really Is
After quite a while of thinking that Objective-C blocks did some mean magic on the stack, it simply took me seriously using C++’s lambdas (their implementation of the concept) that I realized what blocks are.
Effectively, a block is simply a declaration of a class, plus an instantiation of one instance of that class, hidden under syntactic sugar. Dont believe me? Well, lets have a look at C++ lambdas to clear things up:
MyVisitorPattern( [localVariableToCapture]( MyObject* objectToVisit ) { objectToVisit->Print(localVariableToCapture); }, 15 );
The red part is a C++ block. Its pretty much the same as an Objective-C block, with two differences:
- You explicitly specify which local variables to capture in square brackets.
- Instead of the ^-operator, you use those square brackets to indicate that this is a block.
Seeing the captured variables specified explicitly listed here, like parameters to a constructor, made me realize that that’s really all that a block is. In-line syntax to declare a subclass of a functor (i.e. an object whose entire purpose is to call a single of its methods), and return you an instance of that class. In ObjC-like pseudo-code, you could rewrite the above statement as:
@interface MYBlockSubClass : NSBlock { int localVariableToCapture; } -(id) initWithLocalVar: (int)inLocalVariableToCapture; -(void) runForObject: (MyObject*)objectToVisit; @end @implementation MYBlockSubClass -(id) initWithLocalVar: (int)inLocalVariableToCapture { self = [super init]; if( self ) localVariableToCature = inLocalVariableToCapture; return self; } -(void) runForObject: (MyObject*)objectToVisit { objectToVisit->Print(localVariableToCapture); } @end
and at the actual call site:
MyVisitorPattern( [[MYBlockSubClass alloc] initWithLocalVar: localVariableToCapture], 15 );
The difference is that C++ (and even more so Objective-C) automatically declare the class for you, create the instance variables and constructor for the variables you want to capture, pick a unique class name (which you can see in the stack backtraces if you stop the debugger inside a block) and instantiate the class all in a single line of code.
So there you see it, blocks aren’t really black magic, theyre 99% syntactic sugar. Delicious, readability-enhancing syntactic sugar. Mmmmmh…
PS - Of course I’m simplifying. Objective-C blocks are actually Objective-C objects created on the stack, which you usually can’t do in plain Objective-C, though it can be done with some clever C code if you insist.
A more magical approach to blocks
That said, there is a fancier way for a compiler developer to implement blocks that also makes them 100% compatible with regular C functions:
If you implement a function in assembler, you can stick additional data onto the end of a function and calculate an offset between an instruction and the end of the function (e.g. by just filling the end of the function with a bunch of 1-byte No-Ops). This means that if someone duplicates a block, theyll duplicate this data section as well.
So what you can do is declare a struct equivalent to the captured variables, and implement your code with (pseudocode):
void MyBlock( void ) { struct CapturedIVars * capturedIVars = NULL; currentInstruction: capturedIVars = pointer_to_current_instruction + ivarsSection-currentInstruction; // Block's code goes here. goto ivarsSectionEnd; // Jump over ivars so we don't try to execute our data. ivarsSection: assembler_magic_to_reserve_some_space; ivarsSectionEnd: }
Now you can use the capturedIVars pointer to access the data attached to your function, but to any caller, MyBlock is just a plain old function that takes no arguments. But if you look at it from a distance, this is simply an object prefixed with a stub that looks like a function, so our general theory of blocks being just special syntax for objects holds.
I presume this is how Swift implements its blocks, because it really doesnt distinguish between blocks and functions.