当UIViewController被取消分配时,Dispatch Queues会发生什么?

时间:2017-03-01 21:07:28

标签: 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(viewController)引用self.sessionQueue,调度到self.sessionQueue的闭包捕获self。这是一个参考周期吗? self不引用闭包,只是DispatchQueue。如果[unowned self]是必要的,那么根据我的理解,如果我确定自我不会为零,我只想使用unowned self。那么我可以说我在sessionQueue上放了一个需要很长时间的任务,并且viewController被弹出并在任务完成之前被解除分配? sessionQueue和任务会发生什么?如果它仍然在那时,当它试图访问自己时,应用程序将崩溃。另一方面,由于无主的自我不会增加self的保留计数,因此它不会阻止viewController被释放。

所以我的问题是当一个viewController被解除分配时DispatchQueues会发生什么,以及在这种情况下会发生什么,如果在dispatchQueue任务完成之前取消分配了一个viewController?如果有人能够对这里发生的事情有所了解,那将非常有帮助和赞赏。

感谢帮助我的朋友们!

1 个答案:

答案 0 :(得分:15)

  

这里有[unowned self]个自我吗?

不仅没有必要使用[unowned self],而且在异步调度的块中它非常危险。你最终得到一个指向解除分配对象的悬空指针。

如果您不想在异步调用中保持对self的强引用,请改用[weak self]。如果您知道在unowned被取消分配后永远不能调用块,则应该只使用self。显然,使用异步调用时,您不知道这一点,因此不应在该上下文中使用[unowned self]

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

weakunowned引用的更为关键的用法是避免强引用周期。但这并不适用于您提供的示例。如果视图控制器保留对块本身的一些引用(例如,您有一些闭包属性),并且那些闭包引用self,但没有weak / {{1},您只需要担心这些周期。 } qualifier。

  

我的问题是当视图控制器被解除分配时unowned会发生什么?

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

因此,如果异步调度具有DispatchQueue 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") } } ,视图控制器仅在当前调度块完成之前保留,然后视图控制器将被释放,其余块将快速启动,但由于{{ 1}},[weak self]在视图控制器关闭后不会运行。

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

  • 如果您只是简单地删除self?.performSomeTask(...)并让它使用对[unowned self]的隐式强引用,您将看到在所有排队的块完成之前它不会释放视图控制器。 / p>