0

Say I have view controllers A, B, C, D & E all embedded in a navigation controller. In view controller B, I have a custom UIImageView object. In C, I have a custom UITextfield object. Both custom classes have a reference to the view controller for various reasons such as I have to perform things like segue when a user taps the image view. To accomplish this, I have this inside each custom class file:

var controller: UIViewController?

And then inside each view controller, inside viewDidLoad I set that variable to self and everything works as expected (segues on tap etc..)

I have an unwind segue from E back to A. However, I noticed that due to these custom objects in view controllers B & C, both were not being deallocated due to a retain cycle caused by having this reference to the view controller. I fixed the issue by setting the controller variable to nil upon segue, however this creates a problem such that if the user goes back (pops the current view controller), because I set the controller variable to nil upon segue, nothing works (it wont segue again because controller var = nil). I thought I might fix this by adding viewWillAppear code as follows:

 override func viewWillAppear(_ animated: Bool) {
    usernameTextField.controller = self
    passwordTextField.controller = self
}

Because I read that viewWillAppear will be called each time the viewcontroller comes into view. This did not fix the problem.

Any ideas on how to go about this? How can I set the controllers to nil during the unwind maybe...?

user7804097
  • 308
  • 1
  • 3
  • 17

3 Answers3

1

var controller: UIViewController? should be a weak reference. Like this:

weak var controller: UIViewController?

To know more about that read about Resolving Strong Reference Cycles Between Class Instances in Swift's documentation.

Mo Abdul-Hameed
  • 6,030
  • 2
  • 23
  • 36
1

You should use weak references when you keep some ViewControllers

weak var controller: UIviewControler?

You should check everything link to retain cycle, and referencing in swift :

I had similar issues, I advice you to look at those link : How can I manage and free memory through ViewControllers

Sacha.R
  • 394
  • 2
  • 17
1

As the other answers have said you need to make it a weak reference like this:

weak var controller: UIViewControler?

However I would go further and say that you should not be keeping a reference to to a UIViewController inside any UIView based object (UIImageView, UITextField, etc). The UIViews should not need to know anything about their UIViewControllers.

Instead you should be using a delegation pattern. This is a basic example:

1) Create a protocol for the custom UIImageField like this:

protocol MyImageFieldProtocol: class {
    func imageTapped()
}

2) Then add a delegate like this:

weak var delegate: MyImageFieldProtocol?

3) Your UIViewController then conforms to the protocol like this:

class MyViewController: UIViewController, MyImageFieldProtocol {
}

4) Somewhere inside the view controller (viewDidLoad is usually a good place you assign the view controller to the image views delegate like this:

func viewDidLoad {
    super.viewDidLoad()
    myImageView.delegate = self
}

5) Then add the function to respond to the protocol action to the view controller like this:

func imageTapped {
    self.performSegue(withIdentifier: "MySegue", sender: nil)
}
Upholder Of Truth
  • 4,643
  • 2
  • 13
  • 23
  • I REALLY REALLY appreciate you going so far as to introduce me to protocols. Everything is self-taught for me and I was able to get it all working following your exmple. I will study them further now. – user7804097 Jan 06 '18 at 00:39
  • No problem glad to help and that you got it working. – Upholder Of Truth Jan 06 '18 at 09:22