16

I want to free up memory my ViewController used after dismissing it. I use the following code to present the new ViewController and dismiss the old one:

let sB: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let newVC: UIViewController = sB.instantiateViewController(withIdentifier: "MyVC")
self.present(newVC, animated: true, completion: { x in
    oldVC.dismiss(animated: false, completion: { _ in //oldVC: variable keeping track of currently visible view controller
        print("done")
    })
})

This code successfully presents newVC and prints done after dismissing oldVC. However, my memory still stays as high as it was when having oldVC on screen.
What am I doing wrong?

FYI

  • I am using ENSwiftSideMenu
    • Since I didn't get it to work in a different way, all my ViewControllers are a subclass of ENSideMenuNavigationController
  • I am getting console warnings about detached views and views not being in the window hierarchy
  • for all ViewControllers, both presentingViewController and presentedViewController are nil
Community
  • 1
  • 1
LinusGeffarth
  • 27,197
  • 29
  • 120
  • 174

4 Answers4

6

Check the following issues:

  1. You should dismiss the old ViewController before present a new ViewController, or you will dismiss the new one at the same time.
  2. Swift using ARC to manage memory. The oldVC is not released since some objects have a strong reference to it. Make an example, i have a ViewController with a variable tableView. The tableView will release when the ViewController released. But at the same time if you link it to another ViewController that is not released, the tableView will not release although the previous ViewController is released.

Solution:

  1. Present a UINavigationController and using setViewControllers to manage the transition of your ViewControllers.
  2. Remove the useless reference of the oldVC

If you really do not understand. You can search:

  1. ARC
  2. UIViewController transition
jhd
  • 1,243
  • 9
  • 21
3

Your oldVC will not be deallocated, since your newVC (and any UIViewController) keeps by default a strong reference to its presentingViewController - the controller that presented it. If this was not the case, you would never be able to safely dismiss your newVC without disturbing the view hierarchy.

As far as your specific problem is concerned, in order to deallocate your old view controller, I would suggest dismissing the oldVC first and then display your newVC from another ViewController, earlier in the stack (could be the UIApplication shared().keyWindow?.rootViewController)

spassas
  • 4,778
  • 2
  • 31
  • 39
  • Well I am getting a warning saying sth. about the view not being in window hierarchy. Until now I wasn't able to fix that issue and since it never caused any problems I didn't really have to. – LinusGeffarth Sep 27 '16 at 10:25
  • @LinusG. It is indeed issued as a warning (although in my experience it can result in errors) – spassas Sep 27 '16 at 10:29
  • Presenting from `rootViewController` leaves me w/ a blank screen :( – LinusGeffarth Sep 28 '16 at 13:14
3

I had the same issue for UINavigationController's push/pop. @spassas is right about the strong reference which is the reason your newVC is not deallocated. You should look at the newVC closely.

In my case (written in Objective-C) there was another view controller referenced as a private property of the pushed view controller (your newVC):

EventActionSheetViewController* actionSheet;

That actionSheet had a strong reference to the pushed view controller (newVC) as its delegate:

@property (nonatomic) id<EventActionSheetDelegate> delegate;

I've been checking my app's memory and found that the pushed view controller wasn't deallocated after being popped by the navigation controller. After a few hours I changed the strong reference to weak and it worked out:

@property (nonatomic, weak) id<EventActionSheetDelegate> delegate;

The problem was that my pushed view controller and its EvenetActionSheetViewController had strong references to each other and somehow did not let each other deallocate.

I recommend you to check if your newVC has similar strong references loops and change them to weak where possible. In Swift use weak let or weak var instead of let/var. Hope this will help you find the bug.

Vlad Fedoseev
  • 152
  • 1
  • 11
1

Try replacing your code with the following:

    let appDel = UIApplication.shared.delegate as! AppDelegate
    let sB: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
    let newVC = sB.instantiateViewController(withIdentifier: "MyVC")
    appDel.window?.rootViewController = newVC
    appDel.window?.makeKeyAndVisible()

Hope this works !

EDIT : You can also check that the old View Controller is being de-initialized by adding the following code to all the View Controllers :

    deinit {
        debugPrint("Name_Of_View_Controller deinitialized...")
    }

EDIT 2:

If you want to preserve the flow of presentation in view controllers and you only want to dismiss the last view controller, i.e.,

In a transition : masterVC -> oldVC -> newVC

And after the transition from oldVC to newVC, you want to return back to masterVC, freeing up memory by oldVC. You can pass the masterVC reference to the oldVC and achieve what you want to as following:

if(masterVC != nil) {
         oldVC.dismiss(animated: false, completion: {
            let sB: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
            let newVC = sB.instantiateViewController(withIdentifier: "vc2")
            self.masterVC!.present(newVC, animated: false, completion: {
                print("done")
            })
        })
}

This works for the case you stated.

Anmol Arora
  • 109
  • 5
  • Which one is not working for you? The first one, or the second one? And do you have any strong references in the oldVC ? – Anmol Arora Oct 06 '16 at 17:59