5

I'm working on an app that changes it's rootViewController depending on it's state. To make a switch I use this code:

- (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [self createManagedDocumentAndContext];

    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    NSString *storyboardId = [userDefaults boolForKey:@"Profile Created"] ? @"User Stats" : @"Profile";

    self.window.rootViewController = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:storyboardId];

    return YES;
}

To switch back I call this method from presented ProfileVC:

- (void)returnOldRootViewController
{
    UIWindow *currentWindow = [UIApplication sharedApplication].keyWindow;

    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    OLDUserStatsVC *userStatsVC = [storyboard instantiateViewControllerWithIdentifier:@"User Stats"];
    userStatsVC.userProfile = self.userProfile;

    [currentWindow setRootViewController:userStatsVC];

    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults setBool:YES forKey:@"Profile Created"];

}

THE PROBLEM: rootViewController is changed, but previous one is not deallocated. It stays on the "background" of the app - I can see it when VC changes to another one.

The question is how to release it properly? Thank you very much!

  • If it were Swift (which I've grown to really appreciate in lieu of ObjC, personally) memory management would deallocate it automatically as soon as you overwrote the last reference to it with another VC handle, and then a printf() in deinit() would show you its deallocation. presume ARC in ObjC would do that too. Apparently just setting the main window root VC isn't enough to cause it to replace it and it's references. Interesting question. – clearlight Mar 26 '15 at 16:44
  • Check out this thread: http://stackoverflow.com/questions/15774003/changing-root-view-controller-of-a-ios-window – clearlight Mar 26 '15 at 16:48
  • "but previous one is not deallocated" How do you know? Show me the logging on `dealloc` that proves this. – matt Mar 26 '15 at 16:49
  • While you change the root view controller, I'm pretty sure you also need to remove whatever is in the window before you set the new root view controller or else the old stuff will stay there. – AdamPro13 Mar 26 '15 at 16:50
  • @AdamPro13 doesn't that depend on whether they are weak or strong references? – clearlight Mar 26 '15 at 16:51
  • The type of reference shouldn't matter because the window will hold a strong reference to its subviews so they don't deallocate. This doesn't answer why the view controller isn't deallocating but it does answer why he can still see the old view after changing the root view controller. – AdamPro13 Mar 26 '15 at 17:02
  • @matt thing is that there is no output on 'dealloc' - it is not called :( – Aleksandr Shcherbakov Mar 26 '15 at 18:34
  • @AdamPro13 What do you mean by "remove whatever is in the window"? I tried to remove all subviews and set it to nil - didn't work. – Aleksandr Shcherbakov Mar 26 '15 at 18:36
  • 1
    You should not be implementing `willFinishLaunching` unless you are doing view controller save-and-restore. Use `didFinishLaunching` instead. – matt Mar 26 '15 at 19:14

1 Answers1

4

The real problem here is that you are changing the root view controller of the window. Don't do that. You should be setting this once and never again. There should be just one root view controller for the lifetime of the app.

Find another architecture for displaying the correct view controller and switching between them as desired. For example, they might both be children of the root view controller, or one might be a presented view controller in front of the other.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Thank your for the answer. I know that this is a bad architecture - I'm a beginner :) Unfortunately, this is the only option I've found. I need to present one VC when my app is first started - for settings. And another VC whenever else. I changed rootViewController because if I don't, it is the first one shown to user. I would appreciate it very much if you could advice me another way! Thank you! – Aleksandr Shcherbakov Mar 26 '15 at 18:41
  • I already told you. If the basic view controller is the User Stats, then jut make the User Stats the root view controller. Make the Profile, if it is needed, a presented view controller on top of that; when the user is finished with the Profile, simply dismiss it, revealing the User Stats. – matt Mar 26 '15 at 19:13