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.
=================================================
File MyViewController
@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
=================================================
File MyViewControllerTransitioning
@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
=================================================
File MyAnimator
@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