2

I'm a little confused regarding Swift memory management. I understand that removeFromSuperview releases the view and makes it eligible for garbage collection if retainCount is 0. However, after tracking CFGetRetainCount(myView), I'm not sure how retainCount is computed and how to properly ensure that the view will be fully released after removeFromSuperview is called.

This is what I'm getting for a simple UILabel. Code:

let myLabel = UILabel()
   print("After creation")
   print(CFGetRetainCount(myLabel))
myLabel.frame = CGRect(x: 0, y: 0, width: view.frame.width - 40, height: 60)
   print("After frame set")
   print(CFGetRetainCount(myLabel))
myLabel.text = "Lorem ipsum"
myMainView.addSubview(myLabel)
   print("After added to superview")
   print(CFGetRetainCount(myLabel))

let otherLabel = UILabel()
otherLabel.frame = CGRect(x: 0, y: myLabel.frame.maxY + 20, width: view.frame.width - 100, height: 60)
   print("After referenced to position other view")
   print(CFGetRetainCount(myLabel))

myLabel.removeFromSuperview()
   print("After removed from superview")
   print(CFGetRetainCount(myLabel))

Console:

After creation
3
After frame set
3
After added to superview
4
After referenced to position other view
4
After removed from superview
3

That leads me to several questions:

1) I understand CFGetRetainCount includes weak references, but how is it 3 after I just created the UILabel?

2) I'm adding views inside a function with no class-wide reference when I don't need them later (I may add gestureRecognizers too) and remove them before removing the containing view with:

myMainView.subviews.forEach({
   $0.removeFromSuperview()
})

Is that considered good practice? Do gestureRecognizers prevent memory to be freed?

3) How to make sure memory is freed when the count is already at 3 when I create the view?

What am I missing?

Will
  • 1,123
  • 1
  • 9
  • 22
  • 1
    Worth reading (as well as following some of the links in the answers): https://stackoverflow.com/questions/4636146/when-to-use-retaincount – DonMag Sep 25 '19 at 18:01
  • "you should never user the retainCount" alright then lol – Will Sep 25 '19 at 18:11

2 Answers2

1

the link shared above by DonMag explains why you shouldnt use retainCount. ARC handles retain counts for you. what you do need to worry about is retain cycles. Nothing you mentioned doing above should create a retain cycle so you should trust that the compiler will remove the label from memory as long as its not referenced anywhere anymore. the common uses cases where retain cycles happen are delegates and blocks which refer to self and other objects.

Tahdiul Haq
  • 245
  • 3
  • 11
  • Thanks for your input. I ended up testing deinit on my custom UIViews. And as you say, memory seems to get deallocated as expected. – Will Sep 25 '19 at 21:36
0

So for those who end up here and wonder the same thing, CFGetRetainCount returns unreliable results as pointed out here.

To make sure memory is deallocated when using custom classes, you may call inside your class and test

deinit {
  print("Memory deallocated")
}

If your object is not retained, the method above is automatically called. See documentation here: https://docs.swift.org/swift-book/LanguageGuide/Deinitialization.html

Other leaks may be tested using Instruments (in Xcode: Product > Profile > Leaks) or using the Debug Memory Graph tool

enter image description here

More explanations here: https://www.youtube.com/watch?v=1LnipXiSrSM

So answers would be as follows:

1+3) Do not use CFGetRetainCount. See explanation above.

2) This seems to be working fine and memory is deallocated when I test the app.

Will
  • 1,123
  • 1
  • 9
  • 22