You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用DispatchQueue创建引用循环?兼谈引用循环认知误区

Great question—this is such a common point of confusion when working with Swift closures, dispatch queues, and memory management. Let’s break this down clearly.

First: What Actually Creates a Reference Cycle?

A reference cycle only forms when there’s a bidirectional chain of strong references between two (or more) reference-type objects, and nothing breaks that chain. In other words:

  • Object A strongly references Object B
  • Object B strongly references Object A
  • Neither side releases their strong reference automatically

Your initial misunderstanding is super relatable—using self in a closure doesn’t automatically create a cycle. It only does so if that closure is itself strongly referenced back to self (or part of a chain that loops back).

Why Your First Dispatch Queue Example Doesn’t Create a Cycle

Let’s look at your UsingQueue code:

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

Here’s the reference chain:

  • self (UsingQueue instance) strongly references queue
  • The closure you pass to asyncAfter strongly references self
  • But queue does not strongly reference the closure long-term. It only holds the closure temporarily while it’s waiting to execute, then releases it as soon as the closure finishes running.

Once you set u = nil, the only strong reference to the UsingQueue instance is the closure. When the closure runs 3 seconds later, it prints the property and then gets released—breaking the last strong reference, so deinit gets called. No cycle here!

Can You Create a Reference Cycle Only Using DispatchQueue?

Short answer: No. DispatchQueue itself doesn’t store closures long-term (beyond the time they’re waiting to execute or running). To form a cycle with a queue, you need an intermediate object that creates the bidirectional strong reference chain.

Your edited DispatchWorkItem example is a perfect illustration of this—but the cycle isn’t with the queue, it’s with the DispatchWorkItem:

class C {
    var item: DispatchWorkItem!
    var name: String = "Alpha"
    func assignItem() {
        item = DispatchWorkItem {
            print(self.name)
        }
    }
    func execute() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: item)
    }
    deinit {
        print("deinit hit!")
    }
}

The cycle here is:

  • self (C instance) strongly references item (DispatchWorkItem)
  • item’s closure strongly references self

The DispatchQueue is just executing the work item—it doesn’t hold a strong reference to it after execution. Even after the work item runs, the cycle remains because self still holds a strong reference to item, and item’s closure still holds a strong reference to self. That’s why the instance never deinitializes unless you set item = nil or use [weak self] in the closure.

Your Correct Observation: [weak self] Isn’t Just for Avoiding Cycles

You’re spot-on here—[weak self] has two key use cases:

  1. Prevent reference cycles: As we’ve covered, breaking that bidirectional strong reference chain.
  2. Control object lifecycle:
    • Use a strong reference (default, or [unowned self] if you’re sure the object will exist) when you need to guarantee the object stays alive until the closure finishes (e.g., completing a payment transaction even if the user navigates away from the view).
    • Use [weak self] when you want to exit early if the object has already been deallocated (e.g., stopping a loading spinner if the view controller was already dismissed).

Recap: Reference Cycle Creation Conditions

To sum up, a reference cycle forms if and only if:

  • You have two or more reference-type objects (classes, not structs/enums)
  • They form a closed loop of strong references (A → B → ... → A)
  • No part of that loop is broken by releasing a strong reference (e.g., setting a property to nil) or using weak/unowned to break a link in the chain.

内容的提问来源于stack exchange,提问作者mfaani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:28:47