当 UIViewController 被释放时,调度队列会发生什么?

2023-11-27

我试图更好地理解保留周期,特别是相对于调度队列。我正在使用 AVFoundation 并在 sessionQueue 上管理 AVCaptureSession:

private let sessionQueue = DispatchQueue(label: "com.andrewferrarone.sessionQueue")

在苹果文档的很多代码示例中我看到了这一点:

self.sessionQueue.async { [unowned self]
    //
}

Is the [unowned self]自我在这里有必要吗? self(viewController)引用self.sessionQueue并将关闭发送至self.sessionQueue捕捉自我。这是一个引用循环吗? self 没有引用闭包,只是引用 DispatchQueue。如果[unowned self]是必要的,那么据我了解,我只想使用unowned self如果我确定 self 不会为零。假设我分配了一个任务sessionQueue这需要很长时间并且 viewController 在任务完成之前被弹出并被释放?会发生什么sessionQueue和任务?如果那时它仍然存在,当它尝试访问 self 时,应用程序将崩溃。另一方面,由于无主的 self 不会增加 self 的保留计数,因此它不会阻止 viewController 被释放。

所以我的问题是,当 viewController 被释放时,DispatchQueues 会发生什么?在这种情况下,如果 viewController 在 DispatchQueue 任务完成之前被释放,会发生什么?如果有人能够阐明这里发生的一切,那将非常有帮助和感激。

感谢朋友们的帮助!


Is the [unowned self]自我在这里有必要吗?

不仅是利用[unowned self]没有必要,但是在异步调度的块中这是非常危险的。您最终会得到一个指向已释放对象的悬空指针。

如果您不想保留对以下内容的强烈引用self在异步调用中,使用[weak self], 反而。你应该只使用unowned如果您知道此后永远无法调用该块self被解除分配。显然,对于异步调用,你不知道这一点,所以[unowned self]不应该在这种情况下使用。

无论你使用[weak self]或使用强引用是一个问题,即是否需要异步执行块来保留对相关对象的强引用。例如,如果您仅更新视图控制器的视图控件,则[weak self]很好(更新已被驳回的视图没有意义)。

更关键的使用weak and unowned引用是为了避免强引用循环。但这不适用于您提供的示例。如果视图控制器保留了对块本身的一些引用(例如,您有一些闭包属性)以及那些闭包引用,那么您只需要担心这些循环self,但没有weak/unowned预选赛。

我的问题是会发生什么DispatchQueue当视图控制器被释放时?

这些队列将继续存在,任何已调度的块也将继续存在,直到 (a) 所有已调度的块完成; (b) 不再有对队列的强引用。

因此,如果您异步调度块weak参考self(即视图控制器),它们将在视图控制器被释放后继续运行。这就是为什么重要的是不要使用unowned在此背景下。


就其价值而言,实证测试可能具有启发性。考虑:

class SecondViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let queue = DispatchQueue(label: "com.domain.app.SecondViewController")

        for i in 0 ..< 10 {
            queue.async { [weak self] in
                print("closure \(i) start")
                self?.performSomeTask(i)
                print("closure \(i) finish")
            }
        }
    }

    private func performSomeTask(_ value: Int) {
        print("performSomeTask starting \(value)")
        Thread.sleep(forTimeInterval: 5)        // you wouldn't generally `sleep`, but merely for diagnostic purposes
        print("performSomeTask finishing \(value)")
    }

    deinit {
        print("deinit SecondViewController")
    }

}

如果在分派的块排队并运行时关闭此视图控制器,您将看到:

  • With [weak self],视图控制器仅保留到当前分派块完成为止,然后视图控制器将被释放,其余块将快速触发,但由于[weak self], the performSomeTask视图控制器关闭后不会运行。

  • 如果你更换weak with unowned(并且显然删除了? in self?.performSomeTask(...)),如果您在排队的块有机会启动之前关闭视图控制器,您会看到它崩溃。这说明了为什么[unowned self]对于异步代码来说是如此危险。

  • 如果你简单地删除[weak self]完全并让它使用隐式强引用self,您会看到它不会释放视图控制器,直到所有排队的块完成。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当 UIViewController 被释放时,调度队列会发生什么? 的相关文章

随机推荐