I've been working on a new app with no storyboard. All went fine until I tested my application with Instruments: it leaked every time I assigned a string to a label. When I worked with a storyboard, I didn't have leaks like that.
I have read the following resources to find the answer:
UILabel memory leak?
memory leak in cell with UILabel with ARC
Instruments show "_NSContiguousstring" memory leak when scrolling UITableView
Potential Leak, assigning NSString-Property to UILabel
iOS: Debugging memory leaks for UILabel in swift
The most popular opinion is that is an Instruments bug, but it looks like a too obvious approach to me.
The leak reproduces in an empty application. In the root view controller:
class ViewController: UIViewController {
var label: UILabel?
override func viewDidLoad() {
super.viewDidLoad()
label = UILabel()
view.addSubview(label!)
var textForLabel: String? = "Hello"
label?.text = textForLabel
//attempt to free the memory
textForLabel = nil
label = nil
//EDIT: added after @J.Doe and @Sh-Khan answers, but it's still leaking
label.removeFromSuperview()
}
}
While testing this app in Instruments on a real device (iPhone SE 11.2) I see the following:
When I click on _NSContiguousString, I see that memory leak appears in [UILabel setText:].
I tried to set label as weak, but then it becomes nil when I try to add it as a subview.
So, my questions are:
I am new to iOS development, so I think that I'm missing something obvious. I will highly appreciate any help or advice.
EDIT: According to @Sh-Khan and @J.Doe answers (thank you guys so much!), I added label.removeFromSuperview()
, but there is still a leak.
EDIT2: With @J.Doe help, I learned that UILabel gets released from memory by calling removeFromSuperview
and setting it to nil afterwards. The memory leak in Instruments remained, but I mark his answer accepted because that's what I wanted to know.
PS: After reading about NSString retain count I think the reason of memory leak might be the fact I am using a string literal that cannot be released, according to the discussion.
Maybe I am wrong, but I think this:
Weak will not increase the reference counter. Therefore, assigning a object label to a weak var label, does not make sense. This is because weak var label will be nil, because the object you created does not have any reference (and therefore it will deinitialize)
Let's count how many reference you have in your code to your created object Label.
label = UILabel() // 1
view.addSubview(label!) // 2
var textForLabel: String? = "Hello"
label?.text = textForLabel
//attempt to free the memory
textForLabel = nil
label = nil // 1
You have 1 reference left in your view to your object Label. Before you do label = nil, call label?.removeFromSuperview(). I think than you have 0 references -> it will deinit.
edit:
Add below subclass of UILabel into your code:
class MyLabel: UILabel {
deinit {
print("I am gone!")
}
}
Change var label: UILabel?
to var label: MyLabel?
And
label = UILabel()
to label = MyLabel()
And check to logs. Do you see the print "I am gone!"?
Edit2: this prints "I am gone!" in a empty project with this only as code:
import UIKit
class ViewController: UIViewController {
var label: MyLabel?
override func viewDidLoad() {
super.viewDidLoad()
label = MyLabel()
view.addSubview(label!)
let textForLabel: String? = "Hello"
label?.text = textForLabel
//EDIT: added after @J.Doe and @Sh-Khan answers, but it's still leaking
label?.removeFromSuperview()
label = nil
}
}
class MyLabel: UILabel {
deinit {
print("I am gone!")
}
}
First setting
textForLabel = nil
won't remove or make the label text nil as the label already took a copy of it
second setting
label = nil
is not enough you have to
label.removeFromSuperview()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With