This commit is contained in:
949478479
2015-11-13 23:38:02 +08:00
parent ffcd9d6ae4
commit 64dee124e6

View File

@ -344,18 +344,18 @@ let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
<a name="class_inheritance_and_initialization"></a> <a name="class_inheritance_and_initialization"></a>
## 类的继承和构造过程 ## 类的继承和构造过程
类里面的所有存储型属性--包括所有继承自父类的属性--都必须在构造过程中设置初始值。 类里面的所有存储型属性——包括所有继承自父类的属性——都必须在构造过程中设置初始值。
Swift 提供了两种类型的类构造器来确保所有类实例中存储型属性都能获得初始值,它们分别是指定构造器和便利构造器。 Swift 为类类型提供了两种构造器来确保实例中所有存储型属性都能获得初始值,它们分别是指定构造器和便利构造器。
<a name="designated_initializers_and_convenience_initializers"></a> <a name="designated_initializers_and_convenience_initializers"></a>
### 指定构造器和便利构造器 ### 指定构造器和便利构造器
*指定构造器*是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。 *指定构造器*是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。
每一个类都必须拥有至少一个指定构造器。在某些情况下,许多类通过继承了父类中的指定构造器而满足了这个条件。具体内容请参考后续章节[自动构造器的继承](#automatic_initializer_inheritance)。 每一个类都必须拥有至少一个指定构造器。在某些情况下,许多类通过继承了父类中的指定构造器而满足了这个条件。具体内容请参考后续章节[构造器的自动继承](#automatic_initializer_inheritance)。
*便利构造器*是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入的实例。 *便利构造器*是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入的实例。
你应当只在必要的时候为类提供便利构造器,比方说某种情况下通过使用便利构造器来快捷调用某个指定构造器,能够节省更多开发时间并让类的构造过程更清晰明了。 你应当只在必要的时候为类提供便利构造器,比方说某种情况下通过使用便利构造器来快捷调用某个指定构造器,能够节省更多开发时间并让类的构造过程更清晰明了。
@ -383,13 +383,13 @@ convenience init(parameters) {
为了简化指定构造器和便利构造器之间的调用关系Swift 采用以下三条规则来限制构造器之间的代理调用: 为了简化指定构造器和便利构造器之间的调用关系Swift 采用以下三条规则来限制构造器之间的代理调用:
#### 规则 1 ##### 规则 1
指定构造器必须调用其直接父类的的指定构造器。 指定构造器必须调用其直接父类的的指定构造器。
#### 规则 2 ##### 规则 2
便利构造器必须调用同一类中定义的其它构造器。 便利构造器必须调用同一类中定义的其它构造器。
#### 规则 3 ##### 规则 3
便利构造器必须最终以调用一个指定构造器结束。 便利构造器必须最终以调用一个指定构造器结束。
一个更方便记忆的方法是: 一个更方便记忆的方法是:
@ -399,109 +399,109 @@ convenience init(parameters) {
这些规则可以通过下面图例来说明: 这些规则可以通过下面图例来说明:
![构造器代理图](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/initializerDelegation01_2x.png) ![构造器代理图](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/initializerDelegation01_2x.png)
如图所示,父类中包含一个指定构造器和两个便利构造器。其中一个便利构造器调用了另外一个便利构造器,而后者又调用了唯一的指定构造器。这满足了上面提到的规则 2 和 3。这个父类没有自己的父类所以规则 1 没有用到。 如图所示,父类中包含一个指定构造器和两个便利构造器。其中一个便利构造器调用了另外一个便利构造器,而后者又调用了唯一的指定构造器。这满足了上面提到的规则 2 和 3。这个父类没有自己的父类所以规则 1 没有用到。
子类中包含两个指定构造器和一个便利构造器。便利构造器必须调用两个指定构造器中的任意一个,因为它只能调用同一个类里的其他构造器。这满足了上面提到的规则 2 和 3。而两个指定构造器必须调用父类中唯一的指定构造器这满足了规则 1。 子类中包含两个指定构造器和一个便利构造器。便利构造器必须调用两个指定构造器中的任意一个,因为它只能调用同一个类里的其他构造器。这满足了上面提到的规则 2 和 3。而两个指定构造器必须调用父类中唯一的指定构造器这满足了规则 1。
> 注意 > 注意
这些规则不会影响使用时,如何用类去创建实例。任何上图中展示的构造器都可以用来完整创建对应类的实例。这些规则只在实现类的定义时有影响。 这些规则不会影响类的实例如何创建。任何上图中展示的构造器都可以用来创建完全初始化的实例。这些规则只影响类定义如何实现
下面图例中展示了一种针对四个类的更复杂的类层级结构。它演示了指定构造器是如何在类层级中充当“管道”的作用,在类的构造器链上简化了类之间的相互关系。 下面图例中展示了一种涉及四个类的更复杂的类层级结构。它演示了指定构造器是如何在类层级中充当“管道”的作用,在类的构造器链上简化了类之间的相互关系。
![复杂构造器代理图](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/initializerDelegation02_2x.png) ![复杂构造器代理图](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/initializerDelegation02_2x.png)
<a name="two_phase_initialization"></a> <a name="two_phase_initialization"></a>
### 两段式构造过程 ### 两段式构造过程
Swift 中类的构造过程包含两个阶段。第一个阶段,每个存储型属性通过引入它们的类的构造器来设置初始值。当每一个存储型属性值被确定后,第二阶段开始,它给每个类一次机会在新实例准备使用之前进一步定制它们的存储型属性。 Swift 中类的构造过程包含两个阶段。第一个阶段,每个存储型属性通过引入它们的类的构造器来设置初始值。当每一个存储型属性值被确定后,第二阶段开始,它给每个类一次机会在新实例准备使用之前进一步定制它们的存储型属性。
两段式构造过程的使用让构造过程更安全,同时在整个类层级结构中给予了每个类完全的灵活性。两段式构造过程可以防止属性值在初始化之前被访问也可以防止属性被另外一个构造器意外地赋予不同的值。 两段式构造过程的使用让构造过程更安全,同时在整个类层级结构中给予了每个类完全的灵活性。两段式构造过程可以防止属性值在初始化之前被访问也可以防止属性被另外一个构造器意外地赋予不同的值。
> 注意 > 注意
Swift 的两段式构造过程跟 Objective-C 中的构造过程类似。最主要的区别在于阶段 1Objective-C 给每一个属性赋值`0`或空值(比如说`0``nil`。Swift 的构造流程则更加灵活,它允许你设置定制的初始值,并自如应对某些属性不能以`0``nil`作为合法默认值的情况。 Swift 的两段式构造过程跟 Objective-C 中的构造过程类似。最主要的区别在于阶段 1Objective-C 给每一个属性赋值`0`或空值(比如说`0``nil`。Swift 的构造流程则更加灵活,它允许你设置定制的初始值,并自如应对某些属性不能以`0``nil`作为合法默认值的情况。
Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程能顺利完成: Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程能顺利完成:
#### 安全检查 1 ##### 安全检查 1
指定构造器必须保证它所在类引入的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中的构造器。 指定构造器必须保证它所在类引入的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中的构造器。
如上所述,一个对象的内存只有在其所有存储型属性确定之后才能完全初始化。为了满足这一规则,指定构造器必须保证它所在类引入的属性在它往上代理之前先完成初始化。 如上所述,一个对象的内存只有在其所有存储型属性确定之后才能完全初始化。为了满足这一规则,指定构造器必须保证它所在类引入的属性在它往上代理之前先完成初始化。
#### 安全检查 2 ##### 安全检查 2
指定构造器必须先向上代理调用父类构造器,然后再为继承的属性设置新值。如果没这么做,指定构造器赋予的新值将被父类中的构造器所覆盖。 指定构造器必须先向上代理调用父类构造器,然后再为继承的属性设置新值。如果没这么做,指定构造器赋予的新值将被父类中的构造器所覆盖。
#### 安全检查 3 ##### 安全检查 3
便利构造器必须先代理调用同一类中的其它构造器,然后再为任意属性赋新值。如果没这么做,便利构造器赋予的新值将被同一类中其它指定构造器所覆盖。 便利构造器必须先代理调用同一类中的其它构造器,然后再为任意属性赋新值。如果没这么做,便利构造器赋予的新值将被同一类中其它指定构造器所覆盖。
#### 安全检查 4 ##### 安全检查 4
构造器在第一阶段构造完成之前,不能调用任何实例方法不能读取任何实例属性的值,`self`的值不能被引用 构造器在第一阶段构造完成之前,不能调用任何实例方法不能读取任何实例属性的值,不能引用`self`作为一个值
类实例在第一阶段结束以前并不是完全有效,仅能访问属性和调用方法,一旦完成第一阶段,该实例才会声明为有效实例。 类实例在第一阶段结束以前并不是完全有效的。只有第一阶段完成后,该实例才会为有效实例,才能访问属性和调用方法
以下是两段式构造过程中基于上述安全检查的构造流程展示: 以下是两段式构造过程中基于上述安全检查的构造流程展示:
#### 阶段 1 ##### 阶段 1
- 某个指定构造器或便利构造器被调用 - 某个指定构造器或便利构造器被调用
- 完成新实例内存的分配,但此时内存还没有被初始化 - 完成新实例内存的分配,但此时内存还没有被初始化
- 指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化 - 指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化
- 指定构造器将调用父类的构造器,完成父类属性的初始化 - 指定构造器将调用父类的构造器,完成父类属性的初始化
- 这个调用父类构造器的过程沿着构造器链一直往上执行,直到到达构造器链的最顶部 - 这个调用父类构造器的过程沿着构造器链一直往上执行,直到到达构造器链的最顶部
- 当到达了构造器链最顶部,且已确保所有实例包含的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化。此时阶段 1 完成。 - 当到达了构造器链最顶部,且已确保所有实例包含的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化。此时阶段 1 完成。
#### 阶段 2 ##### 阶段 2
- 从顶部构造器链一直往下,每个构造器链中类的指定构造器都有机会进一步定制实例。构造器此时可以访问`self`、修改它的属性并调用实例方法等等。 - 从顶部构造器链一直往下,每个构造器链中类的指定构造器都有机会进一步定制实例。构造器此时可以访问`self`、修改它的属性并调用实例方法等等。
- 最终,任意构造器链中的便利构造器可以有机会定制实例和使用`self` - 最终,任意构造器链中的便利构造器可以有机会定制实例和使用`self`
下图展示了在假定的子类和父类之间构造阶段1 下图展示了在假定的子类和父类之间构造阶段 1
![过程阶段1](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/twoPhaseInitialization01_2x.png) ![过程阶段1](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/twoPhaseInitialization01_2x.png)
在这个例子中,构造过程从对子类中一个便利构造器的调用开始。这个便利构造器此时没法修改任何属性,它把构造任务代理给同一类中的指定构造器。 在这个例子中,构造过程从对子类中一个便利构造器的调用开始。这个便利构造器此时没法修改任何属性,它把构造任务代理给同一类中的指定构造器。
如安全检查1所示,指定构造器将确保所有子类的属性都有值。然后它将调用父类的指定构造器,并沿着造器链一直往上完成父类的构过程。 如安全检查 1 所示,指定构造器将确保所有子类的属性都有值。然后它将调用父类的指定构造器,并沿着造器链一直往上完成父类的构过程。
父类中的指定构造器确保所有父类的属性都有值。由于没有更多的父类需要构建,也就无需继续向上做构建代理。 父类中的指定构造器确保所有父类的属性都有值。由于没有更多的父类需要初始化,也就无需继续向上代理。
一旦父类中所有属性都有了初始值,实例的内存被认为是完全初始化,阶段1也已完成。 一旦父类中所有属性都有了初始值,实例的内存被认为是完全初始化,阶段 1 完成。
以下展示了相同构造过程的阶段 2 以下展示了相同构造过程的阶段 2
![构建过程阶段2](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/twoPhaseInitialization02_2x.png) ![构建过程阶段2](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/twoPhaseInitialization02_2x.png)
父类中的指定构造器现在有机会进一步来定制实例(尽管它没有这种必要)。 父类中的指定构造器现在有机会进一步来定制实例(尽管这不是必须的)。
一旦父类中的指定构造器完成调用,子类的指定构造器可以执行更多的定制操作(同样,它也没有这种必要)。 一旦父类中的指定构造器完成调用,子类的指定构造器可以执行更多的定制操作(这也不是必须的)。
最终,一旦子类的指定构造器完成调用,最开始被调用的便利构造器可以执行更多的定制操作。 最终,一旦子类的指定构造器完成调用,最开始被调用的便利构造器可以执行更多的定制操作。
<a name="initializer_inheritance_and_overriding"></a> <a name="initializer_inheritance_and_overriding"></a>
### 构造器的继承和重写 ### 构造器的继承和重写
跟 Objective-C 中的子类不同Swift 中的子类不会默认继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更专业的子类继承,并被错误用来创建子类的实例。 跟 Objective-C 中的子类不同Swift 中的子类默认情况下不会继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更专业的子类继承,并被错误用来创建子类的实例。
>注意: > 注意
父类的构造器仅在确定和安全的情况下被继承。具体内容请参考后续章节[自动构造器的继承](#automatic_initializer_inheritance)。 父类的构造器仅会在安全和适当的情况下被继承。具体内容请参考后续章节[构造器的自动继承](#automatic_initializer_inheritance)。
假如你希望自定义的子类中能实现一个或多个跟父类相同的构造器,也许是为了完成一些定制的构造过程,你可以在你定制的子类中提供和重写与父类相同的构造器 假如你希望自定义的子类中能提供一个或多个跟父类相同的构造器,你可以在子类中提供这些构造器的自定义实现
当你写一个父类中带有指定构造器的子类构造器时,你需要重写这个指定构造器。因此,你必须在定义子类构造器时带上`override`修饰符。即使你重写系统提供的默认构造器也需要带上`override`修饰符,具体内容请参考[默认构造器](#default_initializers)。 当你在编写一个父类中指定构造器相匹配的子类构造器时,你实际上是在重写父类的这个指定构造器。因此,你必须在定义子类构造器时带上`override`修饰符。即使你重写的是系统自动提供的默认构造器也需要带上`override`修饰符,具体内容请参考[默认构造器](#default_initializers)。
无论是重写属性,方法或者是下标脚本,只要含有`override`修饰符会去检查父类是否有相匹配的重写指定构造器和验证重写构造器参数。 正如重写属性,方法或者是下标脚本,`override`修饰符会让编译器去检查父类是否有相匹配的指定构造器,并验证构造器参数是否正确
>注意 > 注意
当你重写一个父类指定构造器时,你需要写`override`修饰符,甚至你的子类构造器继承的是父类的便利构造器。 当你重写一个父类指定构造器时,你总是需要写`override`修饰符,即使你的子类将父类的指定构造器重写为了便利构造器。
相反,如果你写了一个和父类便利构造器相匹配的子类构造器,子类不能直接调用父类的便利构造器每个规则都在上文[类的构造器代理规则](#initializer_delegation_for_class_types)有所描述因此,你的子类不必(严格意义上来讲)提供一个父类构造器重写。这样的结果就是,你不需要在子类中提供一个匹配的父类便利器的实现时,加入`override`前缀。 相反,如果你写了一个和父类便利构造器相匹配的子类构造器,由于子类不能直接调用父类的便利构造器每个规则都在上文[类的构造器代理规则](#initializer_delegation_for_class_types)有所描述因此,严格意义上来讲,你的子类并未对一个父类构造器提供重写。最后的结果就是,你在子类中“重写”一个父类便利构造器时,不需要加`override`前缀。
在下面的例子中定义了一个基础类`Vehicle`。基类中声明了一个存储型属性`numberOfWheels`,它是值为`0``Int`类型属性。`numberOfWheels`属性用于创建名为`descrpiption`类型为`String`的计算型属性 在下面的例子中定义了一个叫`Vehicle`的基类。基类中声明了一个存储型属性`numberOfWheels`,它是值为`0``Int`类型的存储型属性。`numberOfWheels`属性用于创建名为`descrpiption``String`类型的计算型属性
```swift ```swift
class Vehicle { class Vehicle {
@ -512,7 +512,7 @@ class Vehicle {
} }
``` ```
`Vehicle`类只为存储型属性提供默认值,而不自定义构造器。因此,它会自动生成一个默认构造器,具体内容请参考[默认构造器](#default_initializers)。默认构造器通常在类中指定构造器,它可以用于创建属性叫`numberOfWheels``0``Vehicle`实例 `Vehicle`类只为存储型属性提供默认值,而不自定义构造器。因此,它会自动获得一个默认构造器,具体内容请参考[默认构造器](#default_initializers)。自动获得的默认构造器总会是类中指定构造器,它可以用于创建`numberOfWheels``0``Vehicle`实例
```swift ```swift
let vehicle = Vehicle() let vehicle = Vehicle()
@ -533,9 +533,9 @@ class Bicycle: Vehicle {
子类`Bicycle`定义了一个自定义指定构造器`init()`。这个指定构造器和父类的指定构造器相匹配,所以`Bicycle`中的指定构造器需要带上`override`修饰符。 子类`Bicycle`定义了一个自定义指定构造器`init()`。这个指定构造器和父类的指定构造器相匹配,所以`Bicycle`中的指定构造器需要带上`override`修饰符。
`Bicycle`的构造器`init()`一开始调用`super.init()`方法,这个方法的作用是调用`Bicycle`的父类`Vehicle`。这样可以确保`Bicycle`在修改属性之前它所继承的属性`numberOfWheels`能被`Vehicle`类初始化。在调用`super.init()`之后,原本的属性`numberOfWheels`被赋值为`2` `Bicycle`的构造器`init()`调用`super.init()`方法开始,这个方法的作用是调用`Bicycle`的父类`Vehicle`的默认构造器。这样可以确保`Bicycle`在修改属性之前它所继承的属性`numberOfWheels`能被`Vehicle`类初始化。在调用`super.init()`之后,属性`numberOfWheels`的原值被新值`2`替换
如果你创建一个`Bicycle`实例,你可以调用继承的`description`计算型属性去查看属性`numberOfWheels`是否有改变 如果你创建一个`Bicycle`实例,你可以调用继承的`description`计算型属性去查看属性`numberOfWheels`是否有改变
```swift ```swift
let bicycle = Bicycle() let bicycle = Bicycle()
@ -544,34 +544,34 @@ print("Bicycle: \(bicycle.description)")
``` ```
> 注意 > 注意
子类可以在初始化时修改继承变量属性,但是不能修改继承来的常量属性。 子类可以在初始化时修改继承来的变量属性,但是不能修改继承来的常量属性。
<a name="automatic_initializer_inheritance"></a> <a name="automatic_initializer_inheritance"></a>
### 自动构造器的继承 ### 构造器的自动继承
如上所述,子类不会默认继承父类的构造器。但是如果特定条件可以满足,父类构造器是可以被自动继承的。在实践中,这意味着对于许多常见场景你不必重写父类的构造器,并且在尽可能安全的情况下以最小的代价继承父类的构造器。 如上所述,子类在默认情况下不会继承父类的构造器。但是如果满足特定条件,父类构造器是可以被自动继承的。在实践中,这意味着对于许多常见场景你不必重写父类的构造器,并且可以在安全的情况下以最小的代价继承父类的构造器。
假设为子类中引入的任意新属性提供默认值,请遵守以下2个规则: 假设为子类中引入的所有新属性提供默认值,以下 2 个规则适用
#### 规则 1 ##### 规则 1
如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。 如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。
#### 规则 2 ##### 规则 2
如果子类提供了所有父类指定构造器的实现--不管是通过规则1继承过来的,还是通过自定义实现的--它将自动继承所有父类的便利构造器。 如果子类提供了所有父类指定构造器的实现——无论是通过规则 1 继承过来的,还是提供了自定义实现——它将自动继承所有父类的便利构造器。(即使属性没有默认值,只要实现了父类的所有指定构造器,就会自动继承父类的所有便利构造器)
即使你在子类中添加了更多的便利构造器,这两条规则仍然适用。 即使你在子类中添加了更多的便利构造器,这两条规则仍然适用。
>注意 > 注意
子类可以通过部分满足规则2的方式使用子类便利构造器来实现父类的指定构造器。 对于规则 2子类可以将父类的指定构造器实现为便利构造器。
<a name="designated_and_convenience_initializers_in_action"></a> <a name="designated_and_convenience_initializers_in_action"></a>
### 指定构造器和便利构造器实 ### 指定构造器和便利构造器实
接下来的例子将在操作中展示指定构造器、便利构造器和自动构造器的继承。定义了包含三个类`Food``RecipeIngredient`以及`ShoppingListItem`的类层次结构,并将演示它们的构造器是如何相互作用的。 接下来的例子将在实践中展示指定构造器、便利构造器以及构造器的自动继承。这个例子定义了包含三个类`Food``RecipeIngredient`以及`ShoppingListItem`的类层次结构,并将演示它们的构造器是如何相互作用的。
类层次中的基类是`Food`,它是一个简单的用来封装食物名字的类。`Food`类引入了一个叫做`name``String`类型属性,并且提供了两个构造器来创建`Food`实例: 类层次中的基类是`Food`,它是一个简单的用来封装食物名字的类。`Food`类引入了一个叫做`name``String`类型属性,并且提供了两个构造器来创建`Food`实例:
```swift ```swift
class Food { class Food {
@ -587,25 +587,25 @@ class Food {
下图中展示了`Food`的构造器链: 下图中展示了`Food`的构造器链:
![Food构造器链](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/initializersExample01_2x.png) ![Food构造器链](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/initializersExample01_2x.png)
类没有提供一个默认的逐一成员构造器,所以`Food`类提供了一个接受单一参数`name`的指定构造器。这个构造器可以使用一个特定的名字来创建新的`Food`实例: 类型没有默认的逐一成员构造器,所以`Food`类提供了一个接受单一参数`name`的指定构造器。这个构造器可以使用一个特定的名字来创建新的`Food`实例:
```swift ```swift
let namedMeat = Food(name: "Bacon") let namedMeat = Food(name: "Bacon")
// namedMeat 的名字是 "Bacon” // namedMeat 的名字是 "Bacon”
``` ```
`Food`类中的构造器`init(name: String)`被定义为一个指定构造器,因为它能确保所有新`Food`实例的存储型属性都被初始化。`Food`类没有父类,所以`init(name: String)`构造器不需要调用`super.init()`来完成构造。 `Food`类中的构造器`init(name: String)`被定义为一个指定构造器,因为它能确保`Food`实例的所有存储型属性都被初始化。`Food`类没有父类,所以`init(name: String)`构造器不需要调用`super.init()`来完成构造过程
`Food`类同样提供了一个没有参数的便利构造器 `init()`。这个`init()`构造器为新食物提供了一个默认的占位名字,通过代理调用同一类中定义的指定构造器`init(name: String)`并给参数`name`传值`[Unnamed]`来实现: `Food`类同样提供了一个没有参数的便利构造器`init()`。这个`init()`构造器为新食物提供了一个默认的占位名字,通过横向代理到指定构造器`init(name: String)`并给参数`name`传值`[Unnamed]`来实现:
```swift ```swift
let mysteryMeat = Food() let mysteryMeat = Food()
// mysteryMeat 的名字是 [Unnamed] // mysteryMeat 的名字是 [Unnamed]
``` ```
类层级中的第二个类是`Food`的子类`RecipeIngredient``RecipeIngredient`类构建了食谱中的一味调味剂。它引入了`Int`类型的数量属性`quantity`(以及从`Food`继承过来的`name`属性),并且定义了两个构造器来创建`RecipeIngredient`实例: 类层级中的第二个类是`Food`的子类`RecipeIngredient``RecipeIngredient`类构建了食谱中的一味调味剂。它引入了`Int`类型的属性`quantity`(以及从`Food`继承过来的`name`属性),并且定义了两个构造器来创建`RecipeIngredient`实例:
```swift ```swift
class RecipeIngredient: Food { class RecipeIngredient: Food {
@ -622,15 +622,17 @@ class RecipeIngredient: Food {
下图中展示了`RecipeIngredient`类的构造器链: 下图中展示了`RecipeIngredient`类的构造器链:
![RecipeIngredient构造器](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/initializersExample02_2x.png) ![RecipeIngredient构造器](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/initializersExample02_2x.png)
`RecipeIngredient`类拥有一个指定构造器`init(name: String, quantity: Int)`,它可以用来产生新`RecipeIngredient`实例的所有属性值。这个构造器一开始先将传入的`quantity`参数赋值给`quantity`属性,这个属性也是唯一在`RecipeIngredient`中新引入的属性。随后,构造器将任务向上代理父类`Food``init(name: String)`。这个过程满足[两段式构造过程](#two_phase_initialization)中的安全检查1。 `RecipeIngredient`类拥有一个指定构造器`init(name: String, quantity: Int)`,它可以用来填充`RecipeIngredient`实例的所有属性值。这个构造器一开始先将传入的`quantity`参数赋值给`quantity`属性,这个属性也是唯一在`RecipeIngredient`中新引入的属性。随后,构造器向上代理父类`Food``init(name: String)`。这个过程满足[两段式构造过程](#two_phase_initialization)中的安全检查 1。
`RecipeIngredient`定义了一个便利构造器`init(name: String)`,它只通过`name`来创建`RecipeIngredient`的实例。这个便利构造器假设任意`RecipeIngredient`实例的`quantity` 1,所以不需要显指明数量即可创建出实例。这个便利构造器的定义可以让创建实例更加方便和快捷,并且避免了使用重复的代码来创建多个`quantity` 1 `RecipeIngredient`实例。这个便利构造器只是简单的将任务代理给了同一类里提供的指定构造器 `RecipeIngredient`定义了一个便利构造器`init(name: String)`,它只通过`name`来创建`RecipeIngredient`的实例。这个便利构造器假设任意`RecipeIngredient`实例的`quantity``1`,所以不需要显指明数量即可创建出实例。这个便利构造器的定义可以更加方便和快捷地创建实例,并且避免了创建多个`quantity``1``RecipeIngredient`实例时的代码重复。这个便利构造器只是简单地横向代理到类中的指定构造器,并为`quantity`参数传递`1`
注意,`RecipeIngredient`的便利构造器`init(name: String)`使用了跟`Food`中指定构造器`init(name: String)`相同的参数。因为这个便利构造器重写父类的指定构造器`init(name: String)`,必须在前面使用使用`override`标识(参见[构造器的继承和重写](#initializer_inheritance_and_overriding))。 注意,`RecipeIngredient`的便利构造器`init(name: String)`使用了跟`Food`中指定构造器`init(name: String)`相同的参数。由于这个便利构造器重写父类的指定构造器`init(name: String)`因此必须在前面使用`override`修饰符(参见[构造器的继承和重写](#initializer_inheritance_and_overriding))。
在这个例子中,`RecipeIngredient`父类`Food`,它有一个便利构造器`init()`。这个构造器因此也被`RecipeIngredient`继承。这个继承的`init()`函数版本跟`Food`提供的版本是一样的,除了它是将任务代理给`RecipeIngredient`版本的`init(name: String)`而不是`Food`提供的版本 尽管`RecipeIngredient`父类的指定构造器重写为了便利构造器,它依然提供了父类的所有指定构造器的实现。因此,`RecipeIngredient`会自动继承父类的所有便利构造器
在这个例子中,`RecipeIngredient`的父类是`Food`,它有一个便利构造器`init()`。这个便利构造器会被`RecipeIngredient`继承。这个继承版本的`init()`在功能上跟`Food`提供的版本是一样的,只是它会代理到`RecipeIngredient`版本的`init(name: String)`而不是`Food`提供的版本。
所有的这三种构造器都可以用来创建新的`RecipeIngredient`实例: 所有的这三种构造器都可以用来创建新的`RecipeIngredient`实例:
@ -642,7 +644,7 @@ let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
类层级中第三个也是最后一个类是`RecipeIngredient`的子类,叫做`ShoppingListItem`。这个类构建了购物单中出现的某一种调味料。 类层级中第三个也是最后一个类是`RecipeIngredient`的子类,叫做`ShoppingListItem`。这个类构建了购物单中出现的某一种调味料。
购物单中的每一项总是从`unpurchased`未购买状态开始的。为了现这一事实,`ShoppingListItem`引入了一个布尔类型的属性`purchased`,它的默认值是`false``ShoppingListItem`还添加了一个计算型属性`description`,它提供了关于`ShoppingListItem`实例的一些文字描述: 购物单中的每一项总是从未购买状态开始的。为了现这一事实,`ShoppingListItem`引入了一个布尔类型的属性`purchased`,它的默认值是`false``ShoppingListItem`还添加了一个计算型属性`description`,它提供了关于`ShoppingListItem`实例的一些文字描述:
```swift ```swift
class ShoppingListItem: RecipeIngredient { class ShoppingListItem: RecipeIngredient {
@ -655,14 +657,14 @@ class ShoppingListItem: RecipeIngredient {
} }
``` ```
> 注意 > 注意
`ShoppingListItem`没有定义构造器来为`purchased`提供初始值,这是因为任何添加到购物单的的初始状态总是未购买。 `ShoppingListItem`没有定义构造器来为`purchased`提供初始值,因为添加到购物单的物品的初始状态总是未购买。
由于它为自己引入的所有属性都提供了默认值,并且自己没有定义任何构造器,`ShoppingListItem`将自动继承所有父类中的指定构造器和便利构造器。 由于它为自己引入的所有属性都提供了默认值,并且自己没有定义任何构造器,`ShoppingListItem`将自动继承所有父类中的指定构造器和便利构造器。
下图展示了所有三个类的构造器链: 下图展示了三个类的构造器链:
![三类构造器图](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/initializersExample03_2x.png) ![三类构造器图](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/initializersExample03_2x.png)
你可以使用全部三个继承来的构造器来创建`ShoppingListItem`的新实例: 你可以使用全部三个继承来的构造器来创建`ShoppingListItem`的新实例:
@ -682,7 +684,7 @@ for item in breakfastList {
// 6 x eggs ✘ // 6 x eggs ✘
``` ```
如上所述,例子中通过字面量方式创建了一个数组`breakfastList`,它包含了三个新的`ShoppingListItem`实例,因此数组的类型也能自动推导为`ShoppingListItem[]`。在数组创建完之后,数组中第一个`ShoppingListItem`实例的名字从`[Unnamed]`改为`Orange juice`,并标记为已购买。接下来通过遍历数组每个元素并打印它们的描述值,展示了所有项当前的默认状态都已按照预期完成了赋值。 如上所述,例子中通过字面量方式创建了一个数组`breakfastList`,它包含了三个`ShoppingListItem`实例,因此数组的类型也能自动推导为`[ShoppingListItem]`。在数组创建完之后,数组中第一个`ShoppingListItem`实例的名字从`[Unnamed]`改为`Orange juice`,并标记为已购买。打印数组每个元素的描述显示了它们都已按照预期赋值。
<a name="failable_initializers"></a> <a name="failable_initializers"></a>
## 可失败构造器 ## 可失败构造器