alex_c alex_c - 1 year ago 66
Objective-C Question

Why does this code result in EXC_BAD_ACCESS when using ARC?

I am converting an old iPhone project to use ARC. I'm presenting a modal view controller and getting EXC_BAD_ACCESS when dismissing it - can't figure out why, and I suspect I'm missing something fundamental about how ARC works.

The view controller being presented is CorrectionsController, and it uses a delegate to let its presenting view controller know to dismiss it. These are the relevant bits from the header file:

@protocol CorrectionsControllerDelegate
- dismissCorrectionsController;

@property (nonatomic, weak) id<CorrectionsControllerDelegate> correctionsDelegate;

The controller gets initialized in this method:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil delegate:(id<CorrectionsControllerDelegate>)_delegate {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
self.correctionsDelegate = _delegate;
// do other init stuff
return self;

The dismiss button calls this method:

- (void)cancelCorrection {
if (self.correctionsDelegate)
[self.correctionsDelegate dismissCorrectionsController];
// EXC_BAD_ACCESS happens here

The presenting view controller initializes the CorrectionsController like this:

// in .h file
@property (nonatomic, strong) CorrectionsController *corrections;
@property (nonatomic, strong) UINavigationController *secondNavigationController;

// in .m file
NSString *nibName = @"CorrectionsController";
self.corrections = [[CorrectionsController alloc] initWithNibName:nibName bundle:nil delegate:self];
self.secondNavigationController = [[UINavigationController alloc] initWithRootViewController:self.corrections];
if (isiPad()) {
self.secondNavigationController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:self.secondNavigationController animated:YES completion:nil];

And it implements the CorrectionsControllerDelegate protocol:

- (void)dismissCorrectionsController {
[self dismissViewControllerAnimated:TRUE completion:nil];

Now, the fun part. When stepping through the code, execution flows into cancelCorrection, enters dismissCorrectionsController in the delegate, returns to cancelCorrection, and EXC_BAD_ACCESS at the end of cancelCorrection.

enter image description here

enter image description here

self.correctionsDelegate appears to be pointing to a valid object at all times (inspecting it in the Variables view shows the object and values I would expect, and I get the following in the console which looks okay).

(lldb) po self.correctionsDelegate
<SyncController: 0x17b9a970>

The parts that really confuse me:

1) the stack trace shows the EXC_BAD_ACCESS is happening inside objc_retain. Why? What is being retained here?

2) what does the 0x44 memory address refer to?

Answer Source

Best explanation I can find so far is that the modal controller (CorrectionsController) seems to be released right away in dismissCorrectionsController, and is no longer valid when execution returns to cancelCorrection. This change seems to fix the crash:

- (void)cancelCorrection {
    [self.correctionsDelegate performSelector:@selector(dismissCorrectionsController) ];

I'll mark this answer as accepted but it still doesn't make complete sense to me, so if someone has a better explanation for what's happening I'll be happy to accept that answer instead.