Why should we set the __block variable to nil?

From Going to ARC Release Notes

Use lifetime qualifiers to avoid strong reference loops

You can use qualifiers for life to avoid strong reference cycles. For example, usually, if you have a schedule of objects located in the hierarchy of parents and children, and parents need to contact their children and vice versa, then you make the relationship between parents and children strong and the relationship between children and parents is weak. Other situations may be more subtle, especially when they are associated with block objects.

In manual reference counting mode __block id x; valid without saving x . In ARC mode __block id x; x saved by default (just like all other values). To get manual reference counting behavior in ARC, you can use __unsafe_unretained __block id x; . However, since the name __unsafe_unretained implies that an unchecked variable is dangerous (because it can hang out) and is therefore discouraged. The two best options are either to use __weak (if you do not need to support iOS 4 or OS X v10.6), or set __block to nil to interrupt the save cycle.

Ok, so what is different from the __block variable?

Why install nil here? __block variable stored twice? Who has all the links? Block? A bunch? Stack? A thread? What?

The following code snippet illustrates this problem using a template that is sometimes used in manual reference counting.

 MyViewController *myController = [[MyViewController alloc] init…]; // ... myController.completionHandler = ^(NSInteger result) { [myController dismissViewControllerAnimated:YES completion:nil]; }; [self presentViewController:myController animated:YES completion:^{ [myController release]; }]; 

As described, you can use the __block qualifier __block and set the myController variable to nil in the completion handler:

 MyViewController * __block myController = [[MyViewController alloc] init…]; //Why use __block. my controller is not changed at all // ... myController.completionHandler = ^(NSInteger result) { [myController dismissViewControllerAnimated:YES completion:nil]; myController = nil; //Why set to nil here? Is __block variable retained twice? Who hold all the reference? The block? The heap? The stack? The thread? The what? }; 

Also why myController not installed in nil compiler. Why should we do this? It seems that the compiler knows when myController will no longer be used again, namely when the block expires.

+6
source share
2 answers

If you have code for this form:

 object.block = ^{ // reference object from inside the block [object someMethodOrProperty]; }; 

object save or copy the block that you pass to it. But the block itself will also save the object , because it is strictly referenced from within the block. This is a save cycle. Even after the block has completed execution, the reference loop still exists, and neither the object nor the block can be freed. Remember that a block can be called several times, so it cannot just forget all the variables that it refers after it has finished executing once.

To break this loop, you can define object as a __block variable, which allows you to change its value from within the block, for example. changing it to nil to break the loop:

 __block id object = ...; object.block = ^{ // reference object from inside the block [object someMethodOrProperty]; object = nil; // At this point, the block no longer retains object, so the cycle is broken }; 

When we assign object - nil at the end of a block, the block will no longer save object , and the save cycle will be broken. This allows you to free both objects.

One specific example of this is the NSOperation completionBlock property. If you use completionBlock to access the result of the operation, you need to break the save loop that was created:

 __block NSOperation *op = [self operationForProcessingSomeData]; op.completionBlock = ^{ // since we strongly reference op here, a retain cycle is created [self operationFinishedWithData:op.processedData]; // break the retain cycle! op = nil; } 

As described in the documentation, there are a number of other methods that you can also use to break these save cycles. For example, you will need to use a different technique in code other than ARC than in ARC code.

+14
source

I prefer this solution

 typeof(self) __weak weakSelf = self; self.rotationBlock = ^{ typeof (weakSelf) __strong self = weakSelf; [self yourCodeThatReferenceSelf]; }; 

What happens is that the block will grab itself as a weak link, and there will be no save loop. self inside the block is then redefined as __strong self = weakSelf before running your code. This prevents you from freeing yourself while your block is executing.

0
source

Source: https://habr.com/ru/post/917892/


All Articles