在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:)
方法来设置 property1
和 property2
的值。此外,我们还定义了一个便利初始化器 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
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
实例时,初始化过程遵循以下顺序:
- 首先设置
property2
的默认值为0
。 - 然后调用父类
RootClass
的指定初始化器,将传递的参数"hello"
赋值给property1
。 - 最后,在子类
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
,它具有与父类相同的指定初始化器,并且还定义了一个带有三个参数 property1
、property2
和 property3
的初始化方法。
注意到,我们使用 override
关键字重载了父类的指定初始化器,并在新的初始化方法中添加了一个额外的 property3
参数。在新的构造函数中,我们首先初始化 property3
,然后调用父类的指定初始化器来初始化 property1
和 property2
。