- Published on
Block Memory Allocation in iOS
Introduction
I originally intended to write a quick summary of Block memory management, but the topic turned out to be a much deeper rabbit hole than expected.
Block Types
A Block is itself an object. In most cases, Blocks are allocated on the stack. A Block is only allocated in the global data segment when it is defined as a global variable or when no automatic variables are referenced inside the Block (corresponding to the heap and text segment below). __block variables are also allocated on the stack.
Under ARC, the compiler automatically handles most Block memory management for us, but when a Block is passed as a method parameter, the compiler does not do this automatically — you must manually copy the Block object. Fortunately, most Cocoa framework APIs whose names contain usingBlock, as well as GCD APIs, already perform a copy internally, so you don't need to handle that yourself. Outside of those cases, however, manual intervention is required.
Based on their location in memory, Blocks fall into three types: NSGlobalBlock, NSStackBlock, and NSMallocBlock.
NSGlobalBlock
NSGlobalBlock is similar to a regular function and lives in the text segment. It contains no references to any external variables. retain, copy, and release operations on an NSGlobalBlock all have no effect.
void (^globalBlock) () = ^ () {
NSLog(@"global block");
};
NSLog(@"%@", globalBlock);
//<__NSGlobalBlock__: 0x1096e30a0>
NSStackBlock
NSStackBlock lives on the stack. It becomes invalid after the function returns because it references external variables.
Under MRC:
int base = 100;
long (^stackBlock) (int, int) = ^ long (int a, int b) {
return base +a + b;
};
NSLog(@"%@",stackBlock);
//<__NSStackBlock__: 0x7fff57c4bde0>
A stack Block is reclaimed when the function exits. Calling the Block after that causes a crash:
void example_addBlockToArray(NSMutableArray *array) {
char b = 'B';
[array addObject:^{
printf("%cn", b);
}];
}
void example() {
NSMutableArray *array = [NSMutableArray array];
example_addBlockToArray(array);
void (^block)() = [array objectAtIndex:0];
block();
}
The Block added inside example_addBlockToArray is a stack Block; calling it inside example will crash the program. The fix is to copy the Block to the heap first:
[array addObject:[[^{
printf("%cn", b);
} copy autorelease]]];
Under ARC:
int base = 100;
long (^stackBlock) (int, int) = ^ long (int a, int b) {
return base +a + b;
};
NSLog(@"%@",stackBlock);
//<__NSMallocBlock__: 0x7f8da961d390>
Under ARC, the printed type is not NSStackBlock. Many people assume that under ARC there are only NSGlobalBlock and NSMallocBlock, but that is incorrect. Under ARC, the generated Block is still an NSStackBlock; it is just that when assigned to a strong object, the system automatically copies it:
int i=0;
NSLog(@"%@", ^{
NSLog(@"stack block here, i=%d", i);
});
//<__NSStackBlock__: 0x7fff592eacf8>
void (^block)()=^{
NSLog(@"stack block here, i=%d", i);
};
NSLog(@"%@",block);
//<__NSMallocBlock__: 0x7fae49e02660>
NSMallocBlock
NSMallocBlock lives on the heap. If an NSStackBlock needs to be used outside its scope, you must manually copy it to the heap under MRC. NSMallocBlock supports retain and release, which increment and decrement the retain count respectively. copy does not create a new object — it simply adds a reference, similar to retain. Under ARC, the Block is automatically copied to the heap, so manual management is unnecessary. Use ARC whenever possible.
Summary of copy, retain, and release on Blocks
-
BlockCopyis equivalent tocopy;BlockReleaseis equivalent torelease. -
For a Block,
retain,copy, andreleasedo not change theretainCount;retainCountis always 1. -
NSGlobalBlock:retain,copy, andreleaseare all no-ops. -
NSStackBlock:retainandreleaseare no-ops. It is crucial to know that after a function returns, anNSStackBlock's memory is reclaimed. Evenretainwill not help. A common mistake is[mutableArray addObject:stackBlock](note: under ARC this is not a concern because ARC copies stack Blocks to the heap by default). After the function returns, thestackBlockretrieved frommutableArraywill be a dangling pointer. The correct approach is to first copy it to the heap:[mutableArray addObject:[[stackBlock copy] autorelease]].copyon anNSStackBlockcreates a newNSMallocBlock. -
NSMallocBlocksupportsretainandrelease; althoughretainCountalways shows 1, the memory manager still increments and decrements the count internally.copydoes not create a new object — it just adds a reference, similar toretain. Avoid usingretainon a Block when possible.
Block Access to Different Variable Types
Local Variables
Read-only inside a Block. When the Block is defined, the variable's value is copied and treated as a constant inside the Block. Therefore, even if the variable's value changes outside the Block, it does not affect the value inside the Block.
int base = 100;
BlkSum sum = ^ long (int a, int b) {
// base++; compile error — read-only
return base + a + b;
};
base = 0;
printf("%ld\n",sum(1,2)); // output is 103, not 3
Static Variables and Global Variables
If base in the example above is changed to a global or static variable, the Block can both read and write it. Because global and static variables have fixed addresses in memory, the Block reads the variable's current value directly from that address rather than a copy made at definition time.
static int base = 100;
BlkSum sum = ^ long (int a, int b) {
base++;
return base + a + b;
};
base = 0;
printf("%d\n", base);
printf("%ld\n",sum(1,2)); // output is 3, not 103
printf("%d\n", base);
The output is 0 4 1, showing that changes to base outside the Block affect its value inside the Block, and likewise that changes to base inside the Block affect its value outside the Block.
Block Variables
Variables modified with __block are called Block variables. For primitive types, __block variables behave like global or static variables.
When a Block is used by another Block, and that outer Block is copied to the heap, the inner Block is also copied. However, a Block passed as a parameter is not copied.
Objective-C Objects
Unlike primitive types, Blocks affect the retain count of Objective-C objects. Under ARC, be careful about retain cycles.
References: Differences Between Blocks Under ARC and Non-ARC Using Blocks Correctly to Avoid Retain Cycles and Crashes Block Memory Management