在 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?
现在,让我们使用这个引用来将 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 强引用
// 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
分配给了实例 sabby 和 cathy。但是,sabby 和 cathy 是强引用,释放它们只会将它们的引用计数从 **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
在这种情况下,sabby 和 cathy 的引用计数都保持为 **1**,因为这次我们对 colleague 属性使用了弱引用。
因此,当我们释放实例时。
sabby = nil
cathy = nil
引用被完全释放,析构函数被调用。
deinit {
print("Memory Deallocated")
}
因此,我们得到输出“内存已释放”。