Avoiding EXC_BAD_ACCESS when using the delegate pattern

Kenny Winker picture Kenny Winker · Apr 21, 2010 · Viewed 12.9k times · Source

A have a view controller, and it creates a "downloader" object, which has a reference to the view controller (as a delegate). The downloader calls back the view controller if it successfully downloads the item. This works fine as long as you stay on the view, but if you navigate away before the download is complete I get EXC_BAD_ACCESS. I understand why this is happening, but is there any way to check if an object is still allocated? I tried to test using delegate != nil, and [delegate respondsToSelector:], but it chokes.

if (!self.delegate || ![self.delegate respondsToSelector:@selector(downloadComplete:)]) {
  // delegate is gone, go away quietly
        [self autorelease];
        return;
    }
else {
  // delegate is still around
  [self.delegate downloadComplete:result];
}

I know I could,

a) have the downloader objects retain the view controller

b) keep an array of downloaders in the view controller, and set their delegate values to nil when I deallocate the view controller.

But I wonder if there is an easier way, where I just test if the delegate address contains a valid object?

Answer

Drew H picture Drew H · Jun 7, 2014

I just ran into this problem and solved it. For ARC, the solution is to use the weak attribute instead of assign.

The crash come because the delegate

  1. Has an assign attribute, AND
  2. Has been deallocated.

The solution is to use the weak attribute, because when the object deallocates, the pointer WILL be set the nil . So when your code calls respondsToSelector on a nil, Objective C will ignore the call, and not crash.

In your code, when you attempt to call the respondsToSelector method on delegate, you get a EXC_BAD_ACCESS. This is because objects that use the assign property will not be set to nil when they are deallocated. (Hence why doing a !self.delegate before the respondsToSelector does not prevent the responseToSelector from being called on a deallocated object, and still crashes your code)

As already mentioned, using a strong or assign attribute on a delegate (as many have mentioned) in ARC will result in a retain cycle. So don't do it, you don't need to.