references
https://zearfoss.wordpress.com/2012/05/11/a-quick-gotcha-about-blocks/
http://stackoverflow.com/questions/14649661/defining-objective-c-blocks-as-properties-best-practice/14650386#14650386
First, please read Block Basics
But why do we use copy to keep the Block around?
By default blocks are created on the stack. Meaning they only exist in the scope they have been created in. This is an optimization, since stack allocation is much cheaper than heap allocation.
Stack allocation means that, by default again, a block will cease to exist when the scope in which it is declared exits. So a block property with retain semantics will result in a dangling pointer to a block that doesn’t exist anymore when the stack gets popped.
But How?
To move a block from the stack to the heap (and thus give it normal Objective-C memory management semantics and an extended lifetime), you must copy the block via [theBlock copy], Block_copy(theBlock), property copy, using GCD via dispatch_ which copies your block, etc. Once on the heap, the block’s lifetime is managed.
That’s why you need to declare block properties with copy so the block is copied when the property is set. Thus, avoiding a dangling pointer to a stack-based block.
Therefore, if you want to access the block(s) later they have to be copied to the heap by sending a copy message to the block object. ARC will do this for you as soon as it detects a block needs to be accessed outside the scope its created in.
Behind the scene, block implementation consist of a data structure containing the values of the variables used in the block. What this means is that if you declared non-local variables will be in the data structure. Thus when you use those non-local variables, you are using the ones you’ve added onto the data structure.
For example if you have a string literal “ricky”, or a int 5, the block will create these non-local variables unto its own data structure. Thus every block has its own data structures, and contains its own non-local variables.
You can see an example here
In addition, when you have a pointer to an Object allocated on the heap, that data structure adds a new pointer, and that new pointer points to the same object in the heap.

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
|
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; } |
For further info, please read Block Basics
Retain Cycle on Blocks
consider this piece of code in a UIViewController. Basically, the UIViewController has a strong reference to a TapBlockView object.
|
- (void)viewDidLoad { [super viewDidLoad]; tapBlockView = [[TapBlockView alloc] initWithFrame:CGRectZero]; [tapBlockView setTapBlock:^(void) { self.someLabel.text = @"You tapped it!"; //retain cycle here }]; } |
Once we use self within the block, TapBlockView object then has a strong reference back to the UIViewController. Thus, the 2 objects both have a strong reference to each other…creating a retain cycle.
The reason why is because whenever a block uses a non-local variable (in our case, self), it deep copies the reference. Since self is a pointer to the UIViewController object, the block deep-copies the self pointer variable and places it in its data structure. The deep copied pointer strongly points to the UIViewController that’s in the heap.
Thus, now you have 2 situations which results in retain cycle:
1) you have a strong pointer in the block’s data structure pointing to the self object in the heap.
2) By using tapBlockView (or self.tapBlockView), UIViewController strongs it. Remember, by default, Even if you don’t use self.——, the default behavior for variables is __strong.
Solution
First, let’s set the solution up with this tidibit: Whatever pointer variable you declare INSIDE of the block, that means it gets pushed onto the local stack of the block.
Thus when the block is done, that local stack gets popped. Thus, if we were to use a strong reference and point it to whatever, when the block’s local stack gets popped, that strong reference will be nil-ed.
Part 1 – Use a weak reference to self. The weak reference promises that this block will not hold onto self. Basically, a __weak pointer will be put into the block’s data structure to be pointed to self. If and when self gets dealloc-ed, this block will not be holding on to it.
|
__weak MyClass * weakSelf = self; self.myBlock = ^{ //the block will deep copy the weakSelf pointer and place it in its data structure //thus, you have prevented a retain cycle here by doing: // [weakSelf someMethod]; }; |
BUT! What if right we’re using the block, and the self object gets de-allocated?
Our block will be accessing bad data because we only have a weak reference to self. So in order to hold onto self just long enough for our block to do its processing….
Part 2 – our trick is to create a local strong reference to hold onto the weak reference that’s on the block’s data structure. Local strong reference pointers simply gets pushed onto the block’s local stack frame.
That way, when the block starts to process, there’s a strong reference to the self object. Hence self will never be dealloc’ed. Once the block finishes, its local stack will be popped, and thus, the strong reference will be nil-ed. Therefore, the block will only have a weak reference in its data structure that points to self. As a result, self can be dealloc’ed because there is no strong reference to it.
The main idea here, is to get a local strong pointer, and point it to a non-local weak pointer.
This accomplish 2 things:
1) The non-local weak pointer to the self variable (which gets deep copied onto the block’s data structure), prevents a retain cycle
2) The local strong reference to that weak pointer, prevents the self object from being deallocated before or during our block’s usage.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
- (void)viewDidLoad { [super viewDidLoad]; tapBlockView = [[TapBlockView alloc] initWithFrame:CGRectZero]; //the weak reference DOES NOT hold onto the self object __weak typeof (self) weakself = self; [tapBlockView setTapBlock:^(void) { __strong typeof (self) stackStrongSelf = weakSelf; //then use that object stackStrongSelf.someLabel.text = @"You tapped it!"; //stackStrongSelf gets popped, thus getting nil-ed }]; } |