Утечки памяти при назначении текста UILabel (iOS, Swift 4, Xcode 9) - PullRequest
0 голосов
/ 12 мая 2018

Я работаю над новым приложением без раскадровки. Все прошло нормально, пока я не протестировал свое приложение с помощью инструментов: оно просачивалось каждый раз, когда я назначал строку метке. Когда я работал с раскадровкой, у меня не было таких утечек.

Я прочитал следующие ресурсы, чтобы найти ответ:

Самое популярное мнение - это ошибка в Инструментах, но она кажется мне слишком очевидной.

Утечка воспроизводится в пустом приложении. В контроллере корневого представления:

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()

   }
}

При тестировании этого приложения в Instruments на реальном устройстве (iPhone SE 11.2) я вижу следующее:

enter image description here

Когда я нажимаю _NSContiguousString, я вижу, что утечка памяти появляется в [UILabel setText:].

enter image description here

Я попытался установить метку как слабую, но затем она становится нулевой, когда я пытаюсь добавить ее в качестве подпредставления.

Итак, мои вопросы:

  • как я могу устранить эту утечку памяти сейчас и в будущем?
  • по этой причине мне следует создавать элементы пользовательского интерфейса только в файлах .xib / .storyboard?

Я новичок в разработке под iOS, поэтому я думаю, что мне не хватает чего-то очевидного. Я буду очень признателен за любую помощь или совет.

РЕДАКТИРОВАТЬ: Согласно @ Sh-Khan и @J.Doe ответам (спасибо вам, ребята!), Я добавил label.removeFromSuperview(), но все еще есть утечка .

EDIT2: С помощью @ J.Doe я узнал, что UILabel освобождается из памяти, вызывая removeFromSuperview и устанавливая его равным nil впоследствии. Утечка памяти в Инструментах осталась, но я отмечаю, что его ответ принят, потому что это то, что я хотел знать.

PS: После прочтения о NSString retain count Я думаю, что причиной утечки памяти может быть тот факт, что я использую строковый литерал, который не может быть освобожден, согласно обсуждению.

Ответы [ 2 ]

0 голосов
/ 12 мая 2018

Первая настройка

textForLabel = nil

не удалит или не сделает текст метки ноль, поскольку метка уже взяла его копию

вторая настройка

label = nil 

не достаточно, вы должны

label.removeFromSuperview()
0 голосов
/ 12 мая 2018

Возможно, я ошибаюсь, но я думаю, что это:

Слабый не увеличит счетчик ссылок.Поэтому присвоение метки объекта слабой метке var не имеет смысла.Это связано с тем, что слабая метка var будет равна нулю, поскольку созданный вами объект не имеет никакой ссылки (и, следовательно, он будет деинициализирован)

Давайте посчитаем, сколько ссылок вы имеете в своем коде на ваш созданный объект 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

У вас есть 1 ссылка на вид в вашем объекте Метка.Перед тем как сделать label = nil, вызовите label? .RemoveFromSuperview ().Я думаю, что у вас есть 0 ссылок -> это будет deinit.

edit:

Добавьте ниже подкласс UILabel в свой код:

class MyLabel: UILabel {
    deinit {
        print("I am gone!")
    }
}

Измените var label: UILabel? наvar label: MyLabel?

И

label = UILabel() до label = MyLabel()

И чеки в логах.Вы видите отпечаток "Я ушел!"?

Edit2: это печатает "Я ушел!"в пустом проекте с этим только как код:

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!")
    }
}
...