7-5.【高级特性】引用类型的循环引用是如何形成的?如何利用 weak/unowned 打破?

1 阅读2分钟

一、循环引用是如何形成的?

1️⃣ ARC 的工作前提

  • ARC 只做一件事:统计强引用数量
  • 引用计数 > 0 → 对象存活
  • 引用计数 = 0 → 对象释放

ARC 不会分析引用关系图,也不会自动打破环


2️⃣ 最简单的循环引用

class A {
    var b: B?
}

class B {
    var a: A?
}

let a = A()
let b = B()
a.b = b
b.a = a

引用关系:

A ──strong──▶ B
▲             │
└──strong─────┘
  • ab 作用域结束
  • 但 A、B 仍然互相强引用
  • 引用计数永远 ≠ 0
  • 👉 内存泄漏

二、为什么 ARC 无法处理循环引用?

因为 ARC 的模型是:

  • 局部的
  • 基于计数的
  • 不做全局图分析

相比之下:

  • GC(垃圾回收)会扫描整张对象图
  • ARC 不会,也不能(性能 & 确定性)

三、如何用 weak / unowned 打破循环?

1️⃣ weak:最安全、最常用

class B {
    weak var a: A?
}

特点

  • 不增加引用计数
  • 指向对象释放后 → 自动置 nil
  • 必须是 optional

📌 使用场景:

  • delegate
  • 父 → 子 / 子 → 父中的“反向引用”
  • 生命周期不完全确定

2️⃣ unowned:性能更好,但有风险

class Child {
    unowned var parent: Parent
}

特点

  • 不增加引用计数
  • 不会置 nil
  • 对象释放后再访问 → 直接崩溃

📌 使用前提:

你 100% 确定被引用对象的生命周期 ≥ 自己

常见场景:

  • child 的生命周期严格受 parent 控制
  • 闭包捕获 self,且 self 生命周期必然更长

四、weak vs unowned 对比表(面试常问)

维度weakunowned
是否增加引用计数
是否可为 nil
对象释放后自动置 nil野指针 → crash
是否 optional必须不需要
性能略慢略快
安全性

五、闭包中的循环引用(高频翻车点)

1️⃣ 闭包为什么容易造成循环?

class ViewModel {
    var callback: (() -> Void)?

    func setup() {
        callback = {
            self.doSomething()
        }
    }
}

引用链:

ViewModel
   │
   ▼
callback (closure)
   │
   ▼
self (ViewModel)

👉 闭环完成,泄漏成立。


2️⃣ 用捕获列表打破

✅ weak self(推荐)

callback = { [weak self] in
    self?.doSomething()
}
  • 不保证 self 存在
  • 安全

✅ unowned self(需谨慎)

callback = { [unowned self] in
    doSomething()
}
  • 性能更好
  • self 被释放后调用 → crash

六、什么时候用 weak?什么时候用 unowned?

一个实用判断法:

“这个引用在对象释放后,还可能被访问吗?”

  • 可能weak
  • 绝不可能unowned

面试官爱听的版本:

delegate / callback / async → weak
parent-child 强约束 → unowned(谨慎)


七、真实项目里的经典场景

1️⃣ delegate 模式

weak var delegate: UITableViewDelegate?

2️⃣ Timer

Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
    self?.tick()
}

3️⃣ Combine / async Task

Task { [weak self] in
    await self?.load()
}

八、一句话终极总结(建议背)

循环引用是对象之间相互强引用形成的闭环,ARC 无法自动回收;
使用 weak 或 unowned 打破强引用链,其中 weak 安全、unowned 高效但危险。