基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

ARC (自动引用计数) 的工作原理

知识点图片

ARC (Automatic Reference Counting,自动引用计数) 是 Apple 在 iOS 和 macOS 开发中(主要用于 Swift 和 Objective-C)使用的一种内存管理机制。

它的核心目标非常简单:确保当一个对象不再被需要时,它的内存会被自动释放;而当它还在被使用时,绝不会被释放。

以下是 ARC 工作原理的通俗详解:


1. 核心概念:引用计数器

你可以把每个对象想象成一个带有计数器的房间

  • 有人进入房间 (引用对象):计数器 +1。
  • 有人离开房间 (放弃引用):计数器 -1。
  • 房间空了 (计数器为 0):关灯,锁门,拆除房间 (释放内存)。

在代码层面:

  • 每当你创建一个新对象,或者将一个对象赋值给一个变量、常量或属性时,引用计数会 +1
  • 每当这些变量离开作用域、被赋值为 nil 或指向其他对象时,引用计数会 -1
  • 一旦引用计数变为 0,系统会立即销毁该对象(调用 deinitdealloc),释放内存。

2. ARC 是如何“自动”工作的?

ARC 与 Java 或 C# 中的“垃圾回收 (Garbage Collection, GC)”不同。

  • GC (垃圾回收):是在程序运行时,有一个后台进程定期扫描内存,查找不再使用的对象并清除。这可能会导致程序偶尔卡顿。
  • ARC (自动引用计数):是在编译时工作的。

当你写代码时,编译器会分析你的代码,自动在合适的地方插入 retain (计数+1) 和 release (计数-1) 的代码。

例子:

plaintext
class Person {
    let name: String
    init(name: String) { self.name = name }
    deinit { print("\(name) 被销毁了") }
}

// 1. 创建对象
var reference1: Person? = Person(name: "John") 
// 此时 "John" 对象的引用计数 = 1

// 2. 赋值给另一个变量
var reference2 = reference1 
// 此时 "John" 对象的引用计数 = 2 (因为 reference2 也指向它)

// 3. 断开第一个引用
reference1 = nil 
// 此时 "John" 对象的引用计数 = 1 (reference2 还在用)

// 4. 断开第二个引用
reference2 = nil 
// 此时 "John" 对象的引用计数 = 0 -> 内存立即释放,打印 "John 被销毁了"

3. 强引用与弱引用 (关键规则)

为了防止内存泄漏,ARC 引入了三种引用类型:

A. 强引用 (Strong Reference) - 默认

  • 关键字strong (Objective-C), Swift 中默认就是强引用。
  • 作用:只要强引用存在,对象就绝不会被销毁。它会使引用计数 +1
  • 比喻:你牵着一只狗的绳子,只要绳子在你手里,狗就跑不掉。

B. 弱引用 (Weak Reference)

  • 关键字weak
  • 作用:它指向一个对象,但不拥有它。它不会增加引用计数。
  • 特性:如果对象被销毁了,弱引用会自动变成 nil。因此,weak 变量必须是可选类型 (Optional)。
  • 比喻:你看着那只狗,但没牵绳子。如果牵绳子的人(强引用)放手了,狗跑了,你也就看不到了(变成 nil)。

C. 无主引用 (Unowned Reference)

  • 关键字unowned
  • 作用:和 weak 类似,不增加引用计数。
  • 区别:它假设对象永远存在。如果对象被销毁了,你还去访问 unowned 引用,程序会崩溃。通常用于“对象A存在时,对象B一定存在”的关系中。

4. 最大的坑:循环引用 (Retain Cycle)

ARC 虽然自动化了,但它处理不好一种情况:两个对象互相强引用

场景:

  • 对象 A 有一个属性指向对象 B (强引用)。
  • 对象 B 有一个属性指向对象 A (强引用)。

结果:
当你把外部变量设为 nil 时,A 的计数器是 1 (因为 B 指向它),B 的计数器也是 1 (因为 A 指向它)。它们互相“抓着”对方,谁的计数器都变不成 0,导致内存泄漏(内存永远无法释放)。

解决方案:
将其中一端的引用改为 weakunowned。通常是“子对象”对“父对象”的引用设为 weak(例如 Delegate 模式或闭包)。

5. 总结:ARC vs 垃圾回收 (GC)

特性 ARC (iOS/Swift) Garbage Collection (Java/Android)
机制 引用计数 根对象可达性分析
时机 编译时插入代码,运行时实时释放 运行时不定时扫描
性能 平滑,无卡顿,内存占用较低 可能出现 "Stop the World" (卡顿),内存占用较高
确定性 对象何时销毁是确定的 对象何时销毁是不确定的
缺点 无法自动处理循环引用,需开发者注意 可以处理循环引用

一句话总结:
ARC 就是编译器帮你写了内存管理代码,只要你遵循“谁拥有谁”的规则(合理使用 strongweak),内存管理就是全自动且高效的。

00:00
00:00