Swift中的内存管理:ARC与垃圾回收器的工作原理

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

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

开始课程

为什么Swift不使用传统的垃圾回收器?

\n

如果你是从Python或Java的世界来到Swift,你可能会惊讶地发现,这门语言没有传统的垃圾回收器(Garbage Collector)。相反,Apple引入了一种称为自动引用计数(Automatic Reference Counting,ARC)的机制。这并非一时兴起:在移动设备上,每一个CPU时钟周期和每一字节内存都弥足珍贵。传统的GC在回收时会“冻结”应用程序,这对于要求动画流畅(60 FPS)的iOS来说将是一场灾难。

\n

在本文中,我们将探讨Swift中内存管理的实际工作原理,循环引用可能隐藏的陷阱,以及如何编写不会“泄漏”内存的代码。

\n\n

ARC:底层是如何工作的?

\n

ARC并非传统意义上的垃圾回收器。它在编译阶段工作:编译器在适当的位置插入retain和release指令。但有一个问题:ARC无法处理循环引用(retain cycles)。如果两个对象通过强引用相互持有,它们的引用计数永远不会降为零——内存就会泄漏。

\n\n

示例:简单的类与引用计数

\n
class Person {\n    let name: String\n    init(name: String) {\n        self.name = name\n        print("\\(name) 出现在内存中")\n    }\n    deinit {\n        print("\\(name) 从内存中消失")\n    }\n}\n\nvar person1: Person? = Person(name: "Alice") // retain count = 1\nvar person2 = person1                        // retain count = 2\nperson1 = nil                                // retain count = 1\nperson2 = nil                                // retain count = 0 → deinit\n
\n

在控制台中,你会看到:“Alice 出现在内存中”,过一会儿后:“Alice 从内存中消失”。一切正常。

\n\n

循环引用:内存的头号敌人

\n

想象一下:你有一个User和一个Account。用户拥有一个账户,而账户知道它的所有者。如果两个属性都是强引用,就会形成一个闭环:

\n\n
class User {\n    let name: String\n    var account: Account?\n    init(name: String) { self.name = name }\n    deinit { print("User \\(name) 已删除") }\n}\n\nclass Account {\n    let number: String\n    var owner: User?\n    init(number: String) { self.number = number }\n    deinit { print("Account \\(number) 已删除") }\n}\n\nvar user: User? = User(name: "Boris")\nvar account: Account? = Account(number: "12345")\nuser?.account = account\naccount?.owner = user\n\nuser = nil   // User 未被删除!account?.owner 仍然持有引用\naccount = nil // Account 未被删除!user?.account 持有引用\n// 内存泄漏!\n
\n

控制台中不会出现任何deinit消息。对象永远停留在内存中。这就是retain cycle——Swift中内存管理的经典问题。

\n\n

如何解决?Weak和Unowned

\n

解决方案是将其中一个引用设为弱引用(weak)或无主引用(unowned)。区别如下:

\n
    \n
  • weak — 始终是可选类型,当对象被删除时自动变为nil。安全。
  • \n
  • unowned — 非可选类型,假定对象的生命周期更长。如果访问已删除的对象,会导致崩溃。
  • \n
\n\n
class Account {\n    let number: String\n    weak var owner: User?  // ← 弱引用\n    init(number: String) { self.number = number }\n    deinit { print("Account \\(number) 已删除") }\n}\n// 现在 user = nil 会释放 User,而 account?.owner 会变为 nil\n
\n\n

垃圾回收器 vs ARC:哪个更快?

\n

说实话:ARC并非传统意义上的“垃圾回收器”。真正的GC(如Java、Go或Python中的)在运行时工作:它会定期扫描堆,找到不可达的对象并释放它们。这需要暂停(stop-the-world)。

\n

而ARC是一种静态

博客

书籍推荐