0

I'm in front of a big issues, and the only reason I can't find a solution is because my lack of knowledge about swift and memory management in swift. So here is my concerns. I'm working in swift 4.0 and iOS 9.3

I'm actually making a picture gallery app with login/logout. Basic application. I'm working in cleanSwift So I don't have those HUGE ViewControllers.

My application is separate in 3 VC : The login, the gallery and the settings (which contains the LogOut).

Here is my problem. When I log out, I want to create a new loginVC and clear all previous VC. So I have my cleanMemory function which set all the UIImage to nil

func cleanMemory(request: Gallery.Request) { // Interactor
    worker.cleanMemory(completionHandler: { (Value) in

        self.interventions?.removeAll() // Interventions contains UIImages
        self.interventionsSelected.removeAll() // InterventionsSelected contains UIImages

    })

}

and then I delete the rests of the UIImage and the VC

func cleanMemory() {

    interactor?.cleanMemory(request: Gallery.Request())
    self.displayedInterventions.removeAll() // displayedInterventions contains UIImages
    interactor = nil
    router = nil
    self.removeFromParentViewController()
    self.navigationController?.popViewController(animated: true)
}

But when I create my new LoginVC.. my RAM didn't decrease.. And when I check the app memory, not a single VC was deleted.. And when I execute the loop Logout/Login 3 times, my app crash because I'm not well managing my RAM_

So where did I get wrong, and why ??

Thanks you for your answer.

EDIT: I was having 2 problems :

  • My completionHandler was keeping my VC alive
  • I was switching VC with .present, so that was keeping my VC in memory.

So you should change VC like that :

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destinationVC = storyboard.instantiateViewController(withIdentifier: "LoginController")
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = destinationVC
Sacha.R
  • 394
  • 2
  • 17
  • How do you go back to LoginVC again, when user clicks on Logout in SettingsVC? – Mohammad Zaid Pathan Jan 04 '18 at 14:27
  • 1
    Maybe you have a retain cycle ? – Francis.Beauchamp Jan 04 '18 at 14:29
  • Absolutely @ZaidPathan. I have a logout button, I create a new LoginVC, I set my loginVC to root of the application and switch to it. – Sacha.R Jan 04 '18 at 14:33
  • I don't think I have a retain cycle @Francis.Beauchamp. All my VC link are weak. My questions was more about how memory and "free" memory works in those cases – Sacha.R Jan 04 '18 at 14:35
  • You might want to take a peek at Instruments, which can help you find memory issues. Here's a link to a tutorial on it. https://www.raywenderlich.com/166125/instruments-tutorial-swift-getting-started – Adrian Jan 04 '18 at 14:38

1 Answers1

3

To remove viewController from memory you simply need to remove it from navigation stack. So when you call navigationController?.popViewController(animated: true) and back to previous view controller you already destroy that controller.

Then,

Here is my problem. When I log out, I want to create a new loginVC and clear all previous VC. So I have my cleanMemory function which set all the UIImage to nil

on logout it's good practice to stop all request but you don't need to do any changes to UI, because it takes some time and it doesn't need to "remove controller from memory". How to check if view controller completely removed from navigation stack? Simply write print statement in deinit func, compile code and go back from this view controller.

deinit {
    print("ViewController deinit")
}

If this print works fine (you can see text in xcode console), you achieve the result - controller has been removed from navigation stack, but if there is no print result you probably forget to right manage your closures. For example

worker.cleanMemory(completionHandler: { (Value) in
    ...
})

this closure may hold your controller when your think that controller already deallocated and it means that your controller present somewhere in memory. To prevent these retain cycles you need to use [unowned self] of [weak self] (just google for this keywords, it's very easy to understand) like this:

// or you can use `[unowned self]`
worker.cleanMemory(completionHandler: { [weak self] (Value) in
    guard let `self` = self else { return } // only for `weak` way
    ...
})

So, in this case there are nothing that can hold your controller alive after pop from navigation stack action or so. These are simple rules that you should follow to write well managed code.

pacification
  • 5,838
  • 4
  • 29
  • 51
  • OH. I get it. That makes all sense. I didn't think about the completionHandler that maintains the VC present. I had another problem, I was having retain cycle in my routing, I was storing the VC and switching with a ".show". That makes also sense, I fixed it and change my routing with changing to root. Thanks you again, I will edit and close my post ! :) – Sacha.R Jan 04 '18 at 15:04