📜  Swift反初始化(1)

📅  最后修改于: 2023-12-03 15:35:13.039000             🧑  作者: Mango

Swift反初始化

在Swift中,反初始化(deinitialization)是一个可选的方法,可以在对象被释放之前执行清理任务。在这篇文章中,我们将深入探讨Swift中的反初始化。

基本用法

反初始化是类中的一个方法,以deinit关键字开头。这个方法没有参数,也没有返回类型。下面是反初始化的基本语法:

deinit {
    // 执行清理任务
}

在使用类的时候,当对象被释放时会自动调用该对象的反初始化方法。下面是一个简单的例子:

class MyClass {
    deinit {
        print("MyClass is being deinitialized")
    }
}

var object: MyClass? = MyClass()
object = nil // MyClass is being deinitialized

在这个例子中,我们创建了一个MyClass类并将其实例保存在一个可选的变量中。当我们将该变量设置为nil时,MyClass类的对象被释放,并执行了其反初始化方法。

对象生命周期

在了解Swift中反初始化的更多知识之前,我们需要先了解对象的生命周期。一个对象在Swift中的生命周期可以分为以下四个阶段:

  1. 对象创建
  2. 对象使用
  3. 对象不再被使用
  4. 对象释放

当我们使用varlet关键字创建一个变量或常量时,Swift会在内存中为该变量或常量分配一段空间,以存储该变量或常量的值。当我们使用这个变量或常量时,我们称这个变量或常量处于“对象使用”阶段。当这个变量或常量不再被使用时,Swift会释放这个变量或常量所占用的内存空间,“对象释放”阶段到来。

当Swift将对象释放时,会自动调用该对象的反初始化方法。反初始化方法是对象生命周期的最后一个阶段,可以执行清理任务,例如释放对象所持有的资源等。

在Swift中,当一个对象的所有强引用计数(strong reference)被释放时,该对象才会被释放。换句话说,只有当该对象不再被任何一个变量持有(strong reference)时,该对象才会被释放。下面是一个例子:

class MyClass {
    var property: Int

    init() {
        property = 0
    }

    deinit {
        print("MyClass is being deinitialized")
    }
}

var object1: MyClass? = MyClass()
var object2: MyClass? = object1

object1 = nil // MyClass is not being deinitialized
object2 = nil // MyClass is being deinitialized

在这个例子中,我们创建了一个MyClass类的对象,并将其赋值给object1object2两个变量。这意味着我们创建了两个指向同一个对象的强引用。因此,即使我们将object1设置为nil,对象也不会被释放,因为object2仍然持有对该对象的强引用。只有当object2也被设置为nil时,该对象才会被释放,并执行反初始化方法。

弱引用和无主引用

Swift中有两种类型的引用可以在某种程度上解决循环引用(circular reference)的问题:弱引用(weak reference)和无主引用(unowned reference)。这两种引用可以让我们在对象之间建立非强引用关系,以避免循环引用。下面我们将逐个介绍它们。

弱引用

弱引用是一种非强引用,它可以让一部分对象之间建立非强引用关系。弱引用不会保留引用对象的强引用计数,这意味着当对象的所有强引用计数为0时,该对象可以被Swift自动释放。弱引用通常用于创建一个指向可能不存在的对象的引用,例如在创建对象树时。

在Swift中,我们可以使用weak关键字创建一个弱引用。下面是一个例子:

class ParentClass {
    var child: ChildClass?

    deinit {
        print("ParentClass is being deinitialized")
    }
}

class ChildClass {
    weak var parent: ParentClass?

    deinit {
        print("ChildClass is being deinitialized")
    }
}

var parentObject: ParentClass? = ParentClass()
var childObject: ChildClass? = ChildClass()

parentObject?.child = childObject
childObject?.parent = parentObject

parentObject = nil // ParentClass is being deinitialized
childObject = nil // ChildClass is being deinitialized

在这个例子中,我们创建了一个ParentClass类和一个ChildClass类。我们在ParentClass中创建了一个可选的ChildClass类型的变量,以及在ChildClass中创建了一个弱引用类型的可选ParentClass变量。我们将childObject赋值给parentObject?.child,同时将parentObject赋值给childObject?.parent。这意味着我们创建了两个对象之间的互相指向引用。然后,我们将parentObjectchildObject设置为nil,并通过打印语句验证对象正确释放,并执行反初始化方法。

无主引用

无主引用是另一种非强引用,与弱引用类似,也可以让我们在对象之间建立非强引用关系。无主引用不会保留引用对象的强引用计数,但它也会假定该引用对象将一直存在,因此它永远不会是可选类型。

在Swift中,我们可以使用unowned关键字创建一个无主引用。下面是一个例子:

class MyClass {
    unowned let parent: ParentClass

    init(parent: ParentClass) {
        self.parent = parent
    }

    deinit {
        print("MyClass is being deinitialized")
    }
}

class ParentClass {
    var child: MyClass?

    deinit {
        print("ParentClass is being deinitialized")
    }
}

var parentObject: ParentClass? = ParentClass()
var childObject: MyClass? = MyClass(parent: parentObject!)

parentObject?.child = childObject
childObject = nil // MyClass is being deinitialized
parentObject = nil // ParentClass is being deinitialized

在这个例子中,我们创建了一个ParentClass类和一个MyClass类。我们在MyClass中创建了一个无主引用类型的let型属性parent,并在MyClass的初始化方法中初始化它。我们在ParentClass中创建了一个可选的MyClass类型的变量child,并将其赋值给parentObject?.child。这意味着我们创建了两个对象之间的互相指向引用。然后,我们将childObject设置为nil,并通过打印语句验证对象正确释放,并执行反初始化方法。最后,我们将parentObject设置为nil,这将释放ParentClassMyClass对象,并执行它们的反初始化方法。

延迟存储属性和反初始化

Swift中的延迟存储属性(lazy stored property)是另一种场景,其中反初始化方法也很有用。延迟存储属性是指在第一次被访问时才进行初始化的属性。当一个类的延迟存储属性依赖于其他对象时,我们可能需要在该类的反初始化方法中进行一些清理:释放资源、撤销订阅等。

我们可以将延迟存储属性存储在一个闭包中,并在闭包中执行初始化操作。这个闭包只会在该属性第一次被访问时执行。下面是一个例子:

class MyClass {
    var property: Int

    lazy var lazyProperty: String = {
        print("lazyProperty is being initialized")
        return "Hello, world!"
    }()

    init() {
        property = 0
    }

    deinit {
        print("MyClass is being deinitialized")
    }
}

var object: MyClass? = MyClass()
print("Before accessing lazyProperty")
print(object?.lazyProperty ?? "")
print("After accessing lazyProperty")
object = nil // MyClass is being deinitialized

在这个例子中,我们创建了一个MyClass类,并添加了一个延迟存储属性lazyProperty。在这个属性的定义中,我们将属性存储在一个闭包中,并在闭包中执行了初始化操作。我们创建了一个MyClass对象,并在对象创建之后,尝试访问lazyProperty。由于这是第一次访问lazyProperty,因此闭包会被执行,并返回Hello, world!。然后我们将object设置为nil,这将释放对象并执行其反初始化方法。

总结

反初始化是Swift中一个可选的方法,可以在对象被释放之前执行清理任务。在Swift中,当一个对象的所有强引用计数被释放时,该对象才会被释放。使用弱引用或无主引用可以帮助我们在两个对象之间建立非强引用关系,以避免循环引用。延迟存储属性也可以用于在第一次访问时执行初始化操作,并在类的反初始化方法中进行清理。