Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strongly referenced variable may cause memory issues

I have been programming in Swift for a couple months now. Recently, I have focused more on concepts of how Swift as a language works.


Hence, recently while reading apple documentation on Automatic Reference Counting(ARC), I came across the following lines:

This one on top:

In most cases, this means that memory management “just works” in Swift, and you do not need to think about memory management yourself. ARC automatically frees up the memory used by class instances when those instances are no longer needed.

And in the next paragraph, the following:

To make this possible, whenever you assign a class instance to a property, constant, or variable, that property, constant, or variable makes a strong reference to the instance. The reference is called a “strong“ reference because it keeps a firm hold on that instance, and does not allow it to be deallocated for as long as that strong reference remains.


I am a little confused as to what is the dynamics of the situation. I have noted while using storyboards, that you set reference to weak, hence the class looks like this, also what I would call case 1:

Case 1

class SomeClass : UIViewController {
    @IBOutlet weak var nameLabel : UILabel!

    override func viewDidLoad() {
        nameLabel.text = "something."  
    }  
}

Here, the label has one-to-one weak reference with the ViewController, and as soon as the Controller is changed, reference is broken (memory dealloc) since it is weak. Hence, no issues related to the memory.

Pardon me if the above statement is wrong or loosely held. I would be glad if someone confirms my assumption or counters it.


My question is about the second case however, where I do not use storyboards and class looks like below:

Case 2

class SomeClass : UIViewController {
    var nameLabel : UILabel = {

      let label = UILabel()
      label.translatesAutoresizingMaskIntoConstraints = false
      return label

    }()

    override func viewDidLoad() {
        view.addSubView(nameLabel)
        // view.addConstraints...
    }  
}

For the above case, My assumption is that the ViewController has one-on-one strong reference with the label, and the view inside ViewController also has strong reference with the label.. If the class is changed/ label is removed from subview.. then I think the memory would not be deallocated. Or at least the view controller will maintain a strong reference to the label (as per the docs.)

I confirmed this by removing label from view's subviews and printing out the label (It gave me an instance of UILabel with frame that was at 0 origin and 0 size.) hence an instance that isn't nil.

The only thing I could gather from this was that although the label was removed from UIView, it still maintained a strong reference with the controller, hence permanent state in memory. Am I right?

If this is the case. How should I prevent my code from having such memory issues? The bigger problem is that if I declare my variable like so, I get a nil while adding it as a subview of main view in controller.

    weak var nameLabel : UILabel = {

      let label = UILabel()
      label.translatesAutoresizingMaskIntoConstraints = false
      return label

    }()

If declaring variables like in the second case can cause permanent strong references how should I declare them instead to not have memory issues?


So to conclude, my question is:

In cases where no storyboard outlets are used, and variables are strongly referenced to the view controller, will these references cause memory issues?

If so, what code declaration practice must I follow?

If not so, please provide thoughtful arguments with valid explanations to counter it.


Again, pardon me if I am incorrect anywhere.

Thank you in advance.

like image 425
Akshansh Thakur Avatar asked Jul 13 '16 08:07

Akshansh Thakur


1 Answers

The only thing I could gather from this was that although the label was removed from UIView, it still maintained a strong reference with the controller, hence permanent state in memory. Am I right?

No. There's no big issue here.

The label has no strong reference to the view controller — if it did, that would be a retain cycle and would cause both the label and the view controller to leak. For this very reason, a view should never keep a strong reference to its view controller.

Here, however, it's the other way around: the view controller has a strong reference to the label. That's fine. It's true that the label therefore stays in existence after it has been removed from its superview. But that might not be bad. In many cases, it's good! For example, suppose you intend to put the label back into the interface later; you will need to have retained it.

If you are sure you won't need to keep the label around later, then simply use an Optional wrapping a UILabel as your instance property. That way, you can assign nil to the label instance property when you're done with it, and the label will go out of existence.

But in any case there is no leak here and you should just stop worrying. When the view controller goes out of existence, the label will go out of existence too. The label lived longer than it had to, but that's tiny and unimportant on the grand scale of things.

like image 119
matt Avatar answered Oct 24 '22 00:10

matt