Brief Intro to Blocks 3: Blocks Are Closures

来源:互联网 发布:koala for mac 下载 编辑:程序博客网 时间:2024/05/10 11:22

Blocks Are Closures!

As stated earlier in this chapter, a block is an implementation of a closure, a function that allows access to local variables declared outside of its typical scope. To understand what this means, let’s take a moment to understand scope and visibility rules. The visibility of a variable refers to the portion(s) of a program in which it can be accessed; this is also referred to as a variable’s scope. For example, variables declared within a C function definition have local scope, meaning that they are visible and accessible within the function, and not accessible elsewhere. (Note that a function can also reference global variables.) A block augments the visibility of variables compared to C functions through several features, as follows:

  • Support for lexical scope. This is the ability to reference local variables and parameters from an enclosing scope.
  • __block variables. The __block keyword, applied to a variable outside a block but within the same lexical scope, enables this variable to be modified within a block.
  • Instance variable access. A block defined within an object’s method implementationcan access the object’s instance variables.
  • const imports. Constant variables imported through a header file (via an #import or#include directive) are visible within a block literal expression.

Lexical Scope

One of the unique features of blocks is its support for lexical scoping. In contrast, C functions do not have access to local variables declared outside of their definition. The example shown in Listing 15-9will not compile because the local variable myVar is not visible within the logValue function definition.

Listing 15-9.  C Function Attempting to Access a Local Variable Outside Its Scope

void logValue(){  // ERROR, illegal access of myVar, not within scope  NSLog(@"Variable value = %d", myVar);}int main(int argc, const char * argv[]){  @autoreleasepool  {    int myVar = 10;    logValue();  }  return 0;}

A block supports lexical scoping, thus a block literal expression has access to variables declared within the same lexical scope. In addition, blocks can be defined anywhere other variables can; for example, within other blocks, functions, and methods. C functions, on the other hand, cannot be defined within other functions or methods. Listing 15-10 compiles and runs successfully because the variable myVar is declared within the same lexical scope as the block logValueBlock.

Listing 15-10.  Block Accessing Local Variable Through Lexical Scoping

{  int myVar = 10;  void (^logValueBlock)(void) = ^{    NSLog(@"Variable value = %d", myVar);  };  logValueBlock();}

Listing 15-10 shows that a block has access to local variables declared outside its definition. Specifically, these local variables are declared (and initialized) within an enclosing scope, prior to the block literal expression. Braces delimit a local scope; in addition scopes may be nested. Referring again to Listing 15-10, the variable myVar is declared at the same scope as the block definition assigned to logValueBlock, prior to the literal expression, and thus can be used within the expression. On the other hand, Listing 15-11 will not compile, because the local variable myVar is declared and initialized after the block literal expression.

Listing 15-11.  Illegal Access of Local Variable in Block, Declared After Block Literal Expression

void (^logValueBlock)(void) = ^{  // ERROR, illegal access of myVar, declared after literal expression  NSLog(@"Variable value = %d", myVar);};int myVar = 10;logValueBlock();

Local variables accessed through lexical scoping behave as constant values (for primitive types) or by reference variables (for objects) within a block literal expression. Listing 15-12 will not compile because a primitive variable accessed through lexical scoping cannot be modified within a block literal.

Listing 15-12.  Block Attempting to Modify a Local Variable Accessed Through Lexical Scoping

int myVar = 10;void (^logValueBlock)(void) = ^{  // ERROR, lexical scope variable not assignable  myVar = 5;  NSLog(@"Variable value = %d", myVar);};logValueBlock();

As local scopes can be nested, a block can capture local variables within multiple (nested) scopes. This is demonstrated in the code from Listing 15-13.

Listing 15-13.  Block Capturing Local Variables Within Multiple Nested Scopes

for (int ii=0; ii<2; ii++){  for (int jj=0; jj<3; jj++)  {    void (^logValueBlock)(void) = ^{      NSLog(@"Variable values = %d, %d", ii, jj);    };    logValueBlock();  }}

Mutable __block Variables

By default, block local variables accessed through lexical scoping are visible but not writeable within a block literal expression. The __block storage type modifier can be used to make these variables read-write (i.e., mutable). The __block modifier can be used on all supported Objective-C types, except for C variable-length arrays (an array with a length that is not a constant expression) and C structures that contain variable-length arrays. Its use cannot be combined with the existing local storage modifiers autoregister, and static. Listing 15-14 illustrates the use of a __blockvariable.

Listing 15-14.  Correct Use of __block Storage

__block int myVar = 10;void (^incBlock)(int) = ^(int amount){  myVar += amount;  NSLog(@"New value = %d", myVar);};incBlock(5);

Variables qualified with the __block storage type are copied to the heap if the referencing block is copied to heap storage. This last point brings up the topic of memory management with blocks, the subject of the next section.