[unowned self] 在 Swift 中:何时需要,何时危险?

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

永恒的困境:用 [unowned self] 还是不用?

\n\n

如果你曾经用 Swift 编程并遇到过闭包(closures),你肯定问过自己:“我是否需要在每个使用 self 的闭包中写 [unowned self]?”

\n\n

这是 Stack Overflow 上最热门的问题之一(数万次浏览),而且并非没有原因。在 2025 年,随着 Swift 新特性(例如改进的生命周期分析器和更严格的编译器检查)的出现,答案变得更加微妙。

\n\n

在这篇文章中,我们不会仅仅给出一个干巴巴的“是”或“否”的答案。我们将深入分析 Swift 内存管理实际上是如何工作的,何时 [unowned self] 是救星,何时是定时炸弹,以及在现代项目中应使用哪些最佳实践。

\n\n

开始吧!🚀

\n\n

关于 ARC 和 retain cycles 的简要说明(针对初学者)

\n\n

在讨论 [unowned self] 之前,让我们回顾一下自动引用计数(ARC)是如何工作的。

\n\n

ARC 是一种引用计数机制。Swift 中的每个对象(类实例)都有一个引用计数器。当你创建对对象的新引用时(例如,将其赋值给属性或放入数组),计数器会增加。当引用消失时,计数器会减少。当计数器归零时,对象被销毁。

\n\n

当两个对象相互引用时(循环引用),问题就出现了。想象一下:

\n\n
class Parent {\n    var child: Child?\n}\n\nclass Child {\n    var parent: Parent? // 对 Parent 的强引用\n}
\n\n

如果创建一对 Parent 和 Child 并设置两个引用,它们将永远相互持有。ARC 永远不会将它们的计数器归零。这被称为 retain cycle(强引用循环)。

\n\n

闭包也是引用类型。当闭包捕获 self(例如,为了调用方法)时,它会持有对对象的强引用。如果对象反过来持有对闭包的强引用(例如,作为属性),就会产生同样的循环。

\n\n

Weak vs Unowned:有什么区别?

\n\n

为了打破循环,我们使用 weak 或 unowned。

\n\n

weak self

\n
    \n
  • 始终是可选类型(self?)。
  • \n
  • 引用 不会增加 ARC 计数器。
  • \n
  • 当对象被销毁时,weak 引用会自动变为 nil。
  • \n
  • 安全——你总是检查 guard let self = self else { return }。
  • \n
\n\n

unowned self

\n
    \n
  • 非可选类型(self 不带问号)。
  • \n
  • 引用 不会增加 ARC 计数器。
  • \n
  • 当对象被销毁时,unowned 引用 不会变为 nil。它会变成“悬垂指针”(dangling pointer)。
  • \n
  • 在对象销毁后访问此类引用会导致 崩溃(EXC_BAD_ACCESS)。
  • \n
\n\n

关键点:unowned 是对编译器的承诺:“我保证在执行此闭包期间对象将保持存活”。如果你错了,应用程序就会崩溃。

\n\n

何时使用 [unowned self](以及何时不使用)

\n\n

让我们分析一下 2025 年 iOS 开发实践中的真实场景。

\n\n

情况 1:[unowned self] 是可以的(甚至更可取)

\n\n

当你 绝对确定 在闭包执行时 self 仍然存活时,请使用 [unowned self]。典型的例子是在对象方法内部同步执行的闭包,并且保证对象在闭包完成之前不会被销毁。

\n\n
class DataFetcher {\n    var data: Data?\n    \n    func fetchData() {\n        // 此闭包在方法内部同步执行\n        let processCl                

博客

书籍推荐