Swift 如何使用dispatchQueues创建引用循环?
我觉得我总是误解,当引用循环被创建时。在我过去认为几乎任何地方都有一个块,编译器强迫你写Swift 如何使用dispatchQueues创建引用循环?,swift,memory-management,memory-leaks,closures,grand-central-dispatch,Swift,Memory Management,Memory Leaks,Closures,Grand Central Dispatch,我觉得我总是误解,当引用循环被创建时。在我过去认为几乎任何地方都有一个块,编译器强迫你写.self之前,这是我正在创建一个引用循环的标志,我需要在中使用[weak self] 但以下设置不会创建参考循环 import Foundation import PlaygroundSupport PlaygroundPage.current.needsIndefiniteExecution class UsingQueue { var property : Int = 5 var
.self
之前,这是我正在创建一个引用循环的标志,我需要在中使用[weak self]
但以下设置不会创建参考循环
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
在闭包示例中,设置更像:
self ----> block
self <--- block
通过下面的代码,我能够在Xcode的内存图中创建一个泄漏ie,我看到的是一个循环,而不是一条直线。我得到了紫色的指示器。我认为这种设置非常类似于存储的闭包如何产生泄漏。这与您的两个示例不同,在这两个示例中,执行从未完成。在本例中,执行已完成,但由于引用,它仍保留在内存中
我认为参考是这样的:
┌─────────┐─────────────self.item──────────────▶┌────────┐
│ self │ │workItem│
└─────────┘◀︎────item = DispatchWorkItem {...}───└────────┘
你说:
据我所知,这里的设置是:
self ---> queue
self <--- block
即使我启动上述计时器的视图控制器被解除,GCD仍会继续启动此计时器,Ticker
不会被释放。如“调试内存图”功能所示,在startTicker
例程中创建的块保持对Ticker
对象的持久强引用:
如果我在该块中使用[weak self]
作为调度队列上调度的计时器的事件处理程序,显然可以解决这个问题
其他场景包括缓慢(或不确定长度)调度的任务,您希望取消它(例如,在deinit
):
尽管如此,在绝大多数GCD用例中,[弱自我]
的选择并不是一个强引用周期,而仅仅是我们是否介意在任务完成之前对自我
的强引用是否持续
- 如果我们只是想在任务完成时更新UI,那么如果视图控制器被取消,就没有必要让视图控制器及其视图在层次结构中等待一些UI更新
- 如果我们需要在任务完成时更新数据存储,那么如果我们想确保更新发生,我们肯定不想使用
[weak self]
- 通常,分派的任务并不重要,因此不必担心
self
的寿命。例如,当请求完成时,您可能会有一个URLSession
完成处理程序dispatch UI update返回到主队列。当然,理论上我们希望[弱自我]
(因为没有理由保留已被取消的视图控制器的视图层次结构),但这又给我们的代码增加了噪音,通常没有什么实质好处
不相关,但操场是测试记忆行为的可怕场所,因为它们有自己的特质。在实际的应用程序中这样做要好得多。另外,在实际的应用程序中,您还可以使用“调试内存图”功能查看实际的强引用。请参阅。DispatchQueue
专门设计为不会导致保留循环。它包含一个控制行为的autoreleaseFrequency
属性。这很有趣。你能再补充一点细节吗?但是,在
中使用[weak self]来调度队列的目的是什么?只是为了控制流量吗?我做了一个小的编辑来详细说明我的意思,看一下。捕获self
毫无意义。我知道它不会捕获self
,但如果是,那么哪行源代码可以捕获self
?(我只是不能处理所有这些,所以我想缩小我应该处理的部分)我还将队列更改为:var queue:DispatchQueue?=DispatchQueue(标签:“mine”,qos:。后台,属性:。并发,自动释放频率:。从不,目标:nil)
但它仍然被释放。从不
是否意味着它不会自动释放任何内容?自动释放频率
与强引用周期问题无关。这就是在调度任务中创建的对象的自动释放池被耗尽的时间。读完这篇文章后,我感觉到队列的GCD就像计时器的runloop。那很有趣,非常感谢!我从未使用过DispatchSource
&DispatchWorkItem
,但您的示例足以理解。因此,虽然DispatchSource
和DispatchWorkItem
都强烈引用了self
,self
(与我的闭包示例不同)没有指向DispatchSource
或DispatchWorkItem
的指针。这仅仅是一个任务并没有完成的问题,你们可以取消它或者引用它。使用闭包,即使执行了块。它不会释放,因为它仍然关闭self
。PS我不知道如何阅读“调试内存图”,我必须研究它。它是分析强引用、识别周期等的伟大工具。请参阅WWDC 2016视频。我创建了一个新问题作为后续问题。你能看一下吗?class C{var item:DispatchWorkItem!var name:String=“蜂蜜”func assignItem(){item=DispatchWorkItem{//Oops!print(self.name)}}}func execute(){DispatchQueue.main.asyncAfter(截止日期:.now()+1,execute:item)}deinit{print(“deinit hit!”)}
通过以下代码,我能够在Xcode的
┌─────────┐─────────────self.item──────────────▶┌────────┐
│ self │ │workItem│
└─────────┘◀︎────item = DispatchWorkItem {...}───└────────┘
self ---> queue
self <--- block
class Ticker {
private var timer: DispatchSourceTimer?
func startTicker() {
let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".ticker")
timer = DispatchSource.makeTimerSource(queue: queue)
timer!.schedule(deadline: .now(), repeating: 1)
timer!.setEventHandler { // whoops; missing `[weak self]`
self.tick()
}
timer!.resume()
}
func tick() { ... }
}
class Calculator {
private var item: DispatchWorkItem!
deinit {
item?.cancel()
item = nil
}
func startCalculation() {
let queue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".calcs")
item = DispatchWorkItem { // whoops; missing `[weak self]`
while true {
if self.item?.isCancelled ?? true { break }
self.calculateNextDataPoint()
}
self.item = nil
}
queue.async(execute: item)
}
func calculateNextDataPoint() {
// some intense calculation here
}
}