Swift 强引用和弱引用

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

但是,我们可以通过指定引用的类型来阻止 ARC 自动释放内存。例如,强引用会牢牢地持有实例,不允许 ARC 释放。

同样,弱引用也不能阻止实例被 ARC 释放。

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

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


Swift 中的强引用

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

class Employee {
  var name: String
  var colleague: Employee?

  // define initializer
  init(name : String) {
    self.name = name;
  }
}

// create two objects of Employee
var sabby: Employee? = Employee(name: "Sabby")
var cathy: Employee? = Employee(name: "Cathy")

在上面的例子中,我们创建了 Employee 类的实例:sabby 和 cathay。现在,sabby 和 cathy 这两个实例的引用计数都是 **1**。

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

var colleague: Employee?

现在,让我们使用这个引用来将 sabbycolleague 属性指向 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**。这里,上面的代码会将 sabbycathy 的引用计数减少 **1**。

然而,由于强引用,sabbycathy 的引用计数都是 **2**,所以在释放后的最终引用计数将是 **1**(**2 - 1**)。

因此,强引用 sabbycathy 不会被释放(引用计数不等于 **0**)。

注意:类类型必须是可选类型,这样我们才能将 nil 分配给该类的对象。这就是我们使用 Employee? 而不是 Employee 的原因。


示例:Swift 强引用

// declare a class
class  Employee {
    
  var name: String
  var salary: Int
  var colleague: Employee?   

  // define initializer
  init(name: String, salary: Int) {
    self.name = name
    self.salary = salary
  }

  // define deinitializer
  deinit {
    print("Memory Deallocated")
  }
}

// create instances of Employee
var sabby: Employee? = Employee(name: "Sabby", salary: 50000)
var cathy: Employee? = Employee(name: "Cathy", salary: 45000)

// increased reference count of cathy and sabby to 2
sabby?.colleague = cathy
cathy?.colleague = sabby

// deallocate objects
sabby = nil
cathy = nil

在上面的示例中,我们将 nil 分配给了实例 sabbycathy。但是,sabbycathy 是强引用,释放它们只会将它们的引用计数从 **2** 减少到 **1**。

因此,析构函数

deinit {
  print("Memory Deallocated")
}

不会被调用,我们也得不到任何输出。

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


Swift 弱引用

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

class Employee {
  weak var colleague: Employee? 
  ...  
}

这里,我们使用 weak 关键字将 colleague 声明为弱属性。

注意:默认情况下,属性是强类型的。


示例 2:弱引用

// declare a class
class  Employee {
    
  var name: String
  var salary: Int

  // weak property declaration
  weak var colleague: Employee?   

  // define initializer
  init(name: String, salary: Int) {
    self.name = name
    self.salary = salary
  }

  // define deinitializer
  deinit {
    print("Memory Deallocated")
  }
}

// create instances of Employee
var sabby: Employee? = Employee(name: "Sabby", salary: 50000)
var cathy: Employee? = Employee(name: "Cathy", salary: 45000)

// colleague property of sabby instance refers to cathy instance
sabby?.colleague = cathy

// colleague property of cathy instance refers to sabby instance
cathy?.colleague = sabby

// deallocate objects
sabby = nil
cathy = nil

输出

Memory Deallocated
Memory Deallocated

在上面的示例中,我们创建了 Employee 类的实例:sabby 和 cathay。

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

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

因此,当我们释放实例时。

sabby = nil
cathy = nil

引用被完全释放,析构函数被调用。

deinit {
  print("Memory Deallocated")
}

因此,我们得到输出“内存已释放”。

你觉得这篇文章有帮助吗?

我们的高级学习平台,凭借十多年的经验和数千条反馈创建。

以前所未有的方式学习和提高您的编程技能。

试用 Programiz PRO
  • 交互式课程
  • 证书
  • AI 帮助
  • 2000+ 挑战