如何使用dispatchQueues创建一个引用循环?

12 浏览
0 Comments

如何使用dispatchQueues创建一个引用循环?

我觉得我一直对于创建引用循环的时机存在误解。以前我认为几乎在任何你有一个块且编译器强制你写.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("入队")

queue?.asyncAfter(deadline: .now() + 3) {

print(self.property)

}

}

deinit {

print("UsingQueue 被释放")

}

}

var u : UsingQueue? = UsingQueue()

u?.enqueue3()

u = nil

这个块只会在3秒钟内保留self,然后释放它。如果我使用async而不是asyncAfter,那么几乎是立即释放的。

据我所了解,这里的设置是:

self ---> queue
self <--- block

队列只是块的外壳/封装。这就是为什么即使我将队列设为nil,块也会继续执行。它们是独立的。

那么有没有仅使用队列就会创建引用循环的设置呢?

据我所了解,[weak self]只应该用于除了引用循环之外的其他原因,比如控制块的流程。例如:

您想要保留对象并运行您的块,然后释放它吗?一个真实的情况是即使视图已从屏幕上移除,也要完成此事务...

或者您希望使用[weak self] in,以便在对象被释放时可提前退出。例如,一些纯粹的 UI 操作,如不再需要停止加载指示器。


就我所知,如果我使用闭包,情况就会不同,例如:

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 被释放")

}

}

var cc : UsingClosure? = UsingClosure()

cc?.closing()

cc?.execute()

cc?.release() // 要么调用此方法,要么在闭包中使用[weak self],否则会有一个引用循环

cc = nil

在闭包示例中,设置更像是:

self ----> block
self <--- block

因此它是一个引用循环,除非我将 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 {...}───└────────┘

enter image description here

0
0 Comments

如何使用dispatchQueues创建引用循环?

在上述内容中,提到了使用dispatchQueues创建引用循环的原因以及解决方法。创建引用循环的原因是因为GCD会保持对所有已排队任务的dispatch queues的引用。而解决引用循环的方法则是使用[weak self]来避免强引用。

首先,提到了一种常见的引用循环情况,即在重复定时器中使用了未标记为[weak self]的block。即使视图控制器被解除引用,GCD仍然会继续执行定时器。为了解决这个问题,可以在block中使用[weak self]来避免对Ticker对象的持久强引用。

其次,还提到了另一种情况,即在长时间或无限长度的dispatched task中使用了未标记为[weak self]的block。这种情况下,即使调用了deinit方法,Calculator对象仍然无法释放。为了解决这个问题,可以在block中使用[weak self]来避免对Calculator对象的持久强引用。

此外,还强调了在大多数GCD使用场景中,选择使用[weak self]并不是为了解决引用循环的问题,而是根据实际需求决定是否需要在任务完成后保持对self的强引用。

最后,提到了使用Xcode的“Debug Memory Graph”工具来分析强引用、识别循环等。同时还有关于Xcode 12/13版本的问题,建议在Stack Overflow上提问以获取更详细的解答。

使用dispatchQueues创建引用循环的原因是GCD会保持对已排队任务的引用,解决方法是使用[weak self]来避免强引用。在大多数情况下,选择使用[weak self]取决于任务完成后是否需要保持对self的强引用。使用Xcode的“Debug Memory Graph”工具可以帮助分析和解决引用循环问题。

0