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

Swift中的初始化

在Swift中,初始化是类、结构体和枚举类型的重要组成部分。每个类型都需要至少一个指定初始化器来初始化它的属性。便捷初始化器和可失败初始化器是辅助类型,不能直接修改所有属性,只能通过修改指定初始化器中的某些值来实现。Swift 初始化过程从根指定初始化器开始执行,并按照特定顺序执行初始化过程。子类可以继承和重载父类的初始化方法,以提供更多的初始化方式。

初始化的基础知识

初始化的基本语法

我们可以通过 init 关键字来定义初始化方法。每个类、结构体和枚举都必须有至少一个初始化方法来创建其实例。例如:

1
2
3
4
5
6
7
8
9
class MyClass {
    var property: String

    init(property: String) {
        self.property = property
    }
}

let myObject = MyClass(property: "hello, world")

在上面的代码中,我们创建了一个名为 MyClass 的类,并定义了一个带有一个参数的初始化方法。该初始化方法接受一个 String 类型的参数,并将其赋值给 property 属性。我们随后创建了一个 MyClass 实例并将其赋值给 myObject 变量。

我们还可以定义多个初始化方法。例如,下面的代码演示了如何定义一个类并附带两个初始化方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MyClass {
    var property1: String
    var property2: Int

    init(property1: String) {
        self.property1 = property1
        self.property2 = 0
    }

    init(property2: Int) {
        self.property1 = ""
        self.property2 = property2
    }

    init(property1: String, property2: Int) {
        self.property1 = property1
        self.property2 = property2
    }
}

let obj1 = MyClass(property1: "hello")
let obj2 = MyClass(property2: 42)
let obj3 = MyClass(property1: "world", property2: 100)

在上面的例子中,我们定义了三个初始化方法。第一个初始化方法只接受一个字符串参数,第二个初始化方法只接受一个整数参数,第三个初始化方法分别接受一个字符串和一个整数参数。

成员变量的初始化

在Swift中,所有成员变量都必须在对象创建时进行初始化。如果未对成员变量进行初始化,则编译器将引发错误。例如:

1
2
3
4
5
class MyClass {
    var property: String // 编译器会提示错误:Class 'MyClass' has no initializers

    // ...
}

在这种情况下,编译器会抱怨我们的类没有初始化程序。我们必须显式地提供初始化方法或为属性赋一个默认值,以便编译器可以在创建对象时对其进行初始化。

可选链式调用的初始化方法

有时,在创建对象时可能失败,例如因为无效的输入。为了避免抛出异常,我们可以使用可选类型来声明初始化方法。这种方法可以返回 nil,表明初始化失败。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
class MyClass {
    var property: String

    init?(property: String) {
        if property.isEmpty {
            return nil
        }

        self.property = property
    }
}

let myObject = MyClass(property: "") // 这里的 myObject 将被设置为 nil

在上面的代码中,我们定义了一个带有一个参数的初始化方法,该方法返回 MyClass? 类型。如果传递给该方法的参数为空字符串,则该方法将返回 nil,否则它将创建一个新的 MyClass 对象并将其返回。

注意,我们在 init 方法名称后面加上了 ?,表示该方法返回一个可选类型。如果初始化失败,该方法将返回 nil

初始化的不同种类

指定初始化器

指定初始化器是一个主要的初始化方法,它负责为类的所有属性设置初始值。每个类都必须拥有一个指定初始化器。一个类可以有多个指定初始化器,但它们之间必须按照调用链的形式承前启后,也就是说,一个指定初始化器必须调用另一个指定初始化器,最终调用到根指定初始化器,即为类的主要初始化方法。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
class MyClass {
    var property1: String
    var property2: Int

    init(property1: String, property2: Int) {
        self.property1 = property1
        self.property2 = property2
    }

    convenience init() {
        self.init(property1: "", property2: 0)
    }
}

在上面的代码中,我们定义了两个初始化方法,其中 init(property1:property2:) 是指定初始化器。注意到 convenience 关键字表示这是一个便利初始化器(后面会有介绍),而非主要初始化方法。

在本例中,我们通过调用 init(property1:property2:) 方法来设置 property1property2 的值。此外,我们还定义了一个便利初始化器 init(),该方法将默认值传递给主要初始化器 init(property1:property2:)

便捷初始化器

便捷初始化器 是辅助初始化方法,通过调用指定初始化器来初始化对象,并且可以提供一些默认值。它们不能直接修改所有属性,只能通过修改指定初始化器中的某些值来实现。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyClass {
    var property1: String
    var property2: Int

    init(property1: String, property2: Int) {
        self.property1 = property1
        self.property2 = property2
    }

    convenience init() {
        self.init(property1: "", property2: 0)
    }

    convenience init(property1: String) {
        self.init(property1: property1, property2: 0)
    }
}

在上面的代码中,我们除了定义了一个指定初始化器init(property1:property2:),还定义了两个便捷初始化器init()init(property1:)init() 方法使用默认值调用指定初始化器init(property1:property2:),而 init(property1:) 则使用默认值 0 调用 init(property1:property2:)

可失败初始化器

可失败初始化器是一种特殊的初始化方法,可能无法成功地创建对象。当初始化过程失败时,它们将返回 nil。可失败初始化器 前面加上 init? 关键字以表示其返回值是可选类型。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyClass {
    var property: String

    init?(property: String) {
        if property.isEmpty {
            return nil
        }

        self.property = property
    }
}

let myObject1 = MyClass(property: "hello, world") // 初始化成功
let myObject2 = MyClass(property: "") // 初始化失败,myObject2 将被设置为 nil

在上面的代码中,我们定义了一个带有一个参数的可失败初始化器init?(property:)。如果传递给该方法的参数为空字符串,则该方法将返回 nil,否则它将创建一个新的 MyClass 对象并将其返回。

初始化的执行顺序

Swift 初始化过程从根指定初始化器开始,按照以下顺序执行:

  1. 设置存储属性的默认值(如果有的话)
  2. 调用父类的指定初始化器
  3. 修改和自定义属性值等操作

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class RootClass {
    var property1: String

    init(property1: String) {
        self.property1 = property1
    }
}

class ChildClass: RootClass {
    var property2: Int

    override init(property1: String) {
        self.property2 = 0
        super.init(property1: property1)
        self.property2 = 42
    }
}

let obj = ChildClass(property1: "hello")
print(obj.property1) // 输出 "hello"
print(obj.property2) // 输出 "42"

在上面的代码中,我们定义了一个根类 RootClass 和一个子类 ChildClass。我们将 property1 定义在根类中,并将 property2 定义在子类中。注意到 ChildClass 重写了父类的指定初始化器。

当我们创建一个 ChildClass 实例时,初始化过程遵循以下顺序:

  1. 首先设置 property2 的默认值为 0
  2. 然后调用父类 RootClass 的指定初始化器,将传递的参数 "hello" 赋值给 property1
  3. 最后,在子类 ChildClass 中,我们将 property2 的值修改为 42

初始化方法的继承和重载

初始化方法的继承

子类会自动继承其父类的所有初始化方法(包括指定初始化器、便捷初始化器和可失败初始化器),但是子类不能直接继承父类的便捷初始化器和可失败初始化器,因为它们不是主要初始化方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MyClass {
    var property: String

    init(property: String) {
        self.property = property
    }

    convenience init() {
        self.init(property: "")
    }
}

class MySubclass: MyClass {
    // 继承父类的指定初始化器和便利初始化器 init()
}

在上面的代码中,我们定义了一个父类 MyClass,并定义了一个便捷初始化器init()。子类 MySubclass 继承了父类的指定初始化器和便捷初始化器init()

初始化方法的重载

子类可以通过重载父类的初始化方法来提供其他的初始化方式。在重载一个初始化方法时,我们需要使用 override 关键字来标记它。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MyClass {
    var property1: String
    var property2: Int

    init(property1: String, property2: Int) {
        self.property1 = property1
        self.property2 = property2
    }
}

class MySubclass: MyClass {
    var property3: Double

    override init(property1: String, property2: Int) {
        self.property3 = 0.0
        super.init(property1: property1, property2: property2)
    }

    init(property1: String, property2: Int, property3: Double) {
        self.property3 = property3
        super.init(property1: property1, property2: property2)
    }
}

在上面的代码中,我们定义了一个父类 MyClass,其中包含一个指定初始化器init(property1:property2:)。我们还定义了一个子类 MySubclass,它具有与父类相同的指定初始化器,并且还定义了一个带有三个参数 property1property2property3 的初始化方法。

注意到,我们使用 override 关键字重载了父类的指定初始化器,并在新的初始化方法中添加了一个额外的 property3 参数。在新的构造函数中,我们首先初始化 property3,然后调用父类的指定初始化器来初始化 property1property2

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

Swift中的继承

Swift中的反初始化