【问题标题】:Memory leaks when assigning text to UILabel (iOS, Swift 4, Xcode 9)将文本分配给 UILabel 时出现内存泄漏(iOS、Swift 4、Xcode 9)
【发布时间】:2018-10-22 17:14:51
【问题描述】:

我一直在开发一个没有情节提要的新应用。一切都很顺利,直到我用 Instruments 测试了我的应用程序:每次我将字符串分配给标签时,它都会泄漏。当我使用故事板时,我没有这样的泄漏。

我已阅读以下资源以找到答案:

最流行的观点是这是一个 Instruments 错误,但对我来说这似乎是一个太明显的方法。

泄漏在一个空的应用程序中重现。在根视图控制器中:

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

   }
}

在真实设备 (iPhone SE 11.2) 上的 Instruments 中测试此应用时,我看到以下内容:

当我单击 _NSContiguousString 时,我看到 [UILabel setText:] 中出现内存泄漏。

我尝试将标签设置为弱,但是当我尝试将其添加为子视图时它变为 nil。

所以,我的问题是:

  • 现在和将来如何消除这种内存泄漏?
  • 出于这个原因,我应该只在 .xib/.storyboard 文件中创建 UI 元素吗?

我是 iOS 开发的新手,所以我认为我遗漏了一些明显的东西。我将非常感谢任何帮助或建议。

编辑:根据@Sh-Khan@J.Doe 的回答(非常感谢你们!),我添加了label.removeFromSuperview(),但仍然存在泄漏。

EDIT2:在@J.Doe 的帮助下,我了解到 UILabel 通过调用removeFromSuperview 并将其设置为 nil 从内存中释放。 Instruments 中的内存泄漏仍然存在,但我将他的回答标记为已接受,因为这是我想知道的。

PS:根据讨论,在阅读了NSString retain count 之后,我认为内存泄漏的原因可能是我使用了无法释放的字符串文字。

【问题讨论】:

  • 如果将 var 语句移动到 viewDidLoad 中会发生什么?如果您仍然有泄漏,那么它可能不是来自标签,因为它会在函数结束时被取消初始化。
  • @Zyntx 谢谢!是的,根据 Instruments 的说法,泄漏就在那里。但是应用程序中没有任何代码,除了我在上面发布的(以及 AppDelegate 将 rootViewController 设置为窗口)。一个几乎是空的应用程序中怎么会有任何泄漏?我很困惑。
  • 你可以将 textForLabel 设置为 let 吗? -- 让 textForLabel: String = "Hello"
  • @Zyntx 是的,谢谢!我也试过了。事实上,这不是 UILabel 错误,事实证明:它是 Swift._NSContiguousString 泄漏(1 泄漏 48 字节),而且只有一次。我不确定,但我猜测泄漏的原因可能是与 NSString 桥接(我还看到堆栈跟踪顶部的 [_SwiftNativeNSStringBase retain])。你觉得会是这样吗?
  • 希望,是吗?!老实说,我有限地使用它来处理不良情况,并且不会抓住这个。

标签: ios swift memory-leaks uilabel


【解决方案1】:

也许我错了,但我认为:

Weak 不会增加引用计数器。因此,将对象标签分配给弱 var 标签是没有意义的。这是因为weak var label 将是nil,因为你创建的对象没有任何引用(因此它会去初始化)

让我们计算一下您的代码中有多少对您创建的对象标签的引用。

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 个引用 -> 它会取消初始化。

编辑:

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

【讨论】:

  • 非常感谢!不幸的是,仍然存在泄漏。我现在将编辑我的问题。
  • @joliejuly 嗨,奇怪的是它不起作用。也许你在编辑的代码中犯了一个错误,但你应该在调用 label = nil 之前调用 removeFromSuperview。
  • 哦!谢谢,我不知道!我现在改变了它,但这个愚蠢的泄漏仍然存在。也许是因为这是我听说从未定义过的根视图控制器?
  • 谢谢!我现在就去试试!
  • 很遗憾,没有出现日志,泄漏是有的(我想了两天了
【解决方案2】:

第一次设置

textForLabel = nil

不会删除或使标签文本为零,因为标签已经复制了它

第二个设置

label = nil 

你还不够

label.removeFromSuperview()

【讨论】:

  • 谢谢!我错过了它,但不幸的是,它仍然显示泄漏。我将相应地编辑我的问题。
猜你喜欢
  • 2018-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-21
相关资源
最近更新 更多