Swift中的强引用与弱引用
- 强引用和弱引用的区别:在Swift中,强引用阻止ARC(自动引用计数)回收实例,而弱引用则不会,弱引用使用
weak
关键字声明。 - 引用计数的工作原理:创建类的实例时增加引用计数,当引用设为
nil
时减少引用计数,实例只有在引用计数为0时才会被ARC回收。 - 弱引用的应用:使用弱引用避免循环引用和内存泄漏,弱引用对于解耦组件和防止内存泄漏尤为重要。
在 Swift 中,ARC(自动引用计数)自动处理内存的分配和回收。
然而,我们可以通过指定引用的类型来防止 ARC 自动回收内存。例如,强引用牢牢地持有实例,并不允许 ARC 回收。
同样,弱引用不能阻止 ARC 回收实例。
在了解强引用和弱引用之前,请确保你理解了 Swift 中类和对象是如何工作的。
注意:属性的声明默认是强引用。要声明弱引用,我们使用 weak
关键字。
Swift 中的强引用
在 Swift 中,每当我们创建一个类的实例时,引用计数值会从 0 增加到 1。同样,如果我们回收实例,计数将减少到 0。让我们看一个例子,
class Employee {
var name: String
var colleague: Employee?
// 定义初始化器
init(name : String) {
self.name = name;
}
}
// 创建两个 Employee 的对象
var sabby: Employee? = Employee(name: "Sabby")
var cathy: Employee? = Employee(name: "Cathy")
在上面的示例中,我们创建了 Employee 类的实例:sabby 和 cathy。现在 sabby 和 cathy 的引用计数都是 1。
这里,我们在类中创建了一个强引用。
var colleague: Employee?
现在,让我们使用这个引用将 sabby
的 colleague
属性引用到 cathy
。
sabby?.colleague = cathy
在这种情况下,为 cathy
建立了一个新的引用,将 cathy
的引用计数从 1 增加到 2。
同样,如果我们将 sabby 指定给 cathy
的同事,sabby 的引用计数将增加到 2。
cathy?.colleague = sabby
这里,sabby 和 cathy 的实例都将有引用计数 2。
回收实例
在 Swift 中,只有在引用计数为 0 时,内存实例才会被回收。要手动回收内存实例,我们将引用赋值为 nil
。例如,
sabby = nil
cathy = nil
当我们将实例赋值为 nil
时,实例的引用计数将减少 1。这里,上面的代码将 sabby
和 cathy
的引用计数减少 1。
然而,由于强引用的存在,sabby
和 cathy
的引用计数为 2,所以最终回收后的引用计数将是 1(2 - 1)。
这就是强引用的 sabby
和 cathy
不会被回收(引用计数不等于 0)的原因。
注意:类类型必须是可选类型,以便我们可以将该类的对象赋值为 nil
。这就是为什么我们使用了 Employee?
而不是 Employee
。
示例:Swift 强引用
// 声明一个类
class Employee {
var name: String
var salary: Int
var colleague: Employee?
// 定义初始化器
init(name: String, salary: Int) {
self.name = name
self.salary = salary
}
// 定义析构器
deinit {
print("内存已回收")
}
}
// 创建 Employee 的实例
var sabby: Employee? = Employee(name: "Sabby", salary: 50000)
var cathy: Employee? = Employee(name: "Cathy", salary: 45000)
// 增加 cathy 和 sabby 的引用计数到 2
sabby?.colleague = cathy
cathy?.colleague = sabby
// deallocate objects
sabby = nil
cathy = nil
```
在上述示例中,我们将 `nil` 赋值给了 `sabby` 和 `cathy` 的实例。然而,`sabby` 和 `cathy` 是强引用,释放它们只会将它们的引用计数从 **2** 减少到 **1**。
这就是为什么析构器
```swift
deinit {
print("内存已释放")
}
没有被调用,因此我们没有得到任何输出。
如果我们想要完全释放实例的内存,我们可以使用弱引用。
Swift 弱引用
正如之前提到的,弱引用不会阻止对象被释放。这是因为当我们将一个属性声明为弱引用时,该属性的引用计数永远不会超过 1。
class Employee {
weak var colleague: Employee?
...
}
这里,我们使用了 weak 关键字来表示 colleague 是一个弱属性。
注意:默认情况下,属性是强类型的。
示例 2:弱引用
// 声明一个类
class Employee {
var name: String
var salary: Int
// 弱属性声明
weak var colleague: Employee?
// 定义初始化器
init(name: String, salary: Int) {
self.name = name
self.salary = salary
}
// 定义析构函数
deinit {
print("内存已释放")
}
}
// 创建 Employee 的 实例
var sabby: Employee? = Employee(name: "Sabby", salary: 50000)
var cathy: Employee? = Employee(name: "Cathy", salary: 45000)
// sabby 实例的 colleague 属性引用 cathy 实例
sabby?.colleague = cathy
// cathy 实例的 colleague 属性引用 sabby 实例
cathy?.colleague = sabby
// 释放对象
sabby = nil
cathy = nil
输出
内存已释放
内存已释放
在上面的示例中,我们创建了 Employee 类的实例:sabby 和 cathy。
var sabby: Employee? = Employee(name: "Sabby", salary: 50000)
var cathy: Employee? = Employee(name: "Cathy", salary: 45000)
现在 sabby 和 cathy 的引用计数都是 1。
这里,我们将 sabby
的 colleague 属性引用到了 cathy
,反之亦然。
sabby?.colleague = cathy
cathy?.colleague = sabby
在这种情况下,由于这次我们使用了 colleague 属性的弱引用,sabby
和 cathy
的引用计数保持为 1。
这就是为什么当我们释放实例时。
sabby = nil
cathy = nil
引用被完全释放,析构函数被调用。
deinit {
print("内存已释放")
}
因此,我们得到了输出 "内存已释放"。