Is the [unowned self]
自我在这里有必要吗?
不仅是利用[unowned self]
没有必要,但是在异步调度的块中这是非常危险的。您最终会得到一个指向已释放对象的悬空指针。
如果您不想保留对以下内容的强烈引用self
在异步调用中,使用[weak self]
, 反而。你应该只使用unowned
如果您知道此后永远无法调用该块self
被解除分配。显然,对于异步调用,你不知道这一点,所以[unowned self]
不应该在这种情况下使用。
无论你使用[weak self]
或使用强引用是一个问题,即是否需要异步执行块来保留对相关对象的强引用。例如,如果您仅更新视图控制器的视图控件,则[weak self]
很好(更新已被驳回的视图没有意义)。
更关键的使用weak
and unowned
引用是为了避免强引用循环。但这不适用于您提供的示例。如果视图控制器保留了对块本身的一些引用(例如,您有一些闭包属性)以及那些闭包引用,那么您只需要担心这些循环self
,但没有weak
/unowned
预选赛。
我的问题是会发生什么DispatchQueue
当视图控制器被释放时?
这些队列将继续存在,任何已调度的块也将继续存在,直到 (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")
}
}
如果在分派的块排队并运行时关闭此视图控制器,您将看到:
With [weak self]
,视图控制器仅保留到当前分派块完成为止,然后视图控制器将被释放,其余块将快速触发,但由于[weak self]
, the performSomeTask
视图控制器关闭后不会运行。
如果你更换weak
with unowned
(并且显然删除了?
in self?.performSomeTask(...)
),如果您在排队的块有机会启动之前关闭视图控制器,您会看到它崩溃。这说明了为什么[unowned self]
对于异步代码来说是如此危险。
如果你简单地删除[weak self]
完全并让它使用隐式强引用self
,您会看到它不会释放视图控制器,直到所有排队的块完成。