I'm using the excellent TSAlertView project to create popup alerts with enlarged fonts for people with visual impairments. Unfortunately I've encountered a problem that has been puzzling me for a couple of days.
I'm using a TSAlertView
as a validator popup when a user is about to move to a new page in the app to confirm the content of the current page. I.e., the user presses a button to move to a new page in the app, the TSAlertView
popups on the screen and if the user presses the Yes button, the current UIViewController
is replaced with a different UIViewController
using a custom segue. The app randomly crashes with an EXC_BAD_ACCESS
and my educated guess is that it crashes when the current UIViewController
is deallocated before the TSAlertView
has completed its dismiss animation.
An obvious workaround is to increase the duration of the animation between the current UIViewController
and the new one, but that would simply rely on good timing, which is recipe for disaster. Is there an easy way to prevent the presenting UIViewController
from being deallocated till the TSAlertView
is deallocated? The main project uses ARC while TSAlertView
is non-ARC.
EDIT: rubber-ducking in action. I added the following 2 lines in the -(void) show
method:
_presentingViewController = [[UIApplication sharedApplication] keyWindow].rootViewController;
[_presentingViewController retain];
And a corresponding release
in the -(void) dealloc
. Everything seems to work properly now. Am I creating a circular retain chain? Is there a better answer?
Peter Hosey is right - you should use Zombies to find exactly what's causing the crash.
However, my gut tells me (after looking at TSAlertView.h), that you're setting your view controller as the delegate when you show the alert view. You're guess seems likely, your view controller is being deallocated, and the alert view is attempting to talk to a dangling pointer.
Bad new is, if your controller has retained the AlertView, you have likely caused a retain loop with your fix. Even if you haven't, the change is so terribly likely to cause a retain loop, I wouldn't make it.
Good news is, you can still apply pre-ARC best practices, and nil your delegates. Under ARC, dealloc is still a valid method that is called when your object is deallocated - you just don't call super. This gives you a chance to cleanup when your object reaches the end of it's life. If you keep a reference to the alert view after you display it, then you should be able to do something like like the following to cleanup the dangling pointer:
- (void)dealloc {
self.alertView.delegate = nil;
}