1

One of my methods (mySecondMethod) receives a block and need to add an additional treatment to this block before passing it as an argument to another method. Here is the code sample:

- (void)myFirstMethod {
    // some code
    __weak MyController *weakSelf = self;
    [self mySecondMethod:^(BOOL finished) {
        [weakSelf doSomething:weakSelf.model.example];
    }];
}

- (void)mySecondMethod:(void(^)(BOOL finished))completion {
    void (^modifiedCompletion)(BOOL) = ^void(BOOL finished){
        completion(finished);
        _messageView.hidden = YES; //my new line
    };
    [UIView animateWithDuration:duration animations:^{
        //my code
    } completion:modifiedCompletion];
}

When run, I got a bad access error on completion(finished) line. completion is NULL. I tried to copy the block like this:

void (^copiedCompletion)(BOOL) = [completion copy];
void (^modifiedCompletion)(BOOL) = ^void(BOOL finished){
    copiedCompletion(finished);
    _messageView.hidden = YES; // my new line
};

but still got the error.

When I empty out completion block, the crash still happen so the crash is not due to what is inside.

Any idea how to solve this? Thanks!

Aurelien Porte
  • 2,692
  • 27
  • 32
  • Can you show how you are copying? – sbarow Dec 04 '13 at 12:05
  • 1
    There is no reason to use `weakSelf` instead of `self` here. – newacct Dec 04 '13 at 23:17
  • 1
    How do you get rid of compiler warning "capturing self in this block is likely to lead to a retain cycle" then? I'd take any good source for block memory management. – Aurelien Porte Dec 05 '13 at 08:16
  • That particular warning unfortunately has both false-positives and false-negatives. It's a useful "heads up, you might want to double check", but it's not a guarantee either way. – Catfish_Man Dec 09 '13 at 06:33

1 Answers1

1

I think you are getting the bad access because of this

// some code
__weak MyController *weakSelf = self;
[self mySecondMethod:^(BOOL finished) {
    [weakSelf doSomtehing:weakSelf.model.example];
}];

Try changing it to.

id example = self.model.example;
[self mySecondMethod:^(BOOL finished) {
    [self doSomething:example];
}];

Edit

The block needs to be copied before being called.

Side Note

Do a check before calling blocks to avoid unexpected crashes.

if (completion) {
   completion(finished);
}
sbarow
  • 2,759
  • 1
  • 22
  • 37
  • But calling self into the block will result in a memory warning? Plus, when the exception is fired, completion block is NULL so I think the bad access is due to the block itself, not what is inside. – Aurelien Porte Dec 04 '13 at 12:26
  • 1
    Does your program crash if you comment out [weakSelf doSomething:weakSelf.model.example]; ? – sbarow Dec 04 '13 at 12:28
  • 1
    Yeah I think you block is being released, you need to copy it before calling. Do a check like *if (completion) { completion(finished) }* On that note you should always check if the block exists before calling it. – sbarow Dec 04 '13 at 12:35
  • Please edit you answer and I'll accept it. It was as simple as that... My controller was still in an initialization state when mySecondMethod is called for the first time and I directly thought "OMG it's a memory problem with blocks again" instead of checking at basics. Thanks – Aurelien Porte Dec 04 '13 at 12:40
  • "The block needs to be copied before being called." Nope, not based on the code shown. – newacct Dec 04 '13 at 23:19
  • "Try changing it to. __block id example" There is ABSOLUTELY NO reason to use `__block` here. `example` is not assigned to anywhere. – newacct Dec 08 '13 at 11:12