1.ARC是什么
我们知道,在C语言中,创建对象时必须手动分配和释放适量的内存。然而,在 Swift 中,当不再需要类实例时,ARC 会自动释放这些实例的内存。
Swift 使用 ARC 来跟踪和管理应用程序的内存,其主要是由Objective-C语言提供的。ARC是一种自动化的内存管理机制,它通过在编译时插入内存管理代码来自动管理对象的引用计数。ARC机制可以让开发者不用手动管理对象的引用计数,从而减少了内存泄漏和野指针等问题的发生。
总的来说:
- Swift中每个对象都有一个【被引用计数】;
- 当对象A被引用时(实例),对象A的被引用计数会 + 1;
- 当对象A被放弃引用时,对象A的被引用计数会 - 1;
- 只有当对象A的被引用计数为 0 的时候,ARC才会释放对象A的内存。
2.强引用stong
为了防止实例在仍然需要时被释放,属性、常量或变量在分配实例时会建立对实例的强引用。
//简单类Person
class Person{
var name0 : String
var name1 : String
init(name0: String, name1: String) {
self.name0 = name0
self.name1 = name1
}
deinit {
print("Person is deallocated")
}
}
let person : Person? = Person(name0: "whj", name1: "whj1")
var man = person
var women = person
man = nil
women = nil
以上设置了一个简单类Person,它有两个属性:init()和deinit()。deinit()方法仅在释放时才会被调用。类定义之后, Person类的实例已初始化。还有另外两个变量(man、women)指向与第一个变量(person)相同的实例。
当我们将man和women设置为nil时,发现deinit()并没有发挥作用,说明该实例并没有被释放内存,这是因为三个变量(person、man、women)都对该实例有着很强的引用,当三个变量均引用该实例的时候, 该实例的ARC的计数为3。因此,即使将两个变量设置为 nil,引用计数仍然为 1。除非引用计数不为零,否则实例仍然存在。
循环引用(类之间强引用引起)
循环引用可以简单的理解为A引用了B,B也引用了A,因为两者相互持有,所以ARC无法释放两者,这在应用程序中是致命的。
//简单类Person
class Person{
var name0 : String
var name1 : String
var band : Band?
init(name0: String, name1: String) {
self.name0 = name0
self.name1 = name1
}
deinit {
print("Person is deallocated")
}
}
class Band{
var bandName : String
var person : Person?
init(bandName: String) {
self.bandName = bandName
}
deinit {
print("\(bandName) is deallocated.")
}
}
var person : Person? = Person(name0: "whj", name1: "whj1")
var band : Band? = Band(bandName: "ruishi")
person?.band = band
band?.person = person
person = nil
band = nil
在上面的示例中,有两个简单的类,称为Person和Band。每个类都有一个可选属性,该属性具有另一个类的类型。正如您在代码中看到的,这些属性在初始化后被分配给彼此的实例。目前两者的关系可以如下图描述:
当我们设置person和band为nil ,关系如下:
由于变量被设置为nil,变量和实例之间的强引用消失了。然而,实例仍然存在,因为“ Band”属性和“Person”属性对每个实例都有很强的引用。只要存在强引用,引用计数就不会为零。因此,即使变量为零,实例也不会被释放。这称为循环引用。这会导致ARC 无法释放其内存,造成内存泄漏。
Block 和 代理 、NSTimer均有可能造成循环引用。
3.弱引用weak
解决循环引用的方法之一就是使用弱引用weak。弱引用weak对实例没有强引用。换句话说,它不会增加实例的强引用计数。因此,如果使用weak关键字,它并不真正参与实例的生命周期管理。
将代码改成:
//简单类Person
class Person{
var name0 : String
var name1 : String
weak var band : Band?
init(name0: String, name1: String) {
self.name0 = name0
self.name1 = name1
}
deinit {
print("Person is deallocated")
}
}
class Band{
var bandName : String
var person : Person?
init(bandName: String) {
self.bandName = bandName
}
deinit {
print("\(bandName) is deallocated.")
}
}
var person : Person? = Person(name0: "whj", name1: "whj1")
var band : Band? = Band(bandName: "ruishi")
person?.band = band
band?.person = person
person = nil
band = nil
4.无主引用Unowned
解决循环引用的另一种方法就是使用无主引用Unowned。与弱引用相同,无主引用不会增加引用计数。那么,无主引用Unowned和弱引用weak有什么区别呢?区别在于:
weak是可选类型,可以设置为nil,而Unowned是值类型,其必须持有具体的值。
参考:
https://kentakodashima.medium.com/ios-arc-memory-management-in-ios-30aae3da92cf