我正在尝试更好地理解保留周期,尤其是与 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 )。
weak
和unowned
引用更重要的用途是避免强引用循环。但这不适用于您提供的示例。如果 View Controller 保留对 block 本身的一些引用(例如,你有一些闭包属性)并且这些闭包引用 self
,但没有 weak
,你只需要担心这些循环>/unowned
限定词。
My question is what happens to
DispatchQueue
s when a view controller is deallocated?
这些队列将继续存在,任何已分派(dispatch)的 block 也将继续存在,直到 (a) 所有已分派(dispatch)的 block 完成; (b) 不再有对队列的强引用。
因此,如果您使用 weak
对 self
的引用异步分派(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/