More on Objective-C Blocks
In 2011 I first blogged about Objective-C blocks, a game changing language construct that allows defining callable functions on-the-fly. In this post, we delve into some advanced properties of blocks in the Objective-C language.
1. Blocks capture their enclosing scope
Consider the following code snippet:
#import <Foundation/Foundation.h> int main(int argc, char* argv[]) { @autoreleasepool { int capture_me = 10; int (^squared)(void) = ^(void){ return capture_me * capture_me; }; printf("%d\n", squared()); } return 0; }
In the above example, we create a block that captures local variable “capture_me” and store it into a variable called “squared”. When we invoke the “squared” block, it will access the captured variable’s value, square it and return it to the caller.
This is a great feature that allows referencing local variables from deep within a complex operation’s stack. As Miguel de Icaza points out, however, we need to be careful with this feature to avoid producing hard to maintain code.
As you may have guessed, the code above correctly prints value “100”.
2. Blocks can modify captured variables
Now, consider this snippet. We will change our block not to return the squared variable, but rather to capture a reference to the local variable and store the squared value, overriding the original.
#import <Foundation/Foundation.h> int main(int argc, char* argv[]) { @autoreleasepool { __block int modify_me = 10; void (^squared)(void) = ^(void){ modify_me *= modify_me; }; squared(); printf("%d\n", modify_me); } return 0; }
The __block keyword signals that variable “modify_me” is captured as a reference by the Block, allowing it to be modified from within its body.
Just like before, this code still prints “100”. If we were to call the “squared” block a second time, we would square the variable again, yielding “10.000”.
3. Blocks are Objective-C Objects allocated on the stack
Unlike any other object instance in Objective-C, blocks are objects that are allocated on the stack. This means blocks need to be treated as a special case when we want to store them for later usage.
As a general rule of thumb: you should never retain a block. If it is to survive the stack frame where it was defined, you must copy it, so the runtime can place it on the heap.
If you forget and accidentally retain a block on the stack it might lead to runtime errors. The Xcode analyzer, thankfully, detects this problem.
Conclusion
If there were a feature I could have added to the Java programming language (when developing Android apps), it would without be, without a doubt, support for blocks or, in general, lambda expressions.
Objective-C blocks are a powerful feature that must be handled with care. When used correctly, they have the power to let us improve our code to make it more streamlined. When used incorrectly, they can lead to unreadable code and/or hard-to-debug memory-management bugs.
If you are interested in learning more about blocks in the Objective-C programming language, this article is a great resource and here’s the official Apple documentation.
Happy coding!