One of the most difficult things about learning how to program with blocks is mastering their syntax. This section explains block syntax in depth and provides numerous examples to illustrate their application in code.
The syntactic features added to support blocks enable the declaration of variables of block type and the definition of block literals.
The block type consists of a return value type and a list of parameter types. A caret symbol (^) operator is used to declare a variable of block type. Figure 15-1 illustrates the syntax for a block type declaration.
Figure 15-1. Block type declaration syntax
Figure 15-1 shows that the returnType is the type of the value returned by the block when it is invoked. The block variable named blockName is preceded by a caret and enclosed in parentheses. As mentioned previously, a variable of block type may have parameters. The list of parameter types is enclosed in parentheses and follows the block name, where the parameterTypes are each separated by a comma. If a block variable declaration has no parameters, its list of parameter types should be specified as void (enclosed in parentheses). OK, that summarizes the syntax for declaring variables of block type. Now let’s look at some examples. The following statement declares a block variable named oneParamBlock that has a single parameter of type int and returns a value of type int.
int (^oneParamBlock)(int);
As stated, a block variable with no return value has its return type specified as void in its declaration. The next example declares a block variable named twoParamsBlock that takes two parameters of type int and returns nothing.
void (^twoParamsBlock)(int, int);
A block declaration can also include parameter names, similar to an Objective-C method declaration. The following shows the previous example with named input parameters.
void (^twoParamsBlock)(int parm1, int parm2);
The next example declares a block variable named noParamsBlock with no parameters and that returns a value of type int.
int (^noParamsBlock)(void);
Because variables can be declared with block type, a block variable can serve as a function argumentor method parameter. In these cases, it is common to create a type definition (via a typedefstatement) to provide an alternate name for a block type, thereby simplifying its subsequent usage in your code. Listing 15-1 demonstrates the declaration of a method that takes a block as a method parameter.
Listing 15-1. Block Variable in a Method Declaration
typedef int (^AdderBlock)(int);
@interface Calculator : NSObject
- (int)process:(int)count withBlock:(AdderBlock)adder;
@end
Listing 15-1 shows that a typedef statement is used to create a block type named AdderBlock. This type is subsequently used as the parameter type in a method declaration.
A block literal expression defines a reference to a block. It begins with the caret operator, followed by a return type, the list of parameters, and then a collection of statements (the block statement body) enclosed in braces. The syntax for a block literal expression is shown in Figure 15-2.
Figure 15-2. Block literal expression syntax
As you see in Figure 15-2, the caret symbol at the beginning of the expression indicates the beginning of a block literal expression, with the optional returnType specifying the type of the return value of the statement body. The block parameters (passed into the body of the block) are enclosed in parentheses with a type and a name for each, separated by commas. The block code (enclosed within braces) comprises the statements executed when the block is invoked. If the block definition specifies a return type, the code includes a return statement with a value of the specifiedreturnType. If there are multiple return statements in a block literal expression, the return type specified must be identical for each. Now let’s examine a few simple block literal expressions. Listing 15-2 takes a single parameter of type int and returns a value that equals its input parameter incremented by 1.
Listing 15-2. Block Literal Expression
^int (int addend)
{
return addend + 1;
}
As mentioned previously, it is not required to specify the return type for a block definition, because the compiler will infer it from the return statement within the body of the block. Listing 15-3 modifiesListing 15-2 by not specifying the return type in the expression.
Listing 15-3. Block Literal Expression, No Return Type
^(int addend)
{
return addend + 1;
}
If a block literal expression has no parameters, then the parenthesized parameter list is not required.Listing 15-4 provides an example of a block literal with no parameters and no return value, and that logs a greeting to the console.
Listing 15-4. Block Literal Definition, No Parameters
^{
NSLog(@"Hello, World!");
}
A block literal can be assigned to a corresponding block variable declaration. Listing 15-5demonstrates the declaration and subsequent definition of a block variable named oneParamBlock.
Listing 15-5. Block Declaration and Definition
int (^oneParamBlock)(int);
oneParamBlock = ^(int param1)
{
return param1 * param1;
};
As you would expect, the block declaration and definition can be combined into one statement. Listing 15-6 demonstrates the compound declaration and definition of a block variable named incBlock.
Listing 15-6. Compound Block Declaration and Definition
int (^incBlock) (int) = ^(int addend)
{
return addend + 1;
};
As shown in Listing 15-6, the block literal expression parameter type(s) and return type must conform to the corresponding block variable declaration.
Comparing Figures 15-1 and 15-2, you will note that there are several differences between the syntax of a block variable declaration and a block literal expression. For example, a block variable parameter types list is not optional; if there are no parameters, then a single void parameter type must be provided. Contrast that with a block literal expression for which the parameter list is optional—if there are no parameters, then the list may be omitted. Table 15-1 summarizes key differences in the syntax of block variable declarations and block literal expressions.
Table 15-1. Comparison of Block Syntactic Elements
Block Syntactic Element | Block Variable Declaration | Block Literal Expression |
---|---|---|
Caret operator | Begins a block variable declaration. The caret precedes the variable name, both of which are enclosed in parenthesis. | Begins a block literal expression. |
Name | The block variable name is required. | Block literal expressions are nameless. |
Return type | The return type is required in a block variable declaration. A block variable with no return value has a declared return type of void. | The return type is optional in a block literal expression because the compiler will infer it from the type of the return value in the expression statement body. If the expression statement body has more than one return statement, they all must be of the same type. |
Parameters | The parameter type list ismandatory in a block variable declaration. If the block variable has no parameters, then the declared parameter type list is void. | A parameter list is not required in a block literal expression that has no parameters. |
Once a variable of block type has been declared and assigned to a conforming block literal expression, it can be invoked directly with the invoke operator. This syntax is similar to a C function call syntax, with a list of parameters corresponding to the block declaration. (The type of the return value, if any, also corresponds to the block type declaration.) The following statement invokes the block in Listing 15-6 and assigns the result to the variable myValue.
int myValue = incBlock(5);
A block expression can also be defined and invoked in a single statement. Recall that a block expression is anonymous; hence, the invoke operator takes an anonymous (i.e., nameless) function followed by a parameter list that corresponds to the block expression. Listing 15-7 defines a block expression and invokes it in a single statement that logs the message “Greetings, Earthling!” to the console.
Listing 15-7. Block Expression Definition and Invocation
^(NSString *user)
{
NSLog(@"Greetings, %@!", user);
}(@"Earthling");
A block expression can also be defined inline as an anonymous function, thereby serving as a parameter of a function or method invocation. The recommended practice in these scenarios is to use only one block literal as a parameter to a function or method, and that it be the last parameter in the list (if the function/method has other parameters). Listing 15-8 invokes an instance method (from the class interface shown in Listing 15-1) that creates a block literal expression and uses it as a parameter to a method invocation.
Listing 15-8. Block Literal Expression in a Method Invocation
Calculator calc = [Calculator new];
int value = [calc process:2
withBlock:^(int addend)
{
return addend + 1;
}];
Many Objective-C APIs, including the Foundation Framework, contain methods that take a block as a parameter. Later in this chapter, you will develop several programs with blocks, so take your time and review this material as long as necessary until you thoroughly grasp the syntax.