【问题标题】:View controller with strong references causing memory leaks具有强引用的视图控制器导致内存泄漏
【发布时间】:2016-12-13 17:59:56
【问题描述】:

我的一个视图控制器中存在强引用问题,导致内存泄漏。首先,我的设置:

2 个视图控制器(v1 和 v2)。 v1 连接到 v2,v2 有一个关闭按钮,可以将自身弹回 v1。 v2 包含尝试无限重新连接直到建立连接的代码。 (使用 red5pro 的视频流)。代码如下:

func reconnect(){
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(5.0 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { () -> Void in
            self.connectToStream()
        }
}

在我的情况下需要连续重新连接,但是当用户退出 v2 时,我希望停止重新连接。但目前,即使用户离开 v2,重新连接也会无限进行。

我开始知道这是因为 v2 具有强大的引用,并且即使在用户退出它之后也会继续存在。所以这导致无限调用 reconnect() 方法的代码继续运行。我将尝试清理 v2 以将所有内容转换为弱引用,但我也在考虑一些替代方案,对此我有几个问题:

  1. 有没有办法终止 viewDidDisappear 上的重新连接,所以即使我的视图控制器没有被破坏,至少我的重新连接过程停止了?

  2. 从v2退出回到v1后,如果用户再次切换到v2,是否可以分配相同的v2实例,而不是每次都创建一个新的实例?

【问题讨论】:

  • "是否可以分配相同的 v1 实例而不是每次都创建一个新实例?" ...我假设您的意思是 v2,而不是 v1?
  • 假设您真的在谈论 v2,而不是 v1:通常当您弹出视图控制器时,您会释放资源给它,并在需要重新呈现它时重新创建它。有一些方法可以保留它,但它很笨拙,并且有过早优化和/或一些更深层次的设计缺陷的味道。视图控制器本身的创建成本相对较低。为什么即使你已经解雇了它,你仍然觉得有必要保留它?
  • @Rob 是的,我的错,更新了问题。
  • @Rob 其实我不想保留它。但由于我的强引用,它没有被破坏。虽然我将尝试删除强引用,但如果由于某种原因我无法删除,我想知道如果用户从 v1 回到 v2 是否有一个备份计划,这样我就不会得到多个活着的结果v2s。
  • 您应该只解决保留 v2 的问题,而不是扭曲自己来弄清楚如何重用它。 Xcode 8 具有“调试内存图”,它将准确地向您展示什么是保持对 v2 的强引用。这可能是某种形式的强引用循环,或者您可能有一些循环故事板引用。 (例如,你肯定是从 v2 跳回 v1,还是从 v2 到 v1 有一个转场?)但是内存图应该可以很容易地找到问题。

标签: ios swift swift2 red5 strong-references


【解决方案1】:

dispatch_after 无法取消,但有几个选项:

  1. 使用弱引用,这将允许 self 被释放:

    func reconnect() {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(5.0 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { [weak self] in
            self?.connectToStream()
        }
    }
    

    这确实让计时器继续运行,但阻止它保留视图控制器,因此视图控制器被释放,connectToStream 将不会被调用。

  2. 使用NSTimer,当视图消失时取消:

    weak var timer: NSTimer?
    
    func reconnect() {
        timer?.invalidate()
        timer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: #selector(handleTimer(_:)), userInfo: nil, repeats: false)
    }
    
    func handleTimer(timer: NSTimer) {
        self.connectToStream()
    }
    
    override func viewDidDisappear() {
        super.viewDidDisappear()
        timer?.invalidate()
    }
    

    注意,因为这个基于selectorNSTimer 保持对其target 的强引用,所以您不能在deinit 中取消(因为存在强引用循环)。所以你必须找到一些其他合适的事件来解决这个问题(例如viewDidDisappear)。

【讨论】:

  • 会试一试。如果我能够在不解决视图控件引用周期问题的情况下解决我的问题,是否可以让视图控制器挂起?还是总是处理视图控制器的目标?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-15
  • 2011-02-18
  • 1970-01-01
相关资源
最近更新 更多