Reputation: 4435
Recently after following Apple documentation
I used the following conventions to avoid retain cycle issues.
__weak __typeof(self) weak_self = self;
void(^completionBlock)(void) = ^(){
__typeof(self) strong_self = weak_self;
if (strong_self) {
if (strong_self->_completion != NULL) {
strong_self->_completion();
}
}
};
But this code is found to be crashing because self is getting deallocated before invoking the block.
When I used the following it is found to be working.
__block __typeof(self) block_self = self;
void(^completionBlock)(void) = ^(){
if (block_self->_completion != NULL) {
block_self->_completion();
}
};
Now I am confused when we should use __weak reference. Only in the following case of "self.completionBlock"
__weak __typeof(self) weak_self = self;
self.completionBlock = ^(){
if (weak_self->_completion != NULL) {
weak_self->_completion();
}
};
Any light on this conditions will be very useful to me.
My implementation code is given below.
=================================================
@implementation MyViewController
//starting point
- (void)myPushMethod {
__weak __typeof(self) weak_self = self;
MyViewControllerTransitioning *delegateObj = [[MyViewControllerTransitioning alloc] initWithCompletion:^{
//resetting the delegate
__typeof(self) strong_self = weak_self;
if (strong_self) {
strong_self.navigationController.delegate = nil;
}
}];
self.navigationController.delegate = delegateObj;
[self.navigationController pushViewController:someViewController animated:_animated];
//it is found that delegateObj is getting deallocated while reaches this point
}
@end
=================================================
@implementation MyViewControllerTransitioning
- (instancetype)initWithCompletion:(completionHandler)completionHandler
{
if(self = [super init])
{
if (completionHandler != NULL) {
_completion = completionHandler;
}
}
return self;
}
- (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC
{
id<UIViewControllerAnimatedTransitioning> animationTransitioning = nil;
//crashes here
__block __typeof(self) block_self = self;
void(^completionBlock)(void) = ^(){
if (block_self->_completion != NULL) {
block_self->_completion();
}
};
//showing presentation-up animation if Push
if (operation == UINavigationControllerOperationPush) {
animationTransitioning = [[MyAnimator alloc] initWithAnimationCompletion:completionBlock];
}
return animationTransitioning;
}
- (void)dealloc{
//dealloc is called before invoking completionBlock
}
@end
=================================================
@implementation MyAnimator
- (instancetype)initWithAnimationCompletion:(presentationUpCompletion)completionHandler
{
if(self = [super init])
{
if (completionHandler != NULL) {
_completion = completionHandler;
}
}
return self;
}
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return my_duration;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
//animation logic
[UIView animateWithDuration:duration animations: ^{
//animation logic
} completion: ^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
- (void)animationEnded:(BOOL) transitionCompleted {
if (transitionCompleted && _completion != NULL) {
_completion();
}
}
@end
Upvotes: 4
Views: 328
Reputation: 437977
In my original answer, below, I walk through the standard weakSelf
pattern (and the weakSelf
-strongSelf
"dance"). My conclusion was that presentation slide you reference is absolutely correct regarding the weakSelf
pattern (though that presentation is stylistically dated).
You subsequently provided a more complete code sample, and it turns out that it suffers from a different, unrelated problem. (Worse, it was a problem that only manifests itself when the strong reference cycles are resolved. lol.) Bottom line, the code sets the delegate of the navigation controller to a local object which falls out of scope. Because the navigation controller doesn't retain its delegate, you were ending up with a dangling pointer to this deallocated object.
If you keep your own strong
reference to this delegate object (keeping it from being deallocated), the problem goes away.
My original answer is below.
You said:
used the following conventions to avoid retain cycle issues.
__weak __typeof(self) weak_self = self; void(^completionBlock)(void) = ^(){ __typeof(self) strong_self = weak_self; if (strong_self) { if (strong_self->_completion != NULL) { strong_self->_completion(); } } };
But this code is found to be crashing because self is getting deallocated before invoking the block.
No, this is a very common pattern (often jokingly called the "weak self, strong self dance"). There is nothing wrong with this. If it's crashing, it's for other reasons.
Sure, I'd use modern naming conventions (weakSelf
and strongSelf
rather than weak_self
and strong_self
). And I'd remove the __
at the start of __typeof
. And I wouldn't be inclined to dereference and ivar, but rather use a property. So, I might end up with something like:
__weak typeof(self) weakSelf = self;
void(^completionBlock)(void) = ^(){
typeof(self) strongSelf = weakSelf;
if (strongSelf) {
if (strongSelf.completion != NULL) {
strongSelf.completion();
}
}
};
But, still, if that is crashing, you have some other problem. Frankly, you have a block calling a block so it's a little hard to guess where your problem rests, but the problem is not in the "weak self" pattern.
Later you go on to suggest using:
__block __typeof(self) block_self = self;
That does not do what you think it does. The goal of the "weak self" pattern is to break the strong reference cycle (formerly known as retain cycle), but in ARC, this __block
reference does nothing to resolve that strong reference cycle. Note, in non-ARC code, this block_self
pattern was used to break retain cycles, but it will not do the job in ARC.
Finally, you go on to suggest:
__weak __typeof(self) weak_self = self;
self.completionBlock = ^(){
if (weak_self->_completion != NULL) {
weak_self->_completion();
}
};
This has two serious problems. First, you are dereferencing a weak variable. If weak_self
was nil
, it would crash. Second, even if you fixed that by using property accessor methods rather than dereferencing ivars, you have another problem here, namely a race condition.
Bottom line, the "weak self" pattern from that presentation is correct. Likewise, the "weak self, strong self dance" from your first example is also correct. If it's crashing for you, you have to provide us a MCVE which reproduces the problem. But in ARC code, this is a perfectly valid pattern.
Upvotes: 1
Reputation: 35171
self.completionBlock
self
has a strong reference to completionBlock
(it's a property variable), then u need to weak reference to self
to avoid the cycle.
(void)(^completionBlock)(void) = ^{}
self
has NO strong reference to completionBlock
(it's a local variable), no need to create a weak reference to self.
And the reason for the one you added __block
works is that "__block
causes the variable to be strongly referenced".
Upvotes: 1