首页 Swift中的反初始化
文章
取消

Swift中的反初始化

在 Swift 中,类和结构体是引用类型和值类型。当一个实例不再需要时,Swift 的自动引用计数(ARC)机制会自动释放与之关联的内存。在释放实例之前,Swift 会自动调用该实例的反初始化函数(deinitializer),也称为析构器(destructor)。这个过程确保了实例被释放时可以执行一些清理工作。

反初始化函数的定义

在类或者结构体中定义反初始化函数的方式如下:

1
2
3
deinit {
    // perform some cleanup code here
}

与初始化函数不同,反初始化函数无需参数,并且不需要返回任何值。反初始化函数会在实例被销毁之前被自动调用。

反初始化函数语法

反初始化函数使用 deinit 关键字来声明,其语法与其他函数相似。以下是一个简单的例子:

1
2
3
4
5
6
7
8
class MyClass {
    deinit {
        print("MyClass instance is being deallocated")
    }
}

var myObject: MyClass? = MyClass()
myObject = nil // Output: "MyClass instance is being deallocated"

在这个例子中,我们创建了一个 MyClass 类,并定义了一个反初始化函数来打印一条消息,表示实例正在被销毁。接着,我们创建了一个可选类型的 myObject 对象,并将其赋值为 MyClass 的一个实例。最后,我们将 myObject 设置为 nil,这导致实例被释放并执行了反初始化函数。

结构体的反初始化函数

与类不同,结构体默认情况下没有反初始化函数。Swift 中的结构体是值类型,它们会在变量、常量或函数参数中被复制,而不是像类一样进行引用。因此,在结构体的实例不再需要时,它们会自动被销毁,无需手动释放。

如果你需要在结构体实例被销毁前执行一些清理工作,请手动添加一个反初始化函数。以下是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct MyStruct {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func printName() {
        print(self.name)
    }
    
    // deinitializer for MyStruct
    deinit {
        print("MyStruct instance with name \(name) is being deallocated")
    }
}

var myStructObject: MyStruct? = MyStruct(name: "John")
myStructObject?.printName() // Output: "John"
myStructObject = nil // Output: "MyStruct instance with name John is being deallocated"

在这个例子中,我们创建了一个带有属性 name 和初始化函数的 MyStruct 结构体。我们还添加了一个方法来打印名称。最后,我们手动添加了一个反初始化函数来打印一个消息,指示实例正在被销毁。

循环引用与反初始化函数

循环引用是指两个或多个对象相互持有对方的强引用,这可能导致内存泄漏。在 Swift 中,循环引用通常发生在两个对象相互持有对方的引用时。例如,在两个对象中的一个对象拥有另一个对象的属性,而另一个对象拥有第一个对象的属性时。

Swift 的 ARC 机制会自动管理内存,但是在出现循环引用时,ARC 无法知道哪个对象应该先释放内存,从而导致内存泄漏。幸运的是,Swift 提供了解决循环引用的方法,即弱引用(weak reference)和无主引用(unowned reference)。

弱引用

在 Swift 中,弱引用是一种特殊的引用类型,它不会增加对象的引用计数,这意味着当对象没有其他强引用时,它可以被释放。在声明一个弱引用时,需要在变量或常量前加上 weak 关键字。以下是一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Person {
    var name: String
    weak var dog: Dog?
    
    init(name: String) {
        self.name = name
    }
}

class Dog {
    var name: String
    var owner: Person
    
    init(name: String, owner: Person) {
        self.name = name
        self.owner = owner
    }
    
    deinit {
        print("Dog instance with name \(name) is being deallocated")
    }
}

var john: Person? = Person(name: "John")
var fido: Dog? = Dog(name: "Fido", owner: john!)

john?.dog = fido
john = nil // Output: "Dog instance with name Fido is being deallocated"

在这个例子中,我们创建了一个 Person 类和一个 Dog 类。Person 类包含了一个弱引用属性 dog,而 Dog 类包含了一个非弱引用属性 owner。我们还定义了一个反初始化函数来打印一条消息,表示实例正在被销毁。最后,我们创建了一个 Person 对象 john 和一个 Dog 对象 fido,并将它们关联起来。接着,我们将 john 设置为 nil,这导致 Person 对象被销毁并释放对 fido 的弱引用。因此,Dog 对象也被销毁并执行了反初始化函数。

无主引用

与弱引用类似,无主引用也是一种不会增加对象引用计数的引用类型。但是,与弱引用不同的是,无主引用假定对象一直存在,因此使用无主引用时需要保证对象不会被提前释放。在声明一个无主引用时,需要在变量或常量前加上 unowned 关键字。以下是一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Customer {
    var name: String
    var creditCard: CreditCard!
    
    init(name: String) {
        self.name = name
    }
    
    func addCreditCard(cardNumber: String) {
        self.creditCard = CreditCard(number: cardNumber, customer: self)
    }
    
    deinit {
        print("Customer instance with name \(name) is being deallocated")
    }
}

class CreditCard {
    var number: String
    unowned let customer: Customer
    
    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    
    deinit {
        print("CreditCard instance with number \(number) is being deallocated")
    }
}

var john: Customer? = Customer(name: "John")
john?.addCreditCard(cardNumber: "1234-5678-9012-3456")
john = nil // Output: "Customer instance with name John is being deallocated" and "CreditCard instance with number 1234-5678-9012-3456 is being deallocated"

在这个例子中,我们创建了一个 Customer 类和一个 CreditCard 类。Customer 类包含了一个可选的属性 creditCard,而 CreditCard 类包含了一个无主引用属性 customer。我们还定义了一个反初始化函数来打印一条消息,表示实例正在被销毁。最后,我们创建了一个 Customer 对象 john,并向其添加了一张信用卡。接着,我们将 john 设置为 nil,这导致 Customer 对象和 CreditCard 对象都被销毁并执行了反初始化函数。

本文由作者按照 CC BY 4.0 进行授权

Swift中的初始化

Swift中的可选链