arango_86
arango_86

Reputation: 4435

Difference between self.completionBlock = ^{} and (void)(^completionBlock)(void) = ^{}

Recently after following Apple documentation

Screenshot from an Apple Developer Video 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

Upvotes: 4

Views: 328

Answers (2)

Rob
Rob

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

Kjuly
Kjuly

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

Related Questions