Swift Initialization

构造过程是为了使用某个类、结构体或枚举类型的实例而进行的准备过程。这个过程包含了为实例中的每个属性设置初始值和为其执行必要的准备和初始化任务。

面向对象中的构造方法通过初始化属性值,来创建合法、有意义的对象实例。

合法、有效对象

关于如何构建合法有效对象,非常推荐阅读 Martin FowlerDependency Injection

这里所谈的合法对象,是业务场景,而非技术方面。

举例来说,有些值应该在对象构建时进行赋值,而有些可以通过属性赋值后期添加。

举例:一个系统需要手机号来注册用户,手机号就必须在对象构建时进行赋值。而爱好,则可以在用户注册完成后进行补充。

Swift中,提供了optional,则很方便的应用这种场景,需要构建时期赋值的属性,可以用非可能值类型, 编译器会检查init 方法中是否进行初始化,而其它可以用 Optional 类型

Swift中指定构造器会对所有属性进行初始化,Optional比较特殊,它有初始值 nil,系统会隐式赋值。

有些值在对象生命周期中是不能进行改变的,而有些值则可以。

let 和 var 可以解决这种需求。记得,构造方法中,可以对 let 的属性进行修改。

构造器初识

在Swift中,构造器分为两种,指定构造器便利构造器

- 指定构造器

1
2
3
init(parameters) {
statements
}

该类型 init() 方法中必须确保所有实例的中存储型属性(非可能值类型)都被显示初始化(Optioal 有默认隐式赋值为 nil,可以不显示赋值)。

每一个类都必须拥有至少一个指定构造器。当然,也可以通过继承父类中的指定构造器来满足了条件。(如果一个类未继承任何父类,且没有非可能值类型的属性,可以不用显示写 init() 函数,系统会自动隐式提供)。

1
2
3
4
# 未继承任何父类,且没有非可能值类型的属性,不用显示写 init()
class User {
var name: String?
}

- 便利构造器

1
2
3
convenience init(parameters) {
statements
}

便利构造器是辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,用途:

  • 为其参数提供默认值
  • 创建一个特殊用途或特定输入的实例。

多构造器和构造参数

和其它面向对象语言一样,你可以创建多个构造方法,来为其提供定制化构造所需值的类型和名字。

1
2
3
4
5
6
7
8
9
10
11
struct Celsius {
var temperatureInCelsius: Double = 0.0

init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}

init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
}

init 中参数和 func 一样可使用 (外参 内参: 类型) 的型式。如果不需要外参,可以用 _ 代替。

继承下的对象构建

1
2
3
4
5
6
7
8
9
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
1
2
3
4
5
6
7
8
9
10
class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}

构造器的继承和重载

Swift 的 构造器继承 跟 ObjC 中的子类不一样。

继承规则:

  1. 如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。
  2. 如果子类提供了所有父类指定构造器的实现 - 不管是通过 规则1继承 过来的,还是通过 自定义实现 的–它将自动继承所有父类的便利构造器。

如果子类中有新的属性,需要在构造器中初始化,可以通过以下两种方式进行:

- 重载父类构造器

通过 override 父类的同名构造器,可以对子类的属性进行初始化。

1
2
3
override init() {
super.init()
}

- 自定义构造器

还可以写一个不同名的init方法,注意的是,该方法中强制要求调用父类的任意super.init()

1
2
3
4
init(name: String) {
self.name = name
super.init()
}

构造器链/调用关系

关于 构定构造器便利构造器 之间的调用关系,有三条规则(更多详细):

  1. 指定构造器必须调用其直接父类的的指定构造器。
  2. 便利构造器必须调用同一类中定义的其它构造器。
  3. 便利构造器必须最终以调用一个指定构造器结束。

构造器 继承、重载 Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Base {
var name: String
var sex: Int?

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

init(name: String, sex: Int) {
self.name = name
self.sex = sex
}

convenience init() {
self.init(name: "blank")
self.sex = 1
}

func showName() {
println(self.name)
}
}

这里有一个基类,提供了2个指定构造器、1个便利构造器和一个showName方法。

1. 无 覆写/自定义 指定构造器

1
2
class Member: Base {
}

如果子类中不声明任何指定构造器,它将继承父类中所有的指定构造器和便利构造器。

可以这样初始化子类:

1
2
3
var m = Member()
var m1 = Member(name: "1")
var m2 = Member(name: "", sex: 0)

2. 部分 覆写/自定义 指定构造器

如果子类中,重写 override 了父类的一个或多个(非全部重写),或自定义了一个构造器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class User: Base {
var phone: String

// 覆写 基类构造器
override init(name: String) {
self.phone = "1"

super.init(name: "1")
}

// 自定义新的构造器
init(name: String, sex: Int, phone: String) {
self.phone = phone
// 也需要调用父类的指定构造器
super.init(name: name)
}
}

此时父类有2个指定构造器,而这里只是重写了其中一个。不满足继承规则2。基类中的便利构造器未能被继承。

1
2
3
4
5
6
7
8
9
// 正确,子类中复写和自定义了
var u1 = User(name: "sss")
var u2 = User(name: "", sex: 0, phone: "")

// 错误,子类复写
var u3 = User(name: "s", sex: 1)

// 错误,子类未完全复写父类的指定构造器,父类的便利构造器未被继承。
var u4 = User()

3.全部 覆写 指定构造器

全部继承时,父类的便利构造器会被继承。满足继承规则2

1
2
3
4
5
6
7
8
9
class Account: Base {
override init(name: String) {
super.init(name: name)
}

override init(name: String, sex: Int) {
super.init(name: name, sex: sex)
}
}

如果你重载的构造器是一个便利构造器,你的重载过程必须通过调用同一类中提供的其它指定构造器来实现。这一规则的详细内容请参考构造器链。

1
2
// 正确
var u4 = Account()

4. 便利函数覆写

便利构造器也能被覆写。但注意的是,便利函数必须调用指定构造器结束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Admin: Base {

override init(name: String) {
super.init(name: "")
}

// 这里不用 override。
convenience init() {
self.init(name: "")
}

// 覆写不能用 override,会有以下错误
// Initializer does not override a designated initializer from its superclass
// override convenience init() {
// self.init(name: "")
// }
}

或者,子类中的子类构造器也可以是覆写父类中的指定构造器而来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Admin: Base {

override init(name: String) {
super.init(name: "")
}

override convenience init(name: String, sex: Int) {
self.init(name: "")
}

// 不能和指定构造器同名
// Invalid redeclaration of 'init(name:)'
// override convenience init(name: String) {
// self.init(name: "")
// }
}

使用场景:

在ObjC中,经常有这种场景,简短的构造器中通过设置默认属性值、调用复杂构造器来初始化。

1
2
3
4
5
6
7
- (id)init {
return [self initWithConfiguration:nil];
}

- (id)initWithConfiguration:(WKWebViewConfiguration *)configuration {
self = [super init];
}

Swift中如何实现?

  • 自定义构造器 init(config: WKWebViewConfiguration?)
  • 用 convenience 覆写掉父类的 init()
  • 在 convenience 中调用自定义的构造器
1
2
3
4
5
6
7
8
init(config: WKWebViewConfiguration?) {
// do something
super.init()
}

override convenience init() {
self.init(config: nil)
}

其它事项

required init

在类的构造器前添加 required 修饰符表明所有该类的子类都必须实现该构造器:

当子类覆盖基类的必要构造器时,必须在子类的构造器前同样添加required修饰符以确保当其它类继承该子类时,该构造器同为必要构造器。在覆盖基类的必要构造器时,不需要添加override修饰符:

1
2
3
4
5
6
7
8
9
10
11
class SomeClass {
required init() {
// 在这里添加该必要构造器的实现代码
}
}

class SomeSubclass: SomeClass {
required init() {
// 在这里添加子类必要构造器的实现代码
}
}

引申:所有继承 UIViewController 的类,都被要求被实现一个特性的 Init。SO

1
2
3
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

注意:如果子类继承的构造器能满足必要构造器的需求,则你无需显示的在子类中提供必要构造器的实现。

Failable init

有时候Init 也是一个Optional类型,会返回nil

1
2
3
if let image = UIImage( name: “foo” ) {
} else {
}

REF::