永恒的困境:用 [unowned self] 还是不用?
\n\n如果你曾经用 Swift 编程并遇到过闭包(closures),你肯定问过自己:“我是否需要在每个使用 self 的闭包中写 [unowned self]?”
这是 Stack Overflow 上最热门的问题之一(数万次浏览),而且并非没有原因。在 2025 年,随着 Swift 新特性(例如改进的生命周期分析器和更严格的编译器检查)的出现,答案变得更加微妙。
\n\n在这篇文章中,我们不会仅仅给出一个干巴巴的“是”或“否”的答案。我们将深入分析 Swift 内存管理实际上是如何工作的,何时 [unowned self] 是救星,何时是定时炸弹,以及在现代项目中应使用哪些最佳实践。
开始吧!🚀
\n\n关于 ARC 和 retain cycles 的简要说明(针对初学者)
\n\n在讨论 [unowned self] 之前,让我们回顾一下自动引用计数(ARC)是如何工作的。
ARC 是一种引用计数机制。Swift 中的每个对象(类实例)都有一个引用计数器。当你创建对对象的新引用时(例如,将其赋值给属性或放入数组),计数器会增加。当引用消失时,计数器会减少。当计数器归零时,对象被销毁。
\n\n当两个对象相互引用时(循环引用),问题就出现了。想象一下:
\n\nclass Parent {\n var child: Child?\n}\n\nclass Child {\n var parent: Parent? // 对 Parent 的强引用\n}\n\n如果创建一对 Parent 和 Child 并设置两个引用,它们将永远相互持有。ARC 永远不会将它们的计数器归零。这被称为 retain cycle(强引用循环)。
闭包也是引用类型。当闭包捕获 self(例如,为了调用方法)时,它会持有对对象的强引用。如果对象反过来持有对闭包的强引用(例如,作为属性),就会产生同样的循环。
Weak vs Unowned:有什么区别?
\n\n为了打破循环,我们使用 weak 或 unowned。
weak self
\n- \n
- 始终是可选类型(
self?)。 \n - 引用 不会增加 ARC 计数器。 \n
- 当对象被销毁时,
weak引用会自动变为nil。 \n - 安全——你总是检查
guard let self = self else { return }。 \n
unowned self
\n- \n
- 非可选类型(
self不带问号)。 \n - 引用 不会增加 ARC 计数器。 \n
- 当对象被销毁时,
unowned引用 不会变为 nil。它会变成“悬垂指针”(dangling pointer)。 \n - 在对象销毁后访问此类引用会导致 崩溃(EXC_BAD_ACCESS)。 \n
关键点:unowned 是对编译器的承诺:“我保证在执行此闭包期间对象将保持存活”。如果你错了,应用程序就会崩溃。
何时使用 [unowned self](以及何时不使用)
\n\n让我们分析一下 2025 年 iOS 开发实践中的真实场景。
\n\n情况 1:[unowned self] 是可以的(甚至更可取)
\n\n当你 绝对确定 在闭包执行时 self 仍然存活时,请使用 [unowned self]。典型的例子是在对象方法内部同步执行的闭包,并且保证对象在闭包完成之前不会被销毁。
class DataFetcher {\n var data: Data?\n \n func fetchData() {\n // 此闭包在方法内部同步执行\n let processCl