Blocks are a C-Based language-level feature found in C, Objective-C, and C++ and they define self-contained units of work (Objective-C borrows the name “Block” from SmallTalk, a language in it’s DNA). Blocks are like anonymous functions (lambda abstractions) in that they combine data with related behavior, but they can also have optional binding to stack/heap variables.
For a deeper exploration of the theoretical basis of anonymous functions, you can read more about λ-calculus (lambda calculus) a mathematical formalism invented by Alonzo Church from 1932 onwards. This was a universal model of computation, it was what we now call “Turing-complete”, and can be “typed” or “untyped”. It’s worth noting that how “lambda” is used in mathematical contexts is quite different from how we tend to use it in mixed programming languages, although a lot more like how we see it in functional programming languages.
Blocks allow the creation of chunks of code that can be passed around to methods or functions as if they were values (Ref#3). Blocks are widely regarded as an alternative to the delegation pattern in many cases but can have downsides when it comes to the possibility of creating retain cycles if not coded correctly.
Retain cycles tend to happen when you have two objects which each hold strong pointers to one another meaning they can’t get deallocated. In the case of blocks, we see this happen when a class has a strong reference to a closure and a closure also has a strong reference to the class. One way this is typically overcome in both Objective-C and Swift is to mark references like references to self as weak (__weak). For more key details on this have a look here for a good explanation.
Blocks are not function-pointers but are they are in fact represented by a Data Structure – they are Objective-C Objects (created and managed by the compiler).
One obvious syntactical difference between a block and a function pointer is that a block name is preceded with a caret ^
symbol instead of an asterisk *
. Like a function pointer, you can pass arguments to a block and receive a return value from it. This means they can be added to collections like NSArray
or NSDictionary
, and the block then will typically then live in the heap and is able to be passed around your app.
Blocks were introduced to Objective-C along with iOS 4.0 and OS X v10.6 (meaning you may use blocks with these versions and later). The reasons behind the introduction were may have included a drive to make the programming language more easily able to model lambda calculus style logic through the introduction of elements from functional programming languages which were based on the background math concepts. Additionally, it was really just to simplify the code and reduce reliance on the delegate pattern, and blocks can also make our code a bit cleaner and easier to read (Ref#: 5).
Syntax of Blocks in Objective-C
The first thing to note up front is, as mentioned above, you can identify where a block is being used in Objective-C code where-ever you see the ^
(or caret) character. However, there are a variety of ways that we may use blocks that might enhance our code.
Defining a Block as a Local Variable
We can create a variable name for a block that stores what is effectively an anonymous function for later use. The syntax for that looks like this:
1 |
returnType (^blockVarName)(paramTypesArgList) = ^returnType(parameters) {body}; |
Breaking down the syntax a bit more the elements are as follows (where there can be multiple arguments separated by a comma):
SOURCE: Apple Reference Docs
Declaring a Block In-Line
We may define a block in-line without associating that block with a variable name, and this is actually the more common syntax used by programmers. The below example code uses the enumerateObjectsUsingBlock method for NSArray which executes a given block using each object in an array, beginning with the first object and iterating through the array to the last object:
1 2 3 4 5 |
NSArray *presSurnames = @[@"Clinton", @"Bush", @"Nixon"]; [presSurnames enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { NSLog(@"President %@", (NSString *)obj); } |
__block Variables
The term scope refers to the part of a computer program where the binding of a name to an entity (name binding) is valid, and we use the term lexical scope when we are referring to an area of source-code where that scope is set or defined. Blocks in Objective-C have the ability to capture values from their enclosing scope, and this makes them similar to closures which are really lambdas that capture parameters from outside scopes such as in Swift. These are often just called lambdas in other programming languages (Ref#3, Ref#11). Indeed, blocks are really flexible because they may access global variables, parameters passed into the block, stack variables (as copies), and the __block variables (which are passed by reference) (Ref#2).
We signal that a block can capture and modify a variable that has been defined in it’s enclosing scope by using the “__block” storage type modifier when declaring the variable in the enclosing scope, thus allowing a block to access the variable by reference. To elaborate on this, let’s look at an example of how we use this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
extern NSInteger CounterGlobal; static NSInteger CounterStatic; { NSInteger localCounter = 42; __block char localCharacter; void (^aBlock)(void) = ^(void) { ++CounterGlobal; ++CounterStatic; CounterGlobal = localCounter; // localCounter fixed at block creation localChracter = 'a'; // sets local charater in enclosing scope }; ++localCounter; // this increment is not seen by the block localChracter = 'b'; aBlock(); // execute the block // local character is now 'a' } |
Source: Apple Ref Docs (Ref# 10)
We see in the above code snippet that localCounter and localCharacter are both updated before the block is called. Only the change to localCharacter is going to be visible to the block however as this is the only variable marked with the __block storage type modifier and hence is the only variable passed by reference here. So again before the block is called, localCharacter has changed to be == ‘b’, and the block itself changes localCharacter back to be == ‘a’, but the increment to the localCounter has no effect since it sits below the creation of the block in the code.
Using Queues / NSOperationQueue with Blocks
An NSOperationQueue
is a queue that regulates the execution of a set of NSOperations / “operations” (these classes are built on top of GCD but with the ability to add dependency and to re-use, suspend, or cancel operations). If we want to use blocks in NSOperationQueues we can use addOperationWithBlock which wraps the specified block in an operation object and adds it to the receiver.
1 2 3 4 5 |
NSOperationQueue *someOpQueue; [someOpQueue addOperationWithBlock:^{ // Some Code Here }]; |
dispatch_async
We might use block syntax when working using different threads (for example when downloading content for the internet). For this type of thing, we might choose to use the dispatch_async function to submit a block for asynchronous execution on a dispatch queue (calls to dispatch_async will return immediately without waiting for a block to be invoked).
1 2 3 4 |
// put on global queue and set priority level dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //Some async code can be put in this block }); |
N.B. When using ARC we no longer need to use the dispatch_retain and dispatch_release keywords since ARC will deal with the retain count automatically, but we do still need to be super careful to avoid retain cycles by being careful when we find ourselves using strong pointers.
For a good reference on applying dispatch_async etc with blocks in Objective-C see this Ray Wenderlich tutorial here (N.B. be aware that some syntax may have changed till now).
Completions Handlers
Like @escaping
Closures in Swift, Blocks in Objective-C are very often used for completion handlers both with the things that the Cocoa framework provides for us, and for our own purposes. Completion handlers are of course a way for implementing callback functionality using blocks and like escaping closures in swift we may pass in a block that gets fired off once we have done something else.
1 2 3 4 |
[self presentViewController:viewController animated:YES completion:^{ NSLog(@"View Controller presentation completed so now do this...") // Do anything you want to do once you know that the vc was presented }]; |
1 2 3 4 5 6 7 8 9 10 |
[UIView animateWithDuration:0.5 delay:0 options:(UIViewAnimationTransitionNone) animations:^{ [tempImageView setAlpha:1.0f]; } completion:^(BOOL finished){ subviewStyle1Image = tempImageView; } ]; |
In the above example, you will see a block for “animations” and a completion block that will be scheduled to execute after the animations block actions have completed via a callback.
Extending Object Lifetime in Blocks
1 2 3 4 5 6 |
__weak __typeof__(self) weakSelf = self; dispatch_group_async(_operationGroup, _operationsQueue, ^ { [weakSelf doSomething]; [weakSelf doSomethingElse]; }); |
In the above example, on line 4 weakSelf might not be nil, yet on line 5 it could then be nil meaning that only one of the two operations is ever carried out. One way to ensure that this scenario does not occur is to do the following:
1 2 3 4 5 6 7 |
__weak __typeof__(self) weakSelf = self; dispatch_group_async(_operationsGroup, _operationsQueue, ^ { __typeof__(self) strongSelf = weakSelf; [strongSelf doSomething]; [strongSelf doSomethingElse]; } ); |
Here we first check if we can get self from weakSelf, if so we ensure that we keep it around for the full set of operations inside the closure such that we can never get one of the operations (lines) in the closure getting executed, and another not, in fact, we keep self around till the closure returns.
Conclusion
In this article, we explored what blocks are in Objective-C. We additionally covered scope, operation queues, and the strong-weak dance of weakification.
See my other articles to compare how Swift closures compare to Objective-C blocks.
References
1) Declaring and Creating Blocks. Retrieved from: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Blocks/Articles/bxDeclaringCreating.html
2) Objective-C Blocks. Retrieved from: “https://www.youtube.com/watch?v=FS4JAy1Wy3w&t=307s”
3) Working With Blocks. Retrieved from: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html
4) How Do I Declare A Block in Objective-C? Retrieved from: http://fuckingblocksyntax.com/
5) How To Use Blocks in iOS 5 Tutorial – Part 2. Retrieved from: https://www.raywenderlich.com/9438/how-to-use-blocks-in-ios-5-tutorial-part-2
6) https://www.raywenderlich.com/76341/use-nsoperation-nsoperationqueue-swift
7) Dispatch. https://developer.apple.com/documentation/dispatch
8) Dispatch Queues. https://developer.apple.com/library/content/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html
9) Blocks Programming Topics. https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Blocks/Articles/00_Introduction.html#//apple_ref/doc/uid/TP40007502
10) https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Blocks/Articles/bxVariables.html#//apple_ref/doc/uid/TP40007502-CH6-SW6
11) Lambdas everywhere. http://dobegin.com/lambda-functions-everywhere/
12) Lambda. https://martinfowler.com/bliki/Lambda.html
13) Category Theory 1.1: Motivation and Philosophy. “https://www.youtube.com/watch?v=I8LbkfSSR58&list=PLbgaMIhjbmEnaH_LTkxLI7FMa2HsnawM_”
14) Curry–Howard correspondence. https://en.wikipedia.org/wiki/Curry%E2%80%93Howard_correspondence
15/ https://dhoerl.wordpress.com/2013/04/23/i-finally-figured-out-weakself-and-strongself
16/ http://clang.llvm.org/docs/AutomaticReferenceCounting.html
Last Updated: 14/07/2019
Comments