1. Swift 数据类型内存管理特性及差异
- 类(Class):
- 特性:类是引用类型。多个常量和变量可以引用同一个类实例。
- 内存管理:其内存分配在堆上,生命周期由 ARC 管理。ARC 会跟踪和管理类实例的引用计数。
- 结构体(Struct):
- 特性:结构体是值类型。当传递或赋值时,会进行值拷贝。
- 内存管理:其内存分配在栈上(对于简单结构体,复杂结构体可能部分内容在堆上),不需要 ARC 管理,当它们离开作用域时自动释放。
- 枚举(Enum):
- 特性:枚举也是值类型。与结构体类似,传递或赋值时会进行值拷贝。
- 内存管理:内存分配通常在栈上,生命周期与所在作用域相关,无需 ARC 管理。
2. 引用计数与 ARC 工作原理
- ARC 原理:ARC 是 Swift 中的自动内存管理机制,它通过跟踪对象的引用计数来管理内存。每当一个类实例被赋值给一个常量、变量或作为函数参数传递时,该实例的引用计数就会增加;当引用该实例的常量、变量超出其作用域时,引用计数就会减少。当引用计数变为 0 时,ARC 会自动释放该实例所占用的内存。
3. 内存管理问题 - 循环引用
- 循环引用场景:当两个类实例相互持有对方的强引用时,会导致循环引用。例如:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deinitialized")
}
}
class Apartment {
let number: Int
var tenant: Person?
init(number: Int) {
self.number = number
}
deinit {
print("Apartment \(number) is being deinitialized")
}
}
var john: Person? = Person(name: "John")
var number73: Apartment? = Apartment(number: 73)
john?.apartment = number73
number73?.tenant = john
john = nil
number73 = nil
// 此时 John 和 Apartment 73 都不会被 deinitialized,因为存在循环引用
4. 解决方案
- 使用弱引用(Weak Reference):
- 原理:弱引用不会增加对象的引用计数。适用于关系中一个实例可以在另一个实例之前释放的情况。
- 代码示例:
class Person {
let name: String
var apartment: Apartment?
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deinitialized")
}
}
class Apartment {
let number: Int
weak var tenant: Person?
init(number: Int) {
self.number = number
}
deinit {
print("Apartment \(number) is being deinitialized")
}
}
var john: Person? = Person(name: "John")
var number73: Apartment? = Apartment(number: 73)
john?.apartment = number73
number73?.tenant = john
john = nil
number73 = nil
// 此时 John 和 Apartment 73 都会被 deinitialized,因为使用了弱引用打破了循环引用
- 使用无主引用(Unowned Reference):
- 原理:无主引用与弱引用类似,不会增加引用计数,但它假定被引用的对象永远不会变为
nil
。适用于两个实例彼此间的生命周期紧密相连,并且在初始化完成后,都不会变为 nil
的情况。
- 代码示例:
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit {
print("\(name) is being deinitialized")
}
}
class CreditCard {
let number: Int
unowned let customer: Customer
init(number: Int, customer: Customer) {
self.number = number
self.customer = customer
}
deinit {
print("Credit card \(number) is being deinitialized")
}
}
var john: Customer? = Customer(name: "John")
john?.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
john = nil
// 此时 John 和 Credit card 1234_5678_9012_3456 都会被 deinitialized,因为使用了无主引用打破了循环引用