3
votes

My application crashes when simulating Memory warning in simulator with error:

[UINavigationController retain]: message sent to deallocated instance

I'm using ARC.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
      UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
     _window = window;


    [self startWithFlash];

     return YES;
}

- (void)startWithFlash
{
    [self.window.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];

    __weak typeof (self) weakSelf = self;

     WPRSplashViewController *splashViewController = [[WPRSplashViewController alloc] initWithNibName:@"WPRSplashView" bundle:nil doneCallback:^{
        [weakSelf startApplication];
    }];


     self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:splashViewController];

    [self.window makeKeyAndVisible];
}

- (void)startApplication
{    
     WPRMainViewController *mainViewController = [[WPRMainViewController alloc] init];
     UINavigationController * controller = [[UINavigationController alloc] initWithRootViewController:mainViewController];

     self.menuController = [[PHMenuViewController alloc] initWithRootViewController:controller
                                                                   atIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];

     self.window.rootViewController = self.menuController;

    [self.window makeKeyAndVisible];
}

This happens when somewhere in the app I call:

[((WPRAppDelegate*) [UIApplication sharedApplication].delegate) startWithFlash];

And right after that simulating memory warning.

Running Profile Tools with NSZombie enabled I get the following trace:

Profile tools with NSZombie enabled screen shot

This is not the only place with such crash. In every place where I use UINavigationController as wrapper for view controller and I present it as modal view, after simulating memory warning I get this crash.

I had very similar issue in other place for which I've posted here another question but did not find a reasonable solution: Similar issue

3

3 Answers

4
votes

Finally after days of investigations I've found a reason to all these crashes including the one described in "iOS memory warning sent to deallocated UIViewController"

The problem came out from PHAirViewController project. I still did not find out a real reason, but simply commenting out - (void)dealloc function in PHAirViewController.m file did the magic.

The main headache I got when was running Instruments to detect NSZombies. All traces were pointing to system classes like UINavigationController, UIImagePickerViewController etc... Due to this I started disabling ARC for parent controllers. In some places it helped but in some it didn't.

After a lot of voodoo I found out that every class (including system classes) was implementing UIViewCOntroller(PHAirViewController) Category and though - (void)dealloc function was always called on dismissing them.

Now the only thing left is to understand why this function is generating NSZombies. The interesting thing is that just commenting its content (which have only one line: self.phSwipeHandler = nil) does not have the same effect.

1
votes

Quickfix: insert assert([NSThread isMainThread]); to various places in your code where you access appDelegate.window.rootViewController. This should be done for write- and for read-accesses to the property! This will reveal the culprit. appDelegate.window.rootViewController must not be accessed from any other thread than the main thread.

Generally, there are these reasons why this may happen:

  1. You are using __unsafe_unretained variables.
  2. You are using an unsafe_unretained property.
  3. You are using non-ARC
  4. You are accessing the same variable from different threads at the same time
  5. You are accessing the same nonatomic, non-weak property from different threads at the same time

The fix for 1 and 2 is simple: Just don't use unsafe_unretained anymore.

The fix for 3 is: use ARC instead.

The fix for 4 and 5: use atomic properties instead, or synchronize access to your iVars. (Note that you must not access iVars from atomic properties directly as this breaks the atomicity.) Alternatively, use the property only from one thread, e.g. only from the main thread.

In your example, I assume that issue #5 applies. The culprit should be some non-main-thread accessing rootViewController from UIWindow.

0
votes

It is likely you are using an assign or __unsafe_unretained property somewhere in your code. Delegates should always be of type weak, so that the reference to the delegate object is nil'ed out on deallocation.

Also, calling:

[((WPRAppDelegate*) [UIApplication sharedApplication].delegate) startWithFlash];

... from within another class in your app is a bit of a smell. One that I've had many times. It means you have circular dependencies. Your app delegate is dependent on the class using this code (transitively, if not directly), and this class is dependent on your app delegate. Looking at your Instruments trace, it looks like you have adopted the delegate pattern else where, so you have some experience with decoupling. I would suggest bubbling that message up through a delegate chain, notification, or block.