将文本分配给UILabel(iOS,Swift 4,XCode9)时内存泄漏

我一直在开发一款没有故事板的新应用。 一切顺利,直到我用仪器测试我的应用程序:每次我将一个字符串分配给标签时它就泄露了。 当我使用故事板时,我没有那样的泄漏。

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

  • UILabel内存泄漏?

  • 使用带UILabel和ARC的单元中的内存泄漏

  • 滚动UITableView时,仪器显示“_NSContiguousstring”内存泄漏

  • 潜在泄漏,将NSString-Property分配给UILabel

  • iOS:在swift中调试UILabel的内存泄漏

  • UILabel和内存泄漏
  • 在UIView / Memory泄漏中生成UILabel

最流行的观点是,这是一个仪器错误,但对我来说它看起来是一个太明显的方法。

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

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)上的仪器中测试此应用程序时,我看到以下内容:

在此处输入图像描述

当我点击_NSContiguousString时,我发现内存泄漏出现在[UILabel setText:]中。

在此处输入图像描述

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

所以,我的问题是:

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

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

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

编辑2:在@ J.Doe的帮助下,我了解到UILabel通过调用removeFromSuperview并在之后将其设置为nil而从内存中释放。 仪器中的内存泄漏仍然存在,但我接受了他的回答,因为这是我想知道的。

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

也许我错了,但我想这个:

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

让我们计算您的代码中有多少引用到您创建的对象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个引用 – >它会去掉。

编辑:

将UILabel的以下子类添加到您的代码中:

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

更改var label: UILabel?var label: MyLabel?

label = UILabel() to 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!") } } 

第一次设定

 textForLabel = nil 

不会删除或使标签文本为零,因为标签已经取了它的副本

第二个设定

 label = nil 

你还不够

 label.removeFromSuperview()