Introduction
Block objects are a C-level syntactic and runtime feature. They are similar to standard C functions, but in addition to executable code they may also contain variable bindings to automatic (stack) or managed (heap) memory. A block can therefore maintain a set of state (data) that it can use to impact behavior when executed.
You can use blocks to compose function expressions that can be passed to API, optionally stored, and used by multiple threads. Blocks are particularly useful as a callback because the block carries both the code to be executed on callback and the data needed during that execution.
Declaring and Using a Block
You use the
^
operator to declare a block variable and to indicate the beginning of a block literal. The body of the block itself is contained within {}
, as shown in this example (as usual with C, ;
indicates the end of the statement):int multiplier = 7; |
int (^myBlock)(int) = ^(int num) { |
return num * multiplier; |
}; |
The example is explained in the following illustration:
Notice that the block is able to make use of variables from the same scope in which it was defined.
If you declare a block as a variable, you can then use it just as you would a function:
int multiplier = 7; |
int (^myBlock)(int) = ^(int num) { |
return num * multiplier; |
}; |
printf("%d", myBlock(3)); |
// prints "21" |
Conceptual Overview
Block objects provide a way for you to create an ad hoc function body as an expression in C, and C-derived languages such as Objective-C and C++. In other languages and environments, a block object is sometimes also called a “closure”. Here, they are typically referred to colloquially as “blocks”, unless there is scope for confusion with the standard C term for a block of code.
Usage
Blocks represent typically small, self-contained pieces of code. As such, they’re particularly useful as a means of encapsulating units of work that may be executed concurrently, or over items in a collection, or as a callback when another operation has finished.
Blocks are a useful alternative to traditional callback functions for two main reasons:
- They allow you to write code at the point of invocation that is executed later in the context of the method implementation.Blocks are thus often parameters of framework methods.
- They allow access to local variables.Rather than using callbacks requiring a data structure that embodies all the contextual information you need to perform an operation, you simply access local variables directly.
Declaring a Block Reference
Block variables hold references to blocks. You declare them using syntax similar to that you use to declare a pointer to a function, except that you use
^
instead of *
. The block type fully interoperates with the rest of the C type system. The following are all valid block variable declarations:void (^blockReturningVoidWithVoidArgument)(void); |
int (^blockReturningIntWithIntAndCharArguments)(int, char); |
void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int); |
Blocks also support variadic (
...
) arguments. A block that takes no arguments must specify void
in the argument list.
Blocks are designed to be fully type safe by giving the compiler a full set of metadata to use to validate use of blocks, parameters passed to blocks, and assignment of the return value. You can cast a block reference to a pointer of arbitrary type and vice versa. You cannot, however, dereference a block reference via the pointer dereference operator (
*
)—thus a block's size cannot be computed at compile time.
You can also create types for blocks—doing so is generally considered to be best practice when you use a block with a given signature in multiple places:
typedef float (^MyBlockType)(float, float); |
MyBlockType myFirstBlock = // ... ;
|
MyBlockType mySecondBlock = // ... ; |
没有评论:
发表评论