I’ve got some code with an apparent reference cycle in a block ivar. The following code causes a reference cycle and dealloc is never called:
__block MyViewController *blockSelf = self;
loggedInCallback = ^(BOOL success, NSError *error){
if (success)
{
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void)
{
[blockSelf.delegate loginDidFinish];
});
}
};
However, if I create another __block variable to hold a reference to my delegate for the block’s scope to capture, the reference cycle goes away:
__block id <MyViewControllerDelegate> blockDelegate = self.delegate;
loggedInCallback = ^(BOOL success, NSError *error){
if (success)
{
double delayInSeconds = 1.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void)
{
[blockDelegate loginDidFinish];
});
}
};
Just want to understand what’s going on here.
I’m going to assume your’e using ARC here. Prior to ARC, your first example would work just fine. With ARC the semantics of
__blockhave changed.__blockdeclarations are now strongly captured, rather than weakly. Replace__blockwith__weakin your first sample and all should work as expected.As for what the second example works, you are creating a strong reference to the delegate, but your that doesn’t have a reference back to your object. Thus no cycle and everyone is happy.
I recommend reading Mike Ash’s article on the changes introduced with ARC, especially around block capture and
__weakhttp://www.mikeash.com/pyblog/friday-qa-2011-09-30-automatic-reference-counting.html