当 UIViewController 被释放时调度队列会发生什么?
What happens to Dispatch Queues when UIViewController is Deallocated?
我试图更好地理解保留周期,尤其是与调度队列相关的。我正在使用 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 任务完成之前被释放会发生什么?如果有人能阐明这里发生的一切,那将非常有帮助和感激。
感谢朋友们的帮助!
Is the [unowned self]
self here necessary?
不仅没有必要使用 [unowned self]
,而且在异步调度块中它非常危险。您最终得到一个指向已释放对象的悬空指针。
如果您不想在异步调用中保留对 self
的强引用,请改用 [weak self]
。如果您知道在 self
被释放后永远无法调用块,您应该只使用 unowned
。显然,对于异步调用,您不知道这一点,因此不应在该上下文中使用 [unowned self]
。
使用[weak self]
还是使用强引用是一个问题,就是你是否需要异步执行的块来保持对相关对象的强引用。例如,如果您只更新视图控制器的视图控件,那么 [weak self]
就可以了(没有必要更新已关闭的视图)。
weak
和unowned
引用更关键的用途是避免强引用循环。但这不适用于您提供的示例。如果视图控制器保留对块本身的一些引用(例如,你有一些闭包 属性)并且那些闭包引用 self
,但没有 weak
/,你只需要担心这些循环unowned
预选赛。
My question is what happens to DispatchQueue
s when a view controller is deallocated?
这些队列将继续存在,任何已分派的块也将继续存在,直到 (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")
}
}
如果在分派的块排队和 运行ning 时关闭此视图控制器,您将看到:
使用[weak self]
,视图控制器只保留到当前调度块完成,然后视图控制器将被释放,其余块将快速触发,但是由于 [weak self]
,performSomeTask
不会在视图控制器关闭后 运行。
如果将 weak
替换为 unowned
(并且显然删除了 self?.performSomeTask(...)
中的 ?
),您会看到它崩溃在排队的块有机会启动之前关闭视图控制器。这说明了为什么 [unowned self]
对于异步代码如此危险。
如果你简单地完全删除 [weak self]
并让它使用对 self
的隐式强引用,你会看到它不会释放视图控制器,直到所有排队块完成。
我试图更好地理解保留周期,尤其是与调度队列相关的。我正在使用 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 任务完成之前被释放会发生什么?如果有人能阐明这里发生的一切,那将非常有帮助和感激。
感谢朋友们的帮助!
Is the
[unowned self]
self here necessary?
不仅没有必要使用 [unowned self]
,而且在异步调度块中它非常危险。您最终得到一个指向已释放对象的悬空指针。
如果您不想在异步调用中保留对 self
的强引用,请改用 [weak self]
。如果您知道在 self
被释放后永远无法调用块,您应该只使用 unowned
。显然,对于异步调用,您不知道这一点,因此不应在该上下文中使用 [unowned self]
。
使用[weak self]
还是使用强引用是一个问题,就是你是否需要异步执行的块来保持对相关对象的强引用。例如,如果您只更新视图控制器的视图控件,那么 [weak self]
就可以了(没有必要更新已关闭的视图)。
weak
和unowned
引用更关键的用途是避免强引用循环。但这不适用于您提供的示例。如果视图控制器保留对块本身的一些引用(例如,你有一些闭包 属性)并且那些闭包引用 self
,但没有 weak
/,你只需要担心这些循环unowned
预选赛。
My question is what happens to
DispatchQueue
s when a view controller is deallocated?
这些队列将继续存在,任何已分派的块也将继续存在,直到 (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")
}
}
如果在分派的块排队和 运行ning 时关闭此视图控制器,您将看到:
使用
[weak self]
,视图控制器只保留到当前调度块完成,然后视图控制器将被释放,其余块将快速触发,但是由于[weak self]
,performSomeTask
不会在视图控制器关闭后 运行。如果将
weak
替换为unowned
(并且显然删除了self?.performSomeTask(...)
中的?
),您会看到它崩溃在排队的块有机会启动之前关闭视图控制器。这说明了为什么[unowned self]
对于异步代码如此危险。如果你简单地完全删除
[weak self]
并让它使用对self
的隐式强引用,你会看到它不会释放视图控制器,直到所有排队块完成。