Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ViewControllers are not destructing

I've got a serious problem with my iOS app.

I have a login logic in my application. When logging in and then logging out, some view controllers are not destructing. This causes some issues, for example, some events that I emit using NSNotifcationCenter are emitted few times. These issues are avoidable, but I really want a solution to avoid some view controllers to stay open in the background without me controlling it.

The way control the login logic is as follows:

In the app delegate start function, if the user is already logged in, I set the root view controller to the main usable view controller. Therefore, I'm not doing anything and the root view controller is set to the login view controller navigation controller through the storyboard.

When the user logs off, I use a modal segue to transition the view controller back to the login view controller navigation controller.

As you may understand I'm using storyboards, swift and the newest iOS.

My logout code is segue that take me to the LoginViewControler:

self.performSegueWithIdentifier("Logout", sender: self)

My app delegate code:

if (userDefaults.valueForKey("uid") != nil) {
    let tabBarView = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("TabBarViewController") as! TabBarViewController
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    appDelegate.window?.rootViewController = tabBarView        
 }

What am I doing wrong?

I would appreciate help :)

EDIT

I even tried just setting the root view controller in the logout action and that didn’t help either. How’s that even possible?

This is how I do the logout now:

let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let newRootViewController = self.storyboard?.instantiateViewControllerWithIdentifier("LoginNavigationController") as! UINavigationController          

appDelegate.window!.rootViewController = newRootViewController
like image 845
gal Avatar asked May 28 '16 14:05

gal


4 Answers

Adam H. is right. If that doesn't work, then check for IBOutlets and delegates that have strong relationships, and change them to weak relationships. i.e.

@IBOutlet weak var collectionView: UICollectionView!

Without the weak keyword the view controller will never be disposed.

Depending on how your project is setup, if you are using a navigation controller (which I recommend) every time someone logs out you would put

dispatch_async(dispatch_get_main_queue()) {
    self.navigationController.popToRootViewControllerAnimated(true)
}

That will pop everything off the navigation stack, which will dispose of all view controllers (unless you have strong relations, then they won't be disposed)

like image 113
SeanRobinson159 Avatar answered Sep 30 '22 01:09

SeanRobinson159


No matter how you choose to manage your trasitions , don't forget to add/ remove the observer whenever the view controller apear/disappear.

like image 26
Nevgauker Avatar answered Sep 30 '22 00:09

Nevgauker


If the logged in screen presents the login screen and the login screen presents the logged in screen then you will have a cycle that keeps piling on new view controllers. To solve this, one must not present the other, but unwind to it. Another possibility is to hold instances of each as singletons and only present those.

like image 35
Adam H. Avatar answered Sep 30 '22 01:09

Adam H.


I implemented something like that not long ago and to me it seems you're abusing the UINavigationController life cycle. After reading your question twice, if I understand it correctly, it seems you're initializing your login view controller as a UINavigationController which stacks-up view controllers. Once user logs out, you're keeping the stack, adding more ViewControllers to the stack using the performSegue. You can avoid it by using two different scenes - 1) Login View Controller which stands by it self. 2) Main flow of your app - can start with UITabController/ UINavigationController, both or whatever.

In AppDelegate you check - If user is logged in - do your logics and set the app rootVC to the main flow vc. Otherwise you set the loginVC (UIViewController) to be the root.

This also allows you to pop the login VC anywhere in the main flow, when needed, without interfering with the main flow. In your case the loginVC is always UINavigationController's root so you must popToRootVC every time you wish to see it or performSegue to it which is worse because then you create another instance of a UINavigationController and resources never get deallocated.

Obviously in programming, in most cases, there are many solutions to one problem. I'm sure your problem can be solved using your flow. I just think it's bad experience to stack a loginVC over a navigation controller.

like image 25
devdc Avatar answered Sep 30 '22 01:09

devdc