At runtime, a block literal expression is allocated on the stack and thus has the same lifetime as a local variable. As a result, it must be copied to permanent storage (i.e., onto the heap) to be used outside of the scope in which it is defined. For example, if you want to return a block literal from a method or save it for later use, then the block must be copied onto the heap and subsequently released when no longer in use. Listing 15-15 illustrates a block literal that’s used outside its scope. This may result in a dangling pointer that causes the program to crash.
Listing 15-15. Block Literal Used Outside of Its Lexical Scope
void (^greetingBlock)(void);
{ // scope begins, local variables pushed onto stack
greetingBlock = ^{
NSLog(@"Hello, World!");
};
} // scope ends, stack variables (e.g. the block literal) popped off stack
greetingBlock(); // Block invocation may cause the program to crash!
Listing 15-15 shows that the block literal expression assigned to the variable greetingBlock has the same lifetime as a local variable; it thus no longer exists outside of its lexical scope. Objective-C includes copy (Block_copy()) and release (Block_release()) operations that provide memory management for block literals.
The Block_copy() operation copies a block literal onto the heap. It is implemented as a function that takes a block literal reference as its parameter and returns a block reference of the same type.
The Block_release() operation releases a block literal from the heap so that its memory is reclaimed. It is implemented as a function that takes a block reference as its parameter. A block reference is released from the heap if it matches a corresponding block reference that was copied to the heap; otherwise, the function call has no effect. In order to avoid a memory leak, aBlock_copy() call must be balanced by a corresponding Block_release(). The Foundation Framework defines copy and release methods for blocks that are the functional equivalent of theBlock_copy() and Block_release() functions. Listing 15-16 updates the code in Listing 15-15 with the appropriate block copy operations.
Listing 15-16. Using the Block Copy and Release Methods
void (^greetingBlock)(void);
{
greetingBlock = [^{
NSLog(@"Hello, World!");
} copy];
}
greetingBlock(); // Block invocation works (uses heap storage)
[greetingBlock release]; // released block to prevent memory leak
With ARC memory management, the compiler automatically performs the block copy and release operations as long as the block does not return an id type or pass an id type as a parameter. In either case, the copy and release operations must be performed manually, as with MRR memory management. Listing 15-17 demonstrates the use of these operations for a block that passes an idtype as a parameter.
Listing 15-17. Using the Block_copy and Block_release Functions for Blocks with an id Type Parameter
void (^greetingBlock)(id salutation);
{
greetingBlock = Block_copy(^(id salutation){
NSLog(@"%@, World!", salutation);
});
}
greetingBlock(@"Hello");
Block_release(greetingBlock);
Variables declared with the __block storage type have different semantics, depending on whether or not MRR or ARC memory management is in use. Under MRR, __block variables are not retained if used within a block literal; however, under ARC, __block variables are retained if used within a block literal. What this means is that if you are using ARC memory management and don’t want a __blockvariable to be retained (for example, to avoid circular references), the __weak storage type should also be applied to the variable.