0

My app is recently crashing randomly reporting a EXC_BAD_ACCESS during the execution of one of the core function of the app itself, the bug has always been there but it keeps happening a lot more after the introduction of iOS 9.3.1.

I've been doing a lot of analysis and static inspection of the code using the tools made available by XCode (Code Analysis, NSZombies, Address Sanitizer and so on). Before proceeding with further investigations I wanted to make sure the count of warnings and potential memory errors is down to 0.

Right now I'm stuck with this warning "Capturing 'endblock' strongly in this block is likely to lead to a retain cycle". I really think that solving this warning could solve also the EXC_BAD_ACCESS problem as using the Leaks instrument the piece of code labeled with that warning is causing a lot of leaks.

Here's the code snippet of the function causing the leaks:

- (void)arrayDayPlan:(void (^)()) block dateArray:(NSArray *)dateArray {

    [MyPlanner sharedInstance].multipleDaycounter = 1;
    NSInteger dayNumber = dateArray.count;

    void (^__block endBlock)() = ^void() {

        if([MyPlanner sharedInstance].multipleDaycounter == dayNumber) {
            block();
        } else {
            NSDate *newDate = [dateArray objectAtIndex:[MyPlanner sharedInstance].multipleDaycounter];
            [MyPlanner sharedInstance].multipleDaycounter = [MyPlanner sharedInstance].multipleDaycounter + 1;
            [[MyPlanner sharedInstance] plan:endBlock FromDay:[newDate dateAtStartOfDay] toDay:[[newDate dateByAddingDays:1] dateAtStartOfDay]];
        }
    };

    NSDate *firstDate = (NSDate *)[dateArray firstObject];
    [[MyPlanner sharedInstance] plan:endBlock FromDay:[firstDate dateAtStartOfDay] toDay:[[firstDate dateByAddingDays:1] dateAtStartOfDay]];
}

The line where I get that warning is this one inside the block:

[[MyPlanner sharedInstance] plan:endBlock FromDay:[newDate dateAtStartOfDay] toDay:[[newDate dateByAddingDays:1] dateAtStartOfDay]];

While the leaks are reported for this line:

void (^__block endBlock)() = ^void() {

This function is called in four places around the project and each call has more or less the following structure:

- (void)planWithArray {

    [self showLoadingView];
    __block MyAssignedViewController *blockSafeSelf = self;

    [[MyPlanner sharedInstance] arrayDayPlan:^{

        [[MyPlanningData sharedInstance] resetDateToPlan];
        [blockSafeSelf refreshView];
        [blockSafeSelf dismissLoadingView];
    } dateArray:[[MyPlanningData sharedInstance] generateDateArray:[[NSDate now] dateAtStartOfDay]]];
}

I've read on SO (here, here and on so many other threads) that others had similar problem but with usage of self variable. Does it apply even on my case?

I've also read that using a __weak reference could save my from this problem. Should I change the blockSafeSelf definition from __block to __weak?

Thanks in advance!

Community
  • 1
  • 1
LucioB
  • 1,744
  • 1
  • 15
  • 32

1 Answers1

2

The error:

Capturing 'endblock' strongly in this block is likely to lead to a retain cycle

has nothing as such to do with the error:

EXC_BAD_ACCESS

You do have a cycle:

  • endblock holds a reference to a block;
  • that block in turn holds a reference to endblock - as it is effectively captured-by-reference rather than captured-by-value due to the __block qualifier; and
  • the __block qualifier is required or the self-reference will not work - if endblock was captured-by-value then its value would be null as its value would be captured before the assignment to itself.

So you will potentially leak a block value every time you create this block, you can address that by creating a shadowing __weak variable and using that for the self reference, e.g. something along the lines of:

typedef void (^EndBlock)(void);

EndBlock endBlock;
__block __weak _endBlock = endBlock = ^{
   ...
   // only reference _endBlock inside the block
   ...
};

// use endBlock outside the block itself

Note you still need the __block or the self-reference will see nil (ff you're not clear what is happening with the self-referential block try this answer from a few years ago). You can also use __unsafe_unretained rather than __weak if you are eking out performance, it should be safe (if in doubt, don't).

However leaking as such won't cause you to EXC_BAD_ACCESS - rather the opposite, you tend to get this error because something isn't there when you expect it to be, not when something is there when you don't expect it (a leak).

So you need to look elsewhere for your EXC_BAD_ACCESS.

HTH

Community
  • 1
  • 1
CRD
  • 52,522
  • 5
  • 70
  • 86