reference
http://stackoverflow.com/questions/22996536/resolving-retain-cycles-is-there-really-a-reason-to-add-strong-strongself-wh?rq=1
http://rypress.com/tutorials/objective-c/blocks
http://www.cocoawithlove.com/2009/10/how-blocks-are-implemented-and.html
demo xCode 7.3
How To Declare
Interface: We have the return type. Then block name. Then the parameters.
Then a equals sign, which leads to our declaration of the block. In the declaration of the block, be sure to name your parameters.
|
return type (^blockName) ( param type, param type,... ) = ^( type param1, type param2 ) { // code }; int (^firstBlock)(NSString *param1, int param2) = ^(NSString * name, int age) { return 0; }; |
non-local variables are variables defined in the block’s enclosing lexical scope, but outside the block itself.
non-local variable are copied and stored with the block as const variable, which means they are read-only.
In other words, it creates “const” copy of any local variables that is referenced inside its scope.
Global Blocks – not referencing any non-local variables
With no references to the surrounding scope, clang configures the Block_literal as a global block instead.
For example,
|
NSString * (^firstBlock)(NSString *param1, int param2) = ^(NSString * name, int age) { return [NSString stringWithFormat:@"%@ is %d years old", name, age]; }; |
If you’re familiar with how Objective-C objects are declared, the isa field in the Block_literal above should be familiar — blocks are Objective-C objects.
As you see, it does not reference any non-local variables. Thus, the compiler creates it as a global block:

This causes the block to appear in a fixed global location instead of on the local stack.
The implication of this is that global blocks are never actually copied or disposed, even if you invoke the functions to do so. This optimization is possible because without any references to the surrounding scope, no part of the block (neither its code nor its Block_literal) will ever change — it becomes a shared constant value.
Malloc Blocks – __strong block definitions
|
void (^whisper)() = ^() { NSLog(@"someone has %d dollars", dough); }; |
The block object was retained by the whisper variable. The strong sends a retain to the block. When a block is retained, it invokes a copy on it.
block implementation detail:
|
id objc_retainBlock(id x) { return (id)_Block_copy(x); } |
Thus, the block object was moved from stack to heap by this _Block_copy. But note that there is only still 1 copy of the block. It simply increases the retain count of the block.

Stack Blocks – __weak block definitions
http://stackoverflow.com/questions/25794306/could-you-help-me-to-understand-block-types-when-added-to-containers-nsdictiona
http://stackoverflow.com/questions/19227982/using-block-and-weak
Marking it as __weak causes the whisper variable to NOT retain the block object.
Thus, the block starts off being allocated on the local stack, and it stays there. Since there is no __strong to retain it, and thus, no copy.
therefore, a weak block variable on a block object keeps the block object on the local stack.
|
//This knowingly leads to dangling pointers if the Block (or a copy) outlives the lifetime of this object. __weak void (^whisper3)() = ^() { NSLog(@"another block %d", localVariable); }; |

If you copy an NSStackBlock, it will return an NSMallocBlock (indicating its changed allocation location).
A block keeps it’s own array of its non-local variables via deep copy
example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
int main(int argc, const char * argv[]) { NSString *make = @"Honda"; //non-local variable NSString *(^getFullCarName)(NSString *) = ^(NSString *model) { //make = nil; //Trying to assign new value to the make variable // from inside the block will throw a compiler error. //Non-local variables are COPIED (deep copy) and stored with //the block as const variables (cannot be changed), which means they are read-only. //The fact that non-local variables are copied as constants means that a //block doesn’t just have access //to non-local variables—it creates a snapshot of them. return [make stringByAppendingFormat:@" %@", model]; }; NSLog(@"%@", getFullCarName(@"Accord")); // Honda Accord make = @"BMW"; NSLog(@"%@", make); //BMW NSLog(@"%@", getFullCarName(@"Civic")); // Honda Civic } |
So as you can see, the non-local variable make gets copied inside of the block getFullCarName. If you try to modify make inside of the block getFullCarName, you will see a compiler error.
Notice how later, outside of the block, we change the variable make to “BMW”. However, its a deep copy, this change takes place at the outside of the closure. Hence the original make variable (outside of the closure) is BMW. The copy inside the block, the make is still Honda. We see this happening by passing @”Civic” into getFullCarName(…).
You can make non-local changeable by using __block to use them as reference
Freezing non-local variables as constant values is a safe default behavior
in that it prevents you from accidentally changing them from within the block;
however, there are occasions when this is NOT desirable.
You can override the const copy behavior by declaring a non-local variable with the __block storage modifier:
|
__block NSString *make = @"Honda"; |
This tells the block to capture the variable by reference, whereas if you didn’t have __block, it would capture it by deep copy.
When the block captures the variable by reference, its like creating a direct link between the variable outside the block and the one inside the block.
You can now assign a new value to make from outside the block, and it will be reflected in the block, and vice versa.
|
__block int i = 0; int (^count)(void) = ^ { i += 1; return i; }; NSLog(@"%d", count()); // 1 NSLog(@"%d", ++i); //2 NSLog(@"%d", count()); // 3 NSLog(@"%d", ++i); //4 NSLog(@"%d", count()); // 5 |
So now, you are referencing the same variable outside and inside the block. Whereas before, you have 2 deep copies of the variable depending on whether you are outside or inside of the closure.
Each block has its own copy of the non-local variables
In the example below, if we call existingBlock or vv twice, you will see that the non-blocking variables that they copy will have the same address. Their local variables, however, are all different throughout the calls. That’s because for the local variables of the block, they just go on the stack. Where as the outside scope’s variable are all copied….in an array for that particular block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
|
void foo(int param) { int x0 = param; //stack local variable, blocks will have their own copy of this variable int x1 = param + 1; //stack local variable, blocks will have their own copy of this variable void (^existingBlock)(void) = ^{ NSLog(@"----------existingBlock START ------"); int y0 = 8; int y1 = 88; printf("existingBlock: local variable y0 address is %p\n", &y0); //existingBlock local variable printf("existingBlock: local variable y1 address %p\n", &y1); //existingBlock local variable printf("existingBlock non-local x0 address %p\n", &x0); //existingBlock non-local variable copy printf("existingBlock non-local x1 address %p\n", &x1); //existingBlock non-local variable copy NSLog(@"----------existingBlock END ------"); }; void (^vv)(void) = ^{ NSLog(@"----------vv START ------"); int y2 = 888; int y3 = 8888; existingBlock(); printf("vv: local variable y2 address is %p\n", &y2); //vv's local variable printf("vv: local variable y3 address is %p\n", &y3); //vv's some local variable printf("vv: non-local variable x0 address is %p\n", &x0); //vv's non-local variable copy printf("vv: non-local variable x1 address is %p\n", &x1); //vv's non-local variable copy NSLog(@"----------vv END ------"); }; NSLog(@"Stack: &x: %p, %u \n", &x0, x0); NSLog(@"Stack: &x: %p, %u \n", &x1, x1); NSLog(@"------- on main thread -------\n"); vv(); dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"THREAD 2 start"); assert(vv); sleep(1); int y4 = 6680; int y5 = 6868; vv(); printf("dispatch async local variable y4 address: %p\n", &y4); printf("dispatch async local variable y5 address: %p\n", &y5); printf("non-local variable x0 address: %p\n", &x0); //block's non-local variable printf("non-local variable x1 address: %p\n", &x1); //block's non-local varaible NSLog(@"THREAD 2 done"); }); } int main(int argc, const char * argv[]) { @autoreleasepool { foo(1); sleep(2); } return 0; } |
But what about Object allocations
As show in the code above, we have a block called vv. vv calls existingBlock block.
Then, say we have an object allocation outside of the block existingBlock at the highest scope at foo function.
We have 2 parts:
1) the reference ptrToStrObj gets pushed onto the local stack,
with address say: 0x7fff5fbff708
2) and the object that’s allocated on the heap, pointed to by reference ptrToStrObj. The object has address say: 0x100102450

LOCAL – address of POINTER ptrToStrObj……0x7fff5fbff708
LOCAL – address of NSString object on heap……0x100102450
The ptrToStrObj variable is a non-local variable, which means it defined in the block’s enclosing lexical scope, but OUTSIDE of the block itself. It is deep copied inside of the block.
We display the address of the the reference ptrToStrObj inside of the block existingBlock, which then shows:
———-existingBlock START ——
existingBlock – address of POINTER ptrToStrObj……0x1020001c0
existingBlock – address of OBJECT……0x100102450
existingBlock – ricky tsao, dob 6680
———-existingBlock END ——
As you can see, the address of the object on the heap remains at 0x100102450.
But the pointer of the reference ptrToStrObj has changed from 0x7fff5fbff708 to 0x1020001c0.
That is proof that the block made a deep copy of the non-local variable reference ptrToStrObj for itself.
Furthermore, if you try to modify ptrToStrObj, it will throw an error:

After calling existingBlock(), we move on to the next line:
changing content of string object
vv – address of POINTER ptrToStrObj……0x102000248
vv – address of OBJECT……0x100102450
vv – HA DO KEN!ao, dob 6680
———-vv END ——
Keep in mind that we are now back in bloc vv.
As you can see, the address of the object in the heap remains the same at 0x100102450.
However, the pointer address changed to 0x102000248. This means that this block vv has its own DEEP COPY of the non-local variable ptrToStrObj.
Thus,
foo’s ptrToStrObj is 0x7fff5fbff708
existingBlock’s ptrToStrObj is 0x1020001c0
vv’s ptrToStrObj is 0x102000248
In much deeper details, each block has an non-local variable array list. That’s where it keeps its list of non-local variables. As we can see in the picture vv block and existingBlock each have an array of non-local variables.
The first item in their array is reference ptrToStrObj. ptrToStrObj points to the object in the heap, which never changes as we can see that the addresses all match.
However the address of the references are different, because each block has their own DEEP COPY of the reference.

Finally, let’s see what happens inside of a GCD block:
|
dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"THREAD 2 start"); assert(vv); sleep(1); vv(); NSLog(@"in dispatch_async block - address of POINTER ptrToStrObj......%p", &ptrToStrObj); NSLog(@"existingBlock - %@", ptrToStrObj); NSLog(@"existingBlock - address of OBJECT......%p", ptrToStrObj); NSLog(@"THREAD 2 done"); }); |
THREAD 2 start
———-vv START ——
———-existingBlock START ——
existingBlock – address of POINTER ptrToStrObj……0x1020001c0
existingBlock – address of OBJECT……0x100102450
existingBlock – HA DO KEN!ao, dob 6680
———-existingBlock END ——
changing content of string object
vv – address of POINTER ptrToStrObj……0x102000248
existingBlock – address of OBJECT……0x100102450
existingBlock – HA DO KEN!N!ao, dob 6680
———-vv END ——
in dispatch_async block – address of POINTER ptrToStrObj……0x100500048
existingBlock – HA DO KEN!N!ao, dob 6680
existingBlock – address of OBJECT……0x100102450
THREAD 2 done
As you can see the object in the heap as address 0x100102450 for all blocks
existingBlock’s ptrToStrObj has 0x1020001c0
vv’s ptrToStrObj has 0x102000248
GCD’s ptrToStrObj has 0x100500048
Hence, as you can see, GCD’s block, like any other blocks, also have its own non-local variable list, and DEEP COPIES the non-local variable reference ptrToStrObj for itself.
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
|
void foo(int param) { int x0 = param; //stack local variable, blocks will have their own copy of this variable int x1 = param + 1; //stack local variable, blocks will have their own copy of this variable NSMutableString * ptrToStrObj = [[NSMutableString alloc] initWithFormat:@"ricky tsao, dob %u", 6680]; NSLog(@"LOCAL - address of POINTER ptrToStrObj......%p", &ptrToStrObj); NSLog(@"LOCAL - address of NSString object on heap......%p", ptrToStrObj); void (^existingBlock)(void) = ^{ NSLog(@"----------existingBlock START ------"); NSLog(@"existingBlock - address of POINTER ptrToStrObj......%p", &ptrToStrObj); NSLog(@"existingBlock - address of OBJECT......%p", ptrToStrObj); NSLog(@"existingBlock - %@", ptrToStrObj); NSLog(@"----------existingBlock END ------"); }; void (^vv)(void) = ^{ NSLog(@"----------vv START ------"); existingBlock(); NSLog(@"changing content of string object"); [ptrToStrObj replaceCharactersInRange:NSMakeRange(0, 8) withString:@"HA DO KEN!"]; NSLog(@"vv - address of POINTER ptrToStrObj......%p", &ptrToStrObj); NSLog(@"existingBlock - address of OBJECT......%p", ptrToStrObj); NSLog(@"existingBlock - %@", ptrToStrObj); NSLog(@"----------vv END ------"); }; NSLog(@"Stack: &x: %p, %u \n", &x0, x0); NSLog(@"Stack: &x: %p, %u \n", &x1, x1); NSLog(@"------- on main thread -------\n"); vv(); dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"THREAD 2 start"); assert(vv); sleep(1); vv(); NSLog(@"in dispatch_async block - address of POINTER ptrToStrObj......%p", &ptrToStrObj); NSLog(@"existingBlock - %@", ptrToStrObj); NSLog(@"existingBlock - address of OBJECT......%p", ptrToStrObj); NSLog(@"THREAD 2 done"); }); } int main(int argc, const char * argv[]) { @autoreleasepool { foo(1); sleep(2); } return 0; } |
output:
LOCAL – address of POINTER ptrToStrObj……0x7fff5fbff7e8
LOCAL – address of NSString object on heap……0x100211f00
Stack: &x: 0x7fff5fbff7f8, 1
Stack: &x: 0x7fff5fbff7f4, 2
——- on main thread ——-
———-vv START ——
existingBlock – address of POINTER ptrToStrObj……0x1001001e0
existingBlock – address of OBJECT……0x100211f00
existingBlock – ricky tsao, dob 6680
———-existingBlock END ——
changing content of string object
vv – address of POINTER ptrToStrObj……0x100300378
existingBlock – address of OBJECT……0x100211f00
existingBlock – HA DO KEN!ao, dob 6680
———-vv END ——
THREAD 2 start
———-vv START ——
———-existingBlock START ——
existingBlock – address of POINTER ptrToStrObj……0x1001001e0
existingBlock – address of OBJECT……0x100211f00
existingBlock – HA DO KEN!ao, dob 6680
———-existingBlock END ——
changing content of string object
vv – address of POINTER ptrToStrObj……0x100300378
existingBlock – address of OBJECT……0x100211f00
existingBlock – HA DO KEN!N!ao, dob 6680
———-vv END ——
in dispatch_async block – address of POINTER ptrToStrObj……0x100300168
existingBlock – HA DO KEN!N!ao, dob 6680
existingBlock – address of OBJECT……0x100211f00
THREAD 2 done
More Examples
GCD: dispatch_async
Contrary to what’s usually believed, dispatch_async per se will NOT cause a retain cycle
|
dispatch_async(queue, { self.doSomething(); }); |
Here, the closure has a strong reference to self, but the instance of the class (self) does not have any strong reference to the closure, so as soon as the closure ends, it will be released, and so no cycle will be created. However, sometimes it’s (incorrectly) assumed that this situation will lead to a retain cycle.
Syntax
Blocks are declared like so:
|
NSString *(^getFullCarName)(NSString *) = ^(NSString *model) { }; |
..and used like so:
|
NSLog(@"%@", getFullCarName(@"Accord")); |
This example declares a variable called simpleBlock to refer to a block that takes no arguments and doesn’t return a value
|
void (^simpleBlock)(void); simpleBlock = ^{ NSLog(@"This is a block"); }; |
You can also combine the variable assignment, and definition:
|
void (^simpleBlock)(void) = ^{ NSLog(@"This is a block"); }; |
Another Example
|
- (void)beginTaskWithCallbackBlock:(void (^)(void))callbackBlock; |
The (void (^)(void)) specifies that the parameter is a block that doesn’t take any arguments or return any values. The implementation of the method can invoke the block in the usual way:
|
- (void)beginTaskWithCallbackBlock:(void (^)(void))callbackBlock { ... callbackBlock(); } |
Method parameters that expect a block with one or more arguments are specified in the same way as with a block variable:
|
- (void)doSomethingWithBlock:(void (^)(double, double))block { ... block(21.0, 2.0); } |
it expects a block that does not return anything. But takes in double as the first 2 parameters.