Swift 强引用和弱引用

在本教程中,我们将通过示例了解 Swift 强引用和弱引用。

在 Swift 中,ARC(自动引用计数)自动处理内存的分配和释放。

但是,我们可以通过指定引用类型来防止 ARC 自动释放内存。例如,强引用保持对实例的固定控制,不允许 ARC 释放。

同样,弱引用不能保护实例不被 ARC 释放。

在学习强引用和弱引用之前,请确保了解 Swift中的类和对象

注意:默认情况下,属性的声明是强引用的。为了声明弱引用,我们使用了 weak 关键字。

Swift 的强引用

在 Swift 中,每当我们创建一个类的实例时,引用计数值都会从 0 增加到 1。同样,如果我们将实例释放,计数会减少到 0。让我们看一个实例,

  1. class Employee {
  2. var name: String
  3. var colleague: Employee?
  4. // define initializer
  5. init(name : String) {
  6. self.name = name;
  7. }
  8. }
  9. // create two objects of Employee
  10. var sabby: Employee? = Employee(name: "Sabby")
  11. var cathy: Employee? = Employee(name: "Cathy")

在上面的实例中,我们创建了 Employee 类的实例:sabbycathay。现在 sabbycathy 实例的引用计数都是 1

这里,我们在类中创建了一个强引用。

  1. var colleague: Employee?

现在,让我们使用这个引用将 sabbycolleague 属性引用给 cathy

  1. sabby?.colleague = cathy

在这种情况下,为 cathy 创建了一个新的引用,将 cathy 的引用计数从 1 增加到 2

同样,如果我们将 sabby 赋值给 cathycolleaguesabby 的引用计数将增加到 2

  1. cathy?.colleague = sabby

这里,sabbycathy 实例的引用计数都是 2

释放实例

在 Swift 中,只有当内存实例的引用计数为 0 时,才会释放内存实例。为了手动释放内存实例,我们将 nil 赋值给引用。例如,

  1. sabby = nil
  2. cathy = nil

当我们将 nil 赋值给实例时,实例的引用计数将减少 1。这里,上面的代码将 sabbycathy 的引用计数减少 1

然而,由于强引用,sabbycathy 的引用计数都是 2,因此释放后的最终引用计数将是 1(2-1)

这是 sabbycathy 无法释放的强引用(引用计数不等于 0)。

注意:类的类型必须是可选类型,以便我们可以将 nil 赋值给该类的对象。这就是我们使用 Employee? 而不是 Employee 的原因。

实例:Swift 强引用

  1. // declare a class
  2. class Employee {
  3. var name: String
  4. var salary: Int
  5. var colleague: Employee?
  6. // define initializer
  7. init(name: String, salary: Int) {
  8. self.name = name
  9. self.salary = salary
  10. }
  11. // define deinitializer
  12. deinit {
  13. print("Memory Deallocated")
  14. }
  15. }
  16. // create instances of Employee
  17. var sabby: Employee? = Employee(name: "Sabby", salary: 50000)
  18. var cathy: Employee? = Employee(name: "Cathy", salary: 45000)
  19. // increased reference count of cathy and sabby to 2
  20. sabby?.colleague = cathy
  21. cathy?.colleague = sabby
  22. // deallocate objects
  23. sabby = nil
  24. cathy = nil

在上面的实例中,我们为 sabbycathy 实例指定了 nil。然而,sabbycathy 是强引用,释放它们只会将引用计数从 2 减少到 1

这就是为什么取消初始化

  1. deinit {
  2. print("Memory Deallocated")
  3. }

没有调用,我们也没有得到任何输出。

如果我们想完全释放实例,我们会使用弱引用。


Swift 弱引用

如前所述,弱引用不能保护对象不被释放。这是因为当我们声明一个属性为弱属性时,该属性的引用计数永远不会超过 1

  1. class Employee {
  2. weak var colleague: Employee?
  3. ...
  4. }

这里,我们使用了 weak 关键字将同事表示为弱属性。

注意:默认情况下,属性为强类型。
实例 2:弱引用
  1. // declare a class
  2. class Employee {
  3. var name: String
  4. var salary: Int
  5. // weak property declaration
  6. weak var colleague: Employee?
  7. // define initializer
  8. init(name: String, salary: Int) {
  9. self.name = name
  10. self.salary = salary
  11. }
  12. // define deinitializer
  13. deinit {
  14. print("Memory Deallocated")
  15. }
  16. }
  17. // create instances of Employee
  18. var sabby: Employee? = Employee(name: "Sabby", salary: 50000)
  19. var cathy: Employee? = Employee(name: "Cathy", salary: 45000)
  20. // colleague property of sabby instance refers to cathy instance
  21. sabby?.colleague = cathy
  22. // colleague property of cathy instance refers to sabby instance
  23. cathy?.colleague = sabby
  24. // deallocate objects
  25. sabby = nil
  26. cathy = nil

结果如下:

  1. Memory Deallocated
  2. Memory Deallocated

在上面的实例中,我们创建了 Employee 类的实例:sabbycathay

  1. var sabby: Employee? = Employee(name: "Sabby", salary: 50000)
  2. var cathy: Employee? = Employee(name: "Cathy", salary: 45000)

现在 sabbycathy 的参考计数都是 1

这里,我们将 sabby 的同级属性转交给 cathy,反之亦然。

  1. sabby?.colleague = cathy
  2. cathy?.colleague = sabby

在这种情况下,sabbycathy 的引用计数都保持为 1,因为这次我们对同级属性使用了弱引用。

这就是我们释放了实例的原因。

  1. sabby = nil
  2. cathy = nil

引用被完全释放,并调用取消初始化。

  1. deinit {
  2. print("Memory Deallocated")
  3. }

因此,最后输出 Memory Deallocated