Swift构造过程的一些要点

一、所有的存储属性必须设置合适的初始值(要么设置默认值,要么在构造器中赋值)。

二、可以在构造过程中修改常量属性的值(但是有默认值得常量属性的值不能修改)。

三、Swift为结构体和类提供默认构造器的条件:

  1. 所有的属性已提供默认值
  2. 自身没有提供任何构造器

四、关于结构体的逐一构造器:书上说结构体在所有的存储属性都提供了默认值,且没有提供定制构造器的情况下,就会自动获得逐一构造器。但是经过测试,第一个条件可以去掉,即存储属性没有提供默认值,也会有逐一构造器,只要没有提供定制构造器即可。

五、对于值类型,一般情况下,一旦自己定义了构造器,就无法再访问默认构造器或者逐一构造器。

六、对于值类型,要想自己定义构造器的同时,还可以使用默认构造器或者逐一构造器,则需要把自定义的构造器放入 extension 中。

七、类的构造器的代理准则:

  1. 指定构造器必须调用其直接构造器的指定构造器,即执行 super.init(...)
  2. 便利构造器必须调用同一类中定义的其他构造器,即执行 self.init(...)
  3. 便利构造器最终以调用一个指定构造器结束

八、两段式构造过程中的安全检查:

  1. 指定构造器必须先将新定义的属性赋值后,再调用 super.init(...)
  2. 指定构造器必须先调用 super.init(...) ,再给继承下来的属性赋新值
  3. 便利构造器必须先调用 self.init(...) ,再给任意属性赋新值
  4. 构造过程第一阶段完成之前,self 的值不能使用

九、Swift 中的子类不会默认继承父类的构造器

十、自动继承的条件:

  1. 子类中没有定义任何指定构造器,则它会继承所有父类的指定构造器
  2. 如果子类提供了所有父类指定构造器的实现(不管是通过规则 1 继承古来的,还是自定义实现的),它将会继承所有父类的便利构造器
  3. 即使在子类中添加了更多的便利构造器,这两条规则仍然适用

一个例子:

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
35
36
37
38
39
40
41
42
43
44
45
46
// 父类
class Food {
var name: String

// 指定构造器
init(name: String) {
self.name = name
}

// 便利构造器
convinience init() {
self.init(name: "[Unnamed]")
}

}

// Food 类的子类
class RecipeIngredient: Food {
var quantity: Int

// 子类自定义了指定构造器,所以不会继承父类的构造器
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name) // 此处先初始化了 quantity 属性,然后再向上代理
}

// 子类的便利构造器,因为与父类的指定构造器重名,所以需要使用 override 标识
override convenience init(name: String) {
self.init(name: name, quantity: 1)
}

}

// RecipeIngredient 类的子类
class ShoppingListItem: RecipeIngredient {

var purched = false
var description: String {
var output = "\(quantity) * \(name.lowercaseString)"
output += purched ? "yes" : "no"
return output
}

// ShoppingListItem 类中没有定义任何构造器,且所有的属性都提供了默认值,因此它将继承 RecipeIngredient 的指定构造器和便利构造器

}

十一、类的可失败构造器只能在所有的类属性初始化后和所有类之间的构造器的代理调用发生完成后触发失败行为

十二、可失败构造器可以向上或者横向代理可失败构造器和非可失败构造器,但是非可失败构造器永远也不能代理可失败构造器。

十三、在类的构造器前加 required 修饰符表明所有的子类都必须实现该构造器。重写基类的必要构造器不需要加 override 修饰符。

十四、如果子类继承的构造器能满足必要构造器的需求,则无需显式的在子类中提供必要构造器的实现。

十五、协议可以要求它的遵循者实现指定的构造器。在遵循该协议的类中我们实现该构造器,可以任意指定其为指定构造器或者便利构造器。在这两种情况下,你都必须给构造器的实现标上 required 修饰符,这样可以保证所有遵循该协议的子类,同样能为构造器规定提供一个显式的实现或者继承实现。

下面就可以来讲一下经常会碰到的一个问题,当我们在 UIViewController 或者 UIView 的子类中自定义或者重载一个指定构造器后,Xcode 总是会提示我们没有实现下面这个构造器:

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

如果我们没有自己没有改动任何构造器,就不会有这个错误提示。
那是因为这个指定构造器是由 NSCoding 协议中定义的:

1
2
3
4
5
6
public protocol NSCoding {

public func encodeWithCoder(aCoder: NSCoder)
public init?(coder aDecoder: NSCoder) // NS_DESIGNATED_INITIALIZER

}

UIViewController 或者 UIView 都实现了该协议,因此提供了实现。所以其子类在没有提供自定义构造器的情况下默认继承了该构造器,所以不会报错。
当我们自定义构造器的时候,该构造器不会被继承,因为它是协议中规定的必要构造器,所以我们必须要实现,所以 Xcode 就会报错了。