Vito Valov
Vito Valov

Reputation: 1795

Navigation Controller back button is not releasing memory of dismissed viewController

Newbie question here. Imagine a very basic storyboard with 2 vc (A and B). A is embedded in a navController. A has a collectionView showing a grid of images. B is displaying the clicked grid item in big. So simple list->detail.

Doing all with IB, I ctrl-dragged from collectionView cell item to B and selected 'show (e.g Push)' segue.

Now when I run the app and click multiple times on image in grid and then on '< Back' button, I explore the memory graph. I can see 10 'B' view controllers if I did the navigation 10 times.

That causes a lot of memory to be used and it grows every time.

I found a few posts speaking about unwind, and pop to root vc, but all are dealing with programmatic navigation. Here's just the case of simple storyboard done all with IB.

Expected: A->B->A. Memory: A

Reality: A->B->A. Memory: A, B

How can I avoid retaining the memory for those vc that are dismissed?

in A I have:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "dreamDetail" {
        let newViewController = segue.destination as! DreamDetailViewController
        newViewController.dream = allDreams?[(collectionView.indexPathsForSelectedItems?.first?.item)!]
        newViewController.dreams = allDreams
    }
}

in B I have:

weak var dream: DreamRealm?{
    didSet {
    }
}

var dreams: [DreamRealm]?{
    didSet {
    }
}

DreamRealm is just a Realm model.

class DreamRealm: Object {

@objc dynamic var filename: String? = nil
@objc dynamic var path = ""

Upvotes: 0

Views: 1326

Answers (3)

thetrutz
thetrutz

Reputation: 1545

As an addition to @Arrabidas92's answer:

  • watch out for double nested blocks

I built a retain cycle with the following code:

navigationItem.reactive.rightBarButtonItems <~ user.producer.map{ $0
    .map{ [weak self] user in
        guard let self = self else { return [] }

I thought, that the [weak self] of the inner map would suffice. It does not. The outer map already captures self (to pass it to the inner?). My retain cycle went away after writing user.producer.map{ [weak self] $0.

Upvotes: 0

Roman Simenok
Roman Simenok

Reputation: 570

  1. All IB connections with UI elements(in view controller) must be weak!
  2. Try to release strong objects in dealloc method;

Sometimes there are some delay before garbage collector deallocate objects, do "show" and "back", wait ~10 seconds and see if more memory are released.

Upvotes: 0

Arrabidas92
Arrabidas92

Reputation: 1153

To avoid retaining the memory for your VC that are dismissed, you need to check if there is any retain cycle.

One step to help you to check if your VCs are correctly deinitialised, you can implement a method called deinit. It's a method called when your VC is deinit and no longer in the memory. You can print a message to see if it's the case or not.

If it's not the case, you probably have a strong reference somewhere in your code. You need to avoid it by weakening your reference with weak keyword or unowned or just delete it if you don't need it.

Upvotes: 1

Related Questions