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

标签 ios grand-central-dispatch retain-cycle

我正在尝试更好地理解保留周期,尤其是与 Dispatch Queues 相关的部分。我正在使用 AVFoundation 并在 sessionQueue 上管理 AVCaptureSession:

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

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

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

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

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

感谢 friend 们的帮助!

最佳答案

Is the [unowned self] self here necessary?

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

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

你是使用[weak self]还是使用强引用是一个问题,就是你是否需要异步执行的 block 来保持对相关对象的强引用。例如,如果您只更新 View Controller 的 View 控件,那么 [weak self] 就可以了(没有必要更新已被关闭的 View )。

weakunowned 引用更重要的用途是避免强引用循环。但这不适用于您提供的示例。如果 View Controller 保留对 block 本身的一些引用(例如,你有一些闭包属性)并且这些闭包引用 self,但没有 weak,你只需要担心这些循环>/unowned 限定词。

My question is what happens to DispatchQueues when a view controller is deallocated?

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

因此,如果您使用 weakself 的引用异步分派(dispatch) block (即 View Controller ),它们将在 View Controller 被释放后继续运行。这就是为什么在这种情况下不使用 unowned 很重要。


就其值(value)而言,实证检验可能很有启发性。考虑:

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")
    }

}

如果在分派(dispatch)的 block 排队并运行时关闭此 View Controller ,您将看到:

  • 使用[weak self], View Controller 只保留到当前分派(dispatch) block 完成,然后 View Controller 将被释放,其余 block 将快速触发,但是由于 [weak self]performSomeTask 不会在 View Controller 被关闭后运行。

  • 如果您将 weak 替换为 unowned(显然删除了 self?.performSomeTask(.. .)),如果在排队的 block 有机会启动之前关闭 View Controller ,您将看到它崩溃。这说明了为什么 [unowned self] 对于异步代码如此危险。

  • 如果您简单地完全删除 [weak self] 并让它使用对 self 的隐式强引用,您会发现它不会释放查看 Controller ,直到所有排队的 block 完成。

关于ios - 当 UIViewController 被释放时调度队列会发生什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42542071/

相关文章:

ios - 回调CFNetwork线程

swift - 为什么捕获列表中的捕获说明符是可选的?

ios - 为什么 UINavigationController 和 UIViewControllers 之间没有保留循环

ios - UIImageView 不改变 UIImage

ios - GCD调试锁定 key 的串行队列值的描述?

iphone - webview 中阅读页面的自定义 Split View

swift - 复杂流程的大中央调度?

iOS block 。如何从 block setter 中引用对象实例?

iphone - Phonegap 应用程序 : External URL don't open in InApp Browser of IOS

iOS 11 heic 格式为蒙版图像添加伪像