我觉得我一直误解了创建引用循环的时间。在我以前认为几乎任何有块并且编译器都会强迫你编写的地方.self
那么这是我正在创建引用循环并且需要使用的标志[weak self] in
.
但以下设置不会创建引用循环。
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution
class UsingQueue {
var property : Int = 5
var queue : DispatchQueue? = DispatchQueue(label: "myQueue")
func enqueue3() {
print("enqueued")
queue?.asyncAfter(deadline: .now() + 3) {
print(self.property)
}
}
deinit {
print("UsingQueue deinited")
}
}
var u : UsingQueue? = UsingQueue()
u?.enqueue3()
u = nil
该块仅保留self
3 秒。然后释放它。如果我使用async
代替asyncAfter
然后几乎是立即的。
据我了解,这里的设置是:
self ---> queue
self <--- block
队列只是块的外壳/包装器。这就是为什么即使我nil
队列中,块将继续执行。他们是独立的。
那么有没有什么设置只使用队列并创建引用循环呢?
据我了解[weak self]
仅用于参考循环以外的原因,即控制流量块的。例如
您想保留该对象并运行您的块然后释放它吗?真实的情况是即使视图已从屏幕上删除,也要完成此事务......
或者你想使用[weak self] in
这样,如果您的对象已被释放,您可以提前退出。例如不再需要一些纯粹的 UI,例如停止加载旋转器
FWIW 我明白,如果我使用闭包,那么事情会有所不同,即如果我这样做:
import PlaygroundSupport
import Foundation
PlaygroundPage.current.needsIndefiniteExecution
class UsingClosure {
var property : Int = 5
var closure : (() -> Void)?
func closing() {
closure = {
print(self.property)
}
}
func execute() {
closure!()
}
func release() {
closure = nil
}
deinit {
print("UsingClosure deinited")
}
}
var cc : UsingClosure? = UsingClosure()
cc?.closing()
cc?.execute()
cc?.release() // Either this needs to be called or I need to use [weak self] for the closure otherwise there is a reference cycle
cc = nil
在闭包示例中,设置更像是:
self ----> block
self <--- block
因此,它是一个引用循环,除非我将块设置为捕获,否则不会取消分配nil
.
EDIT:
class C {
var item: DispatchWorkItem!
var name: String = "Alpha"
func assignItem() {
item = DispatchWorkItem { // Oops!
print(self.name)
}
}
func execute() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: item)
}
deinit {
print("deinit hit!")
}
}
使用以下代码,我能够创建泄漏,即在 Xcode 的内存图中我看到一个循环,而不是一条直线。我得到紫色指示器。我认为这个设置非常类似于存储的闭包如何造成泄漏。这与your两个例子,其中执行是从未完成。在这个例子中执行是finished,但由于引用,它仍然保留在内存中。
我认为参考是这样的:
┌─────────┐─────────────self.item──────────────▶┌────────┐
│ self │ │workItem│
└─────────┘◀︎────item = DispatchWorkItem {...}───└────────┘