【发布时间】:2019-09-27 09:19:37
【问题描述】:
我觉得在创建引用循环时我总是误解了这一点。在我认为几乎任何你有一个块并且编译器强迫你写.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,否则它不会释放。
编辑:
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 的内存图中我看到一个循环,而不是一条直线。我得到紫色指标。我认为这种设置非常类似于存储的闭包如何产生泄漏。这与您的两个示例不同,其中执行从未完成。在此示例中,执行已完成,但由于引用,它仍保留在内存中。
我认为参考是这样的:
┌─────────┐─────────────self.item──────────────▶┌────────┐
│ self │ │workItem│
└─────────┘◀︎────item = DispatchWorkItem {...}───└────────┘
【问题讨论】:
-
DispatchQueue专门设计为不会导致保留周期。它包含一个控制行为的autoreleaseFrequency属性。 -
知道这很有趣。你能在上面添加更多细节吗?但是,使用
[weak self] in进行 dispatchQueues 的目的是什么?只是为了控制流量吗?我做了一个小编辑来详细说明我的意思 -
看看source code。根本没有捕获
self的目的。 -
我知道它不会捕获
self,但如果是,那么源代码的哪一行可以捕获self? (我只是无法处理所有这些,所以我想缩小我应该处理的部分)另外我将队列更改为:var queue : DispatchQueue? = DispatchQueue(label: "mine", qos: .background, attributes: .concurrent, autoreleaseFrequency: .never, target: nil)但它仍然被释放。never不是意味着它不会自动释放任何东西吗? -
autoreleaseFrequency与强引用周期问题无关。那是关于在分派任务中创建的对象的自动释放池耗尽的时间。
标签: swift memory-management memory-leaks closures grand-central-dispatch