Previously
we learn about 'Pointers': Objective C - Pointers
Blocks are an Apple extension to the Objective-C language that
were motivated by the desire to pass small pieces of code in API calls to the
Grand Central Dispatch multithreading system. Grand Central Dispatch (GCD)
works by making use of queues containing such small pieces of code, each
representing a task or unit of work to be done. Blocks do not consist of a new
programming discovery in Objective-C. They exist in other programming languages
too (such as Javascript) with other names, such as Closures. Blocks have been available in iOS since version 4.
In
subsequent iOS versions, Apple re-wrote or updated many framework methods so
they adopt blocks, and it seems that blocks are going to be partly the future
of the way code is written. But what are they all about really?
The basic
idea of a block is to treat a small piece of code as if it were a value. The
piece of code can then be passed as a parameter in messages or assigned to a
variable. Eventually, at some future time, the code will be executed.
Using
blocks, there is no need to adopt protocols, or implementing delegate methods
that lead to much more code in a class. The best thing though, is that the
callback procedure can directly access and use any variables existing locally
in the scope where the block has been defined, so the need of passing data like
we do in callback methods is eliminated.
Blocks are
objects, so they can be stored to NSArray or NSDictionary data structures, as
well as to be returned from methods, even to be assigned to variables.
[ Two facts are sure for every
developer, even if someone has no idea about blocks.
First off,
it’s certain that every one has used blocks, even without knowing it. That’s
because Apple has adopted blocks especially as completion handlers to many important,
commonly used methods.
Secondarily,
thankfully or not, you don’t need to have deep knowledge on block programming
in order to use them, just to understand how they work.]
Blocks have two great features:
- They can be executed in a later time, and not when the code of the scope they have been implemented is being executed.
- Their usage leads eventually to a much cleaner and tidier code writing, as they can be used instead of delegate methods, written just in one place and not spread to many files.
The most
basic form of a block, known as a "block literal", consists of a
caret character ("^"), followed by a set of curly braces that contain
the actual code, as shown below:
^{ NSLog(@"Hi, I'm a block literal.");
}
Blocks
appear inline along with the rest of your code, inside a method or function.
A block declaration follows the next syntax pattern:
ReturnType (^blockName)(Parameters)
This is quite similar to a C function declaration, with one great exception: The caret (^) symbol, which indicates that the declared object is a block. Let’s see everything one by one:
- ReturnType: Any data type
Objective-C supports, or void if the block returns nothing.
- ^blockName: Always remember
that the block name is preceded by the ^ symbol. The block name can be any string you like, just like
you name any other variable or method. Remember that both the ^ and the block name are enclosed
in parentheses ().
- Parameters: The list of parameters you want to pass on the block, just like you do when declaring methods. However, keep in mind an important notice: When you pass no parameters to block, then the void keyword should be always set. The arguments list must also be enclosed in parentheses.
- And that’s all about declaring blocks. Here are some examples:
int (^firstBlock)(NSString *param1, int param2);
void (^showName)(NSString *myName);
NSDate *(^whatDayIsIt)(void);
void (^allVoid)(void);
NSString *(^composeName)(NSString *firstName, NSString *lastName);
The use of the ^ symbol and all those parentheses may seem confusing at the beginning, but don’t worry. You just have to get used to it and do never forget the ^ symbol.
- A special characteristic that the block declaration has, is that the parameter names can be omitted, and just keep the parameter types. Actually, adding parameter names to the declaration only helps developers to remember them, but it offers nothing to the compiler. So, all the above declarations could be re-written without the parameter names this time:
int (^firstBlock)(NSString *, int);
void (^showName)(NSString *);
NSDate *(^whatDayIsIt)(void);
void (^allVoid)(void);
NSString *(^composeName)(NSString *, NSString *);
Using or not parameter names is totally up to you. It’s not wrong to use them, nor to omit them as well. Usually, advanced developers familiarized with blocks do not write parameter names, just their types, but for beginners I think that helps a lot to have the parameter names written.
Let’s focus now on the block definition:
^(Parameters){ ... block body ...
return ReturnValue (or nothing if the block return type is void)
};
As you see,
no block name exists here. In block definition, parameter names are mandatory,
unlike the block declaration where they are optional. Especially note that
after the block body closing, the semicolon ; is added, as the whole block is considered as a variable. Don’t
worry if you ever forget it; Compiler is there to remind it to you.
Here is a really simple example:
^(int a, int b){ int result = a * b; return result; };
Of course,
you are never going to use a block simply like this. The most possible cases is
that the block definition result will be assigned to a variable, or that you’ll
define the block using it as a completion handler, upon a method call (later on
this).
Let’s see now a couple of examples on how you can assign block results to a
block variable:
int (^howMany)(int, int) = ^(int a, int b){ return a + b; };
In this
example, the parameter names have been omitted in the declaration, but they
mandatorily exist on the definition. Once again, I underline the usage of the ^ symbol, and the ; at the closing of the block body.
Some other Examples:
Example 1:
int (^howMany)(int, int) = ^(int a, int b){
return a + b;
};
NSLog(@"%d", howmany(5, 10));
// Output: 15
Example 2:
NSDate *(^today)(void);
today = ^(void){
return [NSDate date];
};
NSLog(@"%@", today());
// Outputs the today's date.
Example 3:
float results = ^(float value1, float value2, float value3){
return value1 * value2 * value3;
} (1.2, 3.4, 5.6);
NSLog(@"%f", results);
// Output: 22.848001
This is a more direct approach, as using the same command we define the block, we pass the parameter values and we assign the results to a variable.
Example 4: using a variable existing out of the block definition:
int factor = 5;
int (^newResult)(void) = ^(void){
return factor * 10;
};
NSLog(@"%d", newResult());
// Output: 50
Now, you are considered to have taken
the basic understanding and have set the ground for writing and using blocks.
Understanding
the Full Syntax of Blocks
A block is similar to a C function,
and can accept input parameters and produce a return value.
To see the similarities (and differences) between blocks and C functions, we can begin with a simple C function that adds two numbers together. We first define it:
int addTwoNumbersFunction(int a, int b) {
return a+b;}
Then we call it:
int c = addTwoNumbersFunction(3,4); NSLog(@"Result is %d",c);
A block is quite similar, although with a messier syntax:
int (^addTwoNumbersBlock) (int,int) = ^(int a, int b) {
return a+b; }
And we can call it in the same way:
int c = addTwoNumbersBlock(3,4);
NSLog(@"Result is %d",c);
We will see on the console log: Result is 7
Looking at the code for defining the block, we have seen what's on the right side of the assignment statement before: this is just the literal block, including a declaration, in parentheses, of the input parameters:
int (^addTwoNumbersBlock) (int,int) = ^(int a, int b) {
return a+b; }
On the left side we have the return
value for the block, the caret declaring that this is a block along with the
name of the variable we are assigning the block to, and a seemingly gratuitous
indication of the fact that there are two input arguments, both integers. (But
as to the last, the left side is declaring the variable and its type, while the
right side is declaring and implementing the actual block.)
Although messy, it is not hard to understand--the real problem is remembering the syntax.
What we have done here is declared the variable addTwoNumbersBlock to have the type block (with return value of int and two input parameters, both int), and assigned an actual block to the variable.
And the compiler and runtime know that when they see a variable that has been declared as a block, that the code for that block should be called, with its input arguments, in the same way that the code for a C function is called.