iosswiftmemory-leaksuinavigationcontroller

How to have a repeating workflow of view controllers without a memory leak?


Within a NavigationController, I have a workflow of three view controllers: A -> B -> C. Each time a workflow is completed, a new workflow begins, so A -> B -> C -> A -> B .... Right now, I simply performSegue to go from one view controller to the next. This "works" perfectly, except that all prior view controllers remain on the navigation stack, effectively leaking memory.

The user can press back to return the previous controller in a specific workflow, but they never need to return to the previous workflow. I.e. they can return from B to A, but can't return from A to the previous C. As such, it seems like I could fix the leak by discarding all the view controllers in the previous workflow when starting a new workflow.

I attempted to do so with navigationController?.viewControllers = [] in C's prepare(for segue..., but all view controllers are still retained in the memory graph. In the memory graph, the problem seems to be a chain of _childModalViewController from the UINavigationController to the first A to the first B to the first C to the second A etc.

It seems that either navigationController?.viewControllers = [] is not the right approach, or I need to do something further so that the navigation controller does not return the previous view controllers through _childModalViewController. I've tried many variations of this, popToRootViewController, removeFromParent, willMove(toParent: nil), dismiss, etc, but haven't found anything that affects the navigation controller's _childModalViewController. Any suggestions on the right way to implement repeating workflows?


Solution

  • After you go from C to A, after the segue is complete and the interface has settled down (so that the navigation controller's children are A1, B1, C1, and A2), set the navigation controller's view controllers to the same A instance (i.e. A2, the one that is now the navigation controller's top view controller). This will invisibly remove the previous A, B, and C (i.e. A1, B1, and C1) from the view controller hierarchy and they will go out of existence.

    I leave it to you to decide how to know when the right moment is. For example you might do it in A's viewDidAppear, but only (of course) the first time this A instance's viewDidAppear is called. But there are many other ways to obtain the right moment.