【问题标题】:What happens to Dispatch Queues when UIViewController is Deallocated?当 UIViewController 被释放时调度队列会发生什么?
【发布时间】:2017-03-01 21:07:27
【问题描述】:

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

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

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

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

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

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

感谢朋友们的帮助!

【问题讨论】:

    标签: ios grand-central-dispatch retain-cycle


    【解决方案1】:

    这里的[unowned self] self 有必要吗?

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

    如果您不想在异步调用中保持对self 的强引用,请改用[weak self]。如果您知道在 self 被释放后永远无法调用该块,则应仅使用 unowned。显然,对于异步调用,您不知道这一点,因此 [unowned self] 不应在该上下文中使用。

    你是使用[weak self]还是使用强引用是你是否需要异步执行的块来保持对相关对象的强引用的问题。例如,如果您只更新视图控制器的视图控件,那么[weak self] 就可以了(更新已关闭的视图没有意义)。

    weakunowned 引用的更关键用途是避免强引用循环。但这不适用于您提供的示例。如果视图控制器保留对块本身的一些引用(例如,您有一些闭包属性)并且这些闭包引用self,但没有weak/unowned 限定符,则您只需要担心这些循环。

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

    这些队列将继续存在,任何已调度的块也将继续存在,直到 (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")
        }
    
    }
    

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

    • 使用[weak self],视图控制器仅保留到当前调度的块完成,然后视图控制器将被释放,其余块将迅速触发,但由于[weak self]performSomeTask 在视图控制器关闭后不会运行。

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

    • 1234563 /li>

    【讨论】:

    • 非常感谢我的朋友。苹果在他们的示例代码中使用[unowned self] 很奇怪,但现在这很有意义。
    • 没问题。 [unowned self] 肯定有作用,但调用async 时很危险。我很惊讶 Apple 做到了,因为他们通常很擅长正确使用 weakunowned。如果您仍然碰巧有参考资料,请随时与我们分享。
    • link 那是代码的链接。在 CameraViewController.swift 中的整个 AVCam 代码中都使用了它。我的一个朋友警告过我有关 Apple 的示例代码,告诉我它是由实习生编写的,不可信,但我不知道这是不是真的。
    • @Rob 解释得很好。非常感谢您的时间和精力。
    猜你喜欢
    • 2011-08-25
    • 2016-01-15
    • 1970-01-01
    • 1970-01-01
    • 2011-05-20
    • 2013-03-11
    • 2011-11-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多