【问题标题】:Removing objects from memory when switching storyboard scenes切换情节提要场景时从内存中删除对象
【发布时间】:2014-11-21 04:22:17
【问题描述】:

我正在以编程方式创建所有显示对象并将它们添加到我的故事板场景子视图中。

例如:

let button: UIButton = UIButton( frame: CGRect( x: 0, y: 0, width: 160, height: 50 ) )
self.view.addSubview( button )

在更改情节提要场景时,我遇到了巨大的内存泄漏(每次场景更改之间的内存使用量几乎翻了一番),这当然表明这些对象在更改场景时并没有被自动删除。我已经阅读了一些关于 Swift 垃圾收集的内容,但没有找到关于这个特定用例的太多信息。

通过更改情节提要场景,我的意思是通过当前视图控制器,如下所示:

self.presentViewController( targetController, animated: true, completion: nil )

话虽如此,我的问题是:

1) 可以像这样移除对象:

button.removeFromSubview()
button = nil

正确吗?

2) 有没有办法在循环中删除插入到视图子视图中的任何对象,并将其分配给 nil 以完全删除与该对象关联的任何引用?我可以一个一个地删除它们,但这是一项艰巨的任务。

3) 有没有办法在改变场景后自动从内存中删除对象?这将是最好的解决方案。

非常感谢您提供解决方案的示例。

提前致谢。

【问题讨论】:

  • 你在哪里添加子视图?视图加载?出现了吗?初始化?
  • 在 viewDidLoad 中。谢谢

标签: memory swift storyboard


【解决方案1】:

一些可能需要澄清的事情:

1) 通过情节提要添加的按钮(那些标记为@IBOutlet 的按钮)很弱;您不需要将它们归零,因为您对它们的引用不会将它们保留在内存中。

2) presentViewController 的行为可能不符合您的预期。最重要的是,不会替换现有的视图控制器与新的视图控制器;它“呈现”新的视图控制器来自旧的视图控制器。为了说明这一点,您可以从新的视图控制器中调用self.presentingViewController,它将为您提供对前一个视图控制器的引用;它还在记忆中。

但是,视图控制器并不是很大,当它们不在屏幕上时,它们的视图就会被卸载。但是,如果您在呈现视图控制器中持有一些大型资源,这些资源将持续存在。我建议通过在 viewWillAppear/viewDidAppear(而不是 viewDidLoad)中加载这些资产,然后在 prepareForSegue: 或 viewDidDisappear 中卸载它们来解决这个问题(我认为 didDisappear 没有被一致调用存在一些长期存在的问题?我有某种心理在那里标记,但我不确定来源是什么......)

或者,如果您真正想做的是完全更改为新的根视图控制器,您可以通过 AppDelegate 的 .window 属性来完成,如下所示:

let storyboard = UIStoryboard(named: "NewStoryboard", bundle: nil)
let newVC = storyboard.instantiateInitialViewController
UIApplication.sharedApplication().delegate.window.rootViewController = newVC

不过,我不会过度使用它;它在您可能在第一次启动时显示教程,然后想要加载正常视图层次结构的地方最有用。

【讨论】:

  • 这接近我正在寻找的答案。为了进一步阐述,我不通过情节提要创建任何东西,除了我的视图控制器之外,所有东西都是以编程方式创建的(我将它们添加到情节提要并实例化它们是需要的。至于卸载对象,在这种情况下,循环遍历子视图就足够了?他们的引用呢?我是否需要将每个对象添加到一个数组中,以便我可以循环遍历并在之后将它们归零?同样的问题适用于视图控制器类中的任何变量。我想要一种方法来转储所有内容
  • 补充一下我上面的评论,循环遍历子视图似乎还不够,在场景之间反复切换后我仍然会出现内存泄漏。这就是我询问其他人在专门处理以编程方式创建的对象时如何清除他们的引用的原因。在我使用的其他框架中,删除对象后将引用归零是标准做法,但可以通过遍历子视图、删除对象并归零它的引用来实现,因此我很困惑:)
  • 视图控制器的视图在视图不再可见时被卸载。您的泄漏可能与保存在系统缓存中的图像有关;见stackoverflow.com/questions/20588338/…
  • 这可能是肯定的。但是,为了更好地理解我的问题..例如在objective-c中,您可以通过执行以下操作来摆脱对象:[object release]; object = nil - 没有 Swift 等价物吗?如果它在内部处理所有这些,那就太好了,但我仍然不确定给出的答案。但是,到目前为止,您的回答很有帮助。谢谢
  • 在启用了 ARC 的 Objective-C 中,您应该无法调用 release。当对象的引用计数降至零时,对象会自动释放;要解除分配,您应该确保您没有对该对象的引用。如果一个对象仍在内存中,则意味着某个地方的某个人正在持有指向它的指针。如果您还没有,请查看 Apple 的 memory management programming guide
【解决方案2】:

如果通过更改场景意味着您正在向堆栈添加一个新视图,那么我的理解是前一个视图仍然存在,只是等待再次弹出到堆栈顶部,因此您的对象不应该是删除。如果我有这个错误,有人会纠正我。无论如何,从视图中删除所有对象的代码是:

for sv in view.subviews {
    sv.removeFromSuperview()
}

【讨论】:

  • 我添加了更多信息。根据我的测试,在我的用例中,由于仍然存在对对象的引用,因此不会清除内存,但是它自然会从显示中删除它们。我在发布之前确实尝试过这种方法,但无法将对象归零并没有帮助。谢谢
猜你喜欢
  • 2012-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-23
  • 2017-10-13
  • 2023-01-26
  • 1970-01-01
相关资源
最近更新 更多