make gitbook

This commit is contained in:
numbbbbb
2014-06-14 20:51:32 +08:00
parent e00caf63d5
commit 31f083eb83
68 changed files with 1893 additions and 1795 deletions

190
chapter2/14_Initialization.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.14" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.14" data-basepath=".." data-revision="1402750255397">
<div class="book-header">
<!-- Actions Left -->
<a href="#" class="btn pull-left toggle-summary" aria-label="Toggle summary"><i class="fa fa-align-justify"></i></a>
@ -268,7 +268,7 @@
<li class="chapter " data-level="2.12" data-path="chapter2/12_Subscripts.html">
<a href="../chapter2/12_Subscripts.html">
<i class="fa fa-check"></i> <b>2.12.</b> 附属脚本
<i class="fa fa-check"></i> <b>2.12.</b> 下标脚本
</a>
@ -520,7 +520,7 @@
<a href="../chapter2/11_Methods.html" title="方法" class="chapter done " data-progress="2.11" style="left: 18.42105263157895%;"></a>
<a href="../chapter2/12_Subscripts.html" title="附属脚本" class="chapter done " data-progress="2.12" style="left: 21.05263157894737%;"></a>
<a href="../chapter2/12_Subscripts.html" title="下标脚本" class="chapter done " data-progress="2.12" style="left: 21.05263157894737%;"></a>
<a href="../chapter2/13_Inheritance.html" title="继承" class="chapter done " data-progress="2.13" style="left: 23.68421052631579%;"></a>
@ -587,11 +587,10 @@
<div class="page-inner">
<section class="normal" id="section-gitbook_113">
<section class="normal" id="section-gitbook_60">
<blockquote>
<p>翻译lifedim</p>
<p>校对lifedim</p>
<p>翻译lifedim<br>校对lifedim </p>
</blockquote>
<h1 id="-initialization-">构造过程Initialization</h1>
<hr>
@ -612,40 +611,40 @@
<p>类和结构体在实例创建时,必须为所有存储型属性设置合适的初始值。存储型属性的值不能处于一个未知的状态。</p>
<p>你可以在构造器中为存储型属性赋初值,也可以在定义属性时为其设置默认值。以下章节将详细介绍这两种方法。</p>
<blockquote>
<p>注意:</p>
<p>当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观测器(<code>property observers</code>)。</p>
<p>注意:<br>当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观测器(<code>property observers</code>)。</p>
</blockquote>
<h3 id="-">构造器</h3>
<p>构造器在创建某特定类型的新实例时调用。它的最简形式类似于一个不带任何参数的实例方法,以关键字<code>init</code>命名。</p>
<p>下面例子中定义了一个用来保存华氏温度的结构体<code>Fahrenheit</code>,它拥有一个<code>Double</code>类型的存储型属性<code>temperature</code></p>
<pre><code>struct Fahrenheit {
<pre><code class="lang-swift">struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
</code></pre>
<pre><code class="lang-swift">var f = Fahrenheit()
println(&quot;The default temperature is \(f.temperature)° Fahrenheit&quot;)
// 输出 &quot;The default temperature is 32.0° Fahrenheit”
</code></pre><p>这个结构体定义了一个不带参数的构造器<code>init</code>,并在里面将存储型属性<code>temperature</code>的值初始化为<code>32.0</code>(华摄氏度下水的冰点)。</p>
</code></pre>
<p>这个结构体定义了一个不带参数的构造器<code>init</code>,并在里面将存储型属性<code>temperature</code>的值初始化为<code>32.0</code>(华摄氏度下水的冰点)。</p>
<h3 id="-">默认属性值</h3>
<p>如前所述,你可以在构造器中为存储型属性设置初始值;同样,你也可以在属性声明时为其设置默认值。</p>
<blockquote>
<p>注意:</p>
<p>如果一个属性总是使用同一个初始值,可以为其设置一个默认值。无论定义默认值还是在构造器中赋值,最终它们实现的效果是一样的,只不过默认值跟属性构造过程结合的更紧密。使用默认值能让你的构造器更简洁、更清晰,且能通过默认值自动推导出属性的类型;同时,它也能让你充分利用默认构造器、构造器继承(后续章节将讲到)等特性。</p>
<p>注意:<br>如果一个属性总是使用同一个初始值,可以为其设置一个默认值。无论定义默认值还是在构造器中赋值,最终它们实现的效果是一样的,只不过默认值跟属性构造过程结合的更紧密。使用默认值能让你的构造器更简洁、更清晰,且能通过默认值自动推导出属性的类型;同时,它也能让你充分利用默认构造器、构造器继承(后续章节将讲到)等特性。</p>
</blockquote>
<p>你可以使用更简单的方式在定义结构体<code>Fahrenheit</code>时为属性<code>temperature</code>设置默认值:</p>
<pre><code>struct Fahrenheit {
<pre><code class="lang-swift">struct Fahrenheit {
var temperature = 32.0
}
</code></pre><p><a name="customizing_initialization"></a></p>
</code></pre>
<p><a name="customizing_initialization"></a></p>
<h2 id="-">定制化构造过程</h2>
<p>你可以通过输入参数和可选属性类型来定制构造过程,也可以在构造过程中修改常量属性。这些都将在后面章节中提到。</p>
<h3 id="-">构造参数</h3>
<p>你可以在定义构造器时提供构造参数,为其提供定制化构造所需值的类型和名字。构造器参数的功能和语法跟函数和方法参数相同。</p>
<p>下面例子中定义了一个包含摄氏度温度的结构体<code>Celsius</code>。它定义了两个不同的构造器:<code>init(fromFahrenheit:)</code><code>init(fromKelvin:)</code>,二者分别通过接受不同刻度表示的温度值来创建新的实例:</p>
<pre><code>struct Celsius {
<pre><code class="lang-swift">struct Celsius {
var temperatureInCelsius: Double = 0.0
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
@ -654,22 +653,22 @@ println(&quot;The default temperature is \(f.temperature)° Fahrenheit&quot;)
temperatureInCelsius = kelvin - 273.15
}
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
</code></pre>
<pre><code class="lang-swift">let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
// boilingPointOfWater.temperatureInCelsius 是 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius 是 0.0”
</code></pre><p>第一个构造器拥有一个构造参数,其外部名字为<code>fromFahrenheit</code>,内部名字为<code>fahrenheit</code>;第二个构造器也拥有一个构造参数,其外部名字为<code>fromKelvin</code>,内部名字为<code>kelvin</code>。这两个构造器都将唯一的参数值转换成摄氏温度值,并保存在属性<code>temperatureInCelsius</code>中。</p>
</code></pre>
<p>第一个构造器拥有一个构造参数,其外部名字为<code>fromFahrenheit</code>,内部名字为<code>fahrenheit</code>;第二个构造器也拥有一个构造参数,其外部名字为<code>fromKelvin</code>,内部名字为<code>kelvin</code>。这两个构造器都将唯一的参数值转换成摄氏温度值,并保存在属性<code>temperatureInCelsius</code>中。</p>
<h3 id="-">内部和外部参数名</h3>
<p>跟函数和方法参数相同,构造参数也存在一个在构造器内部使用的参数名字和一个在调用构造器时使用的外部参数名字。</p>
<p>然而构造器并不像函数和方法那样在括号前有一个可辨别的名字。所以在调用构造器时主要通过构造器中的参数名和类型来确定需要调用的构造器。正因为参数如此重要如果你在定义构造器时没有提供参数的外部名字Swift 会为每个构造器的参数自动生成一个跟内部名字相同的外部名,就相当于在每个构造参数之前加了一个哈希符号。</p>
<blockquote>
<p>注意:</p>
<p>如果你不希望为构造器的某个参数提供外部名字,你可以使用下划线<code>_</code>来显示描述它的外部名,以此覆盖上面所说的默认行为。</p>
<p>注意:<br>如果你不希望为构造器的某个参数提供外部名字,你可以使用下划线<code>_</code>来显示描述它的外部名,以此覆盖上面所说的默认行为。</p>
</blockquote>
<p>以下例子中定义了一个结构体<code>Color</code>,它包含了三个常量:<code>red</code><code>green</code><code>blue</code>。这些属性可以存储0.0到1.0之间的值,用来指示颜色中红、绿、蓝成分的含量。</p>
<p><code>Color</code>提供了一个构造器,其中包含三个<code>Double</code>类型的构造参数:</p>
<pre><code>struct Color {
<pre><code class="lang-swift">struct Color {
let red = 0.0, green = 0.0, blue = 0.0
init(red: Double, green: Double, blue: Double) {
self.red = red
@ -677,15 +676,18 @@ let freezingPointOfWater = Celsius(fromKelvin: 273.15)
self.blue = blue
}
}
</code></pre><p>每当你创建一个新的<code>Color</code>实例,你都需要通过三种颜色的外部参数名来传值,并调用构造器。</p>
<pre><code>let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
</code></pre><p>注意,如果不通过外部参数名字传值,你是没法调用这个构造器的。只要构造器定义了某个外部参数名,你就必须使用它,忽略它将导致编译错误:</p>
<pre><code>let veryGreen = Color(0.0, 1.0, 0.0)
</code></pre>
<p>每当你创建一个新的<code>Color</code>实例,你都需要通过三种颜色的外部参数名来传值,并调用构造器。</p>
<pre><code class="lang-swift">let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
</code></pre>
<p>注意,如果不通过外部参数名字传值,你是没法调用这个构造器的。只要构造器定义了某个外部参数名,你就必须使用它,忽略它将导致编译错误:</p>
<pre><code class="lang-swift">let veryGreen = Color(0.0, 1.0, 0.0)
// 报编译时错误,需要外部名称
</code></pre><h3 id="-">可选属性类型</h3>
</code></pre>
<h3 id="-">可选属性类型</h3>
<p>如果你定制的类型包含一个逻辑上允许取值为空的存储型属性--不管是因为它无法在初始化时赋值,还是因为它可以在之后某个时间点可以赋值为空--你都需要将它定义为可选类型<code>optional type</code>。可选类型的属性将自动初始化为空<code>nil</code>,表示这个属性是故意在初始化时设置为空的。</p>
<p>下面例子中定义了类<code>SurveyQuestion</code>,它包含一个可选字符串属性<code>response</code></p>
<pre><code>class SurveyQuestion {
<pre><code class="lang-swift">class SurveyQuestion {
var text: String
var response: String?
init(text: String) {
@ -699,15 +701,15 @@ let cheeseQuestion = SurveyQuestion(text: &quot;Do you like cheese?&quot;)
cheeseQuestion.ask()
// 输出 &quot;Do you like cheese?&quot;
cheeseQuestion.response = &quot;Yes, I do like cheese.
</code></pre><p>调查问题在问题提出之后,我们才能得到回答。所以我们将属性回答<code>response</code>声明为<code>String?</code>类型,或者说是可选字符串类型<code>optional String</code>。当<code>SurveyQuestion</code>实例化时,它将自动赋值为空<code>nil</code>,表明暂时还不存在此字符串。</p>
</code></pre>
<p>调查问题在问题提出之后,我们才能得到回答。所以我们将属性回答<code>response</code>声明为<code>String?</code>类型,或者说是可选字符串类型<code>optional String</code>。当<code>SurveyQuestion</code>实例化时,它将自动赋值为空<code>nil</code>,表明暂时还不存在此字符串。</p>
<h3 id="-">构造过程中常量属性的修改</h3>
<p>只要在构造过程结束前常量的值能确定,你可以在构造过程中的任意时间点修改常量属性的值。</p>
<blockquote>
<p>注意:</p>
<p>对某个类实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。</p>
<p>注意:<br>对某个类实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。</p>
</blockquote>
<p>你可以修改上面的<code>SurveyQuestion</code>示例,用常量属性替代变量属性<code>text</code>,指明问题内容<code>text</code>在其创建之后不会再被修改。尽管<code>text</code>属性现在是常量,我们仍然可以在其类的构造器中修改它的值:</p>
<pre><code>class SurveyQuestion {
<pre><code class="lang-swift">class SurveyQuestion {
let text: String
var response: String?
init(text: String) {
@ -721,45 +723,48 @@ let beetsQuestion = SurveyQuestion(text: &quot;How about beets?&quot;)
beetsQuestion.ask()
// 输出 &quot;How about beets?&quot;
beetsQuestion.response = &quot;I also like beets. (But not with cheese.)
</code></pre><p><a name="default_initializers"></a></p>
</code></pre>
<p><a name="default_initializers"></a></p>
<h2 id="-">默认构造器</h2>
<p>Swift 将为所有属性已提供默认值的且自身没有定义任何构造器的结构体或基类,提供一个默认的构造器。这个默认构造器将简单的创建一个所有属性值都设置为默认值的实例。</p>
<p>下面例子中创建了一个类<code>ShoppingListItem</code>,它封装了购物清单中的某一项的属性:名字(<code>name</code>)、数量(<code>quantity</code>)和购买状态 <code>purchase state</code></p>
<pre><code>class ShoppingListItem {
<pre><code class="lang-swift">class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
</code></pre><p>由于<code>ShoppingListItem</code>类中的所有属性都有默认值,且它是没有父类的基类,它将自动获得一个可以为所有属性设置默认值的默认构造器(尽管代码中没有显式为<code>name</code>属性设置默认值,但由于<code>name</code>是可选字符串类型,它将默认设置为<code>nil</code>)。上面例子中使用默认构造器创造了一个<code>ShoppingListItem</code>类的实例(使用<code>ShoppingListItem()</code>形式的构造器语法),并将其赋值给变量<code>item</code></p>
</code></pre>
<p>由于<code>ShoppingListItem</code>类中的所有属性都有默认值,且它是没有父类的基类,它将自动获得一个可以为所有属性设置默认值的默认构造器(尽管代码中没有显式为<code>name</code>属性设置默认值,但由于<code>name</code>是可选字符串类型,它将默认设置为<code>nil</code>)。上面例子中使用默认构造器创造了一个<code>ShoppingListItem</code>类的实例(使用<code>ShoppingListItem()</code>形式的构造器语法),并将其赋值给变量<code>item</code></p>
<h3 id="-">结构体的逐一成员构造器</h3>
<p>除上面提到的默认构造器,如果结构体对所有存储型属性提供了默认值且自身没有提供定制的构造器,它们能自动获得一个逐一成员构造器。</p>
<p>逐一成员构造器是用来初始化结构体新实例里成员属性的快捷方法。我们在调用逐一成员构造器时,通过与成员属性名相同的参数名进行传值来完成对成员属性的初始赋值。</p>
<p>下面例子中定义了一个结构体<code>Size</code>,它包含两个属性<code>width</code><code>height</code>。Swift 可以根据这两个属性的初始赋值<code>0.0</code>自动推导出它们的类型<code>Double</code></p>
<p>由于这两个存储型属性都有默认值,结构体<code>Size</code>自动获得了一个逐一成员构造器 <code>init(width:height:)</code>。 你可以用它来为<code>Size</code>创建新的实例:</p>
<pre><code>struct Size {
<pre><code class="lang-swift">struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
</code></pre><p><a name="initializer_delegation_for_value_types"></a></p>
</code></pre>
<p><a name="initializer_delegation_for_value_types"></a></p>
<h2 id="-">值类型的构造器代理</h2>
<p>构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能减少多个构造器间的代码重复。</p>
<p>构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理任务给本身提供的其它构造器。类则不同,它可以继承自其它类(请参考<a href="../chapter2/13_Inheritance.html">继承</a>),这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。这些责任将在后续章节<a href="#class_inheritance_and_initialization">类的继承和构造过程</a>中介绍。</p>
<p>对于值类型,你可以使用<code>self.init</code>在自定义的构造器中引用其它的属于相同值类型的构造器。并且你只能在构造器内部调用<code>self.init</code></p>
<p>注意,如果你为某个值类型定义了一个定制的构造器,你将无法访问到默认构造器(如果是结构体,则无法访问逐一对象构造器)。这个限制可以防止你在为值类型定义了一个更复杂的,完成了重要准备构造器之后,别人还是错误的使用了那个自动生成的构造器。</p>
<blockquote>
<p>注意:</p>
<p>假如你想通过默认构造器、逐一对象构造器以及你自己定制的构造器为值类型创建实例,我们建议你将自己定制的构造器写到扩展(<code>extension</code>)中,而不是跟值类型定义混在一起。想查看更多内容,请查看<a href="../chapter2/20_Extensions.html">扩展</a>章节。</p>
<p>注意:<br>假如你想通过默认构造器、逐一对象构造器以及你自己定制的构造器为值类型创建实例,我们建议你将自己定制的构造器写到扩展(<code>extension</code>)中,而不是跟值类型定义混在一起。想查看更多内容,请查看<a href="../chapter2/20_Extensions.html">扩展</a>章节。</p>
</blockquote>
<p>下面例子将定义一个结构体<code>Rect</code>,用来展现几何矩形。这个例子需要两个辅助的结构体<code>Size</code><code>Point</code>,它们各自为其所有的属性提供了初始值<code>0.0</code></p>
<pre><code>struct Size {
<pre><code class="lang-swift">struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
</code></pre><p>你可以通过以下三种方式为<code>Rect</code>创建实例--使用默认的0值来初始化<code>origin</code><code>size</code>属性;使用特定的<code>origin</code><code>size</code>实例来初始化;使用特定的<code>center</code><code>size</code>来初始化。在下面<code>Rect</code>结构体定义中,我们为着三种方式提供了三个自定义的构造器:</p>
<pre><code>struct Rect {
</code></pre>
<p>你可以通过以下三种方式为<code>Rect</code>创建实例--使用默认的0值来初始化<code>origin</code><code>size</code>属性;使用特定的<code>origin</code><code>size</code>实例来初始化;使用特定的<code>center</code><code>size</code>来初始化。在下面<code>Rect</code>结构体定义中,我们为着三种方式提供了三个自定义的构造器:</p>
<pre><code class="lang-swift">struct Rect {
var origin = Point()
var size = Size()
init() {}
@ -773,21 +778,24 @@ struct Point {
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
</code></pre><p>第一个<code>Rect</code>构造器<code>init()</code>,在功能上跟没有自定义构造器时自动获得的默认构造器是一样的。这个构造器是一个空函数,使用一对大括号<code>{}</code>来描述,它没有执行任何定制的构造过程。调用这个构造器将返回一个<code>Rect</code>实例,它的<code>origin</code><code>size</code>属性都使用定义时的默认值<code>Point(x: 0.0, y: 0.0)</code><code>Size(width: 0.0, height: 0.0)</code></p>
<pre><code>let basicRect = Rect()
</code></pre>
<p>第一个<code>Rect</code>构造器<code>init()</code>,在功能上跟没有自定义构造器时自动获得的默认构造器是一样的。这个构造器是一个空函数,使用一对大括号<code>{}</code>来描述,它没有执行任何定制的构造过程。调用这个构造器将返回一个<code>Rect</code>实例,它的<code>origin</code><code>size</code>属性都使用定义时的默认值<code>Point(x: 0.0, y: 0.0)</code><code>Size(width: 0.0, height: 0.0)</code></p>
<pre><code class="lang-swift">let basicRect = Rect()
// basicRect 的原点是 (0.0, 0.0),尺寸是 (0.0, 0.0)
</code></pre><p>第二个<code>Rect</code>构造器<code>init(origin:size:)</code>,在功能上跟结构体在没有自定义构造器时获得的逐一成员构造器是一样的。这个构造器只是简单的将<code>origin</code><code>size</code>的参数值赋给对应的存储型属性:</p>
<pre><code>let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
</code></pre>
<p>第二个<code>Rect</code>构造器<code>init(origin:size:)</code>,在功能上跟结构体在没有自定义构造器时获得的逐一成员构造器是一样的。这个构造器只是简单的将<code>origin</code><code>size</code>的参数值赋给对应的存储型属性:</p>
<pre><code class="lang-swift">let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
// originRect 的原点是 (2.0, 2.0),尺寸是 (5.0, 5.0)
</code></pre><p>第三个<code>Rect</code>构造器<code>init(center:size:)</code>稍微复杂一点。它先通过<code>center</code><code>size</code>的值计算出<code>origin</code>的坐标。然后再调用(或代理给)<code>init(origin:size:)</code>构造器来将新的<code>origin</code><code>size</code>值赋值到对应的属性中:</p>
<p>let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0))
// centerRect 的原点是 (2.5, 2.5),尺寸是 (3.0, 3.0)</p>
</code></pre>
<p>第三个<code>Rect</code>构造器<code>init(center:size:)</code>稍微复杂一点。它先通过<code>center</code><code>size</code>的值计算出<code>origin</code>的坐标。然后再调用(或代理给)<code>init(origin:size:)</code>构造器来将新的<code>origin</code><code>size</code>值赋值到对应的属性中:</p>
<p>let centerRect = Rect(center: Point(x: 4.0, y: 4.0),</p>
<pre><code class="lang-swift">size: Size(width: 3.0, height: 3.0))
</code></pre>
<p>// centerRect 的原点是 (2.5, 2.5),尺寸是 (3.0, 3.0)</p>
<p>构造器<code>init(center:size:)</code>可以自己将<code>origin</code><code>size</code>的新值赋值到对应的属性中。然而尽量利用现有的构造器和它所提供的功能来实现<code>init(center:size:)</code>的功能,是更方便、更清晰和更直观的方法。</p>
<blockquote>
<p>注意:</p>
<p>如果你想用另外一种不需要自己定义<code>init()</code><code>init(origin:size:)</code>的方式来实现这个例子,请参考<a href="../chapter2/20_Extensions.html">扩展</a></p>
<p>注意:<br>如果你想用另外一种不需要自己定义<code>init()</code><code>init(origin:size:)</code>的方式来实现这个例子,请参考<a href="../chapter2/20_Extensions.html">扩展</a></p>
</blockquote>
<p><a name="class_inheritance_and_initialization"></a></p>
<h2 id="-">类的继承和构造过程</h2>
@ -817,8 +825,7 @@ struct Point {
<p>如图所示父类中包含一个指定构造器和两个便利构造器。其中一个便利构造器调用了另外一个便利构造器而后者又调用了唯一的指定构造器。这满足了上面提到的规则2和3。这个父类没有自己的父类所以规则1没有用到。</p>
<p>子类中包含两个指定构造器和一个便利构造器。便利构造器必须调用两个指定构造器中的任意一个因为它只能调用同一个类里的其他构造器。这满足了上面提到的规则2和3。而两个指定构造器必须调用父类中唯一的指定构造器这满足了规则1。</p>
<blockquote>
<p>注意:</p>
<p>这些规则不会影响使用时,如何用类去创建实例。任何上图中展示的构造器都可以用来完整创建对应类的实例。这些规则只在实现类的定义时有影响。</p>
<p>注意:<br>这些规则不会影响使用时,如何用类去创建实例。任何上图中展示的构造器都可以用来完整创建对应类的实例。这些规则只在实现类的定义时有影响。</p>
</blockquote>
<p>下面图例中展示了一种更复杂的类层级结构。它演示了指定构造器是如果在类层级中充当“管道”的作用,在类的构造器链上简化了类之间的内部关系。</p>
<p><img src="https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/initializerDelegation02_2x.png" alt="复杂构造器代理图"></p>
@ -827,8 +834,7 @@ struct Point {
<p>Swift 中类的构造过程包含两个阶段。第一个阶段,每个存储型属性通过引入它们的类的构造器来设置初始值。当每一个存储型属性值被确定后,第二阶段开始,它给每个类一次机会在新实例准备使用之前进一步定制它们的存储型属性。</p>
<p>两段式构造过程的使用让构造过程更安全,同时在整个类层级结构中给予了每个类完全的灵活性。两段式构造过程可以防止属性值在初始化之前被访问;也可以防止属性被另外一个构造器意外地赋予不同的值。</p>
<blockquote>
<p>注意:</p>
<p>Swift的两段式构造过程跟 Objective-C 中的构造过程类似。最主要的区别在于阶段 1Objective-C 给每一个属性赋值<code>0</code>或空值(比如说<code>0</code><code>nil</code>。Swift 的构造流程则更加灵活,它允许你设置定制的初始值,并自如应对某些属性不能以<code>0</code><code>nil</code>作为合法默认值的情况。</p>
<p>注意:<br>Swift的两段式构造过程跟 Objective-C 中的构造过程类似。最主要的区别在于阶段 1Objective-C 给每一个属性赋值<code>0</code>或空值(比如说<code>0</code><code>nil</code>。Swift 的构造流程则更加灵活,它允许你设置定制的初始值,并自如应对某些属性不能以<code>0</code><code>nil</code>作为合法默认值的情况。</p>
</blockquote>
<p>Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程能顺利完成:</p>
<h4 id="-1">安全检查 1</h4>
@ -873,8 +879,7 @@ struct Point {
<p>如果你重载的构造器是一个指定构造器,你可以在子类里重载它的实现,并在自定义版本的构造器中调用父类版本的构造器。</p>
<p>如果你重载的构造器是一个便利构造器,你的重载过程必须通过调用同一类中提供的其它指定构造器来实现。这一规则的详细内容请参考<a href="#initialization_chain">构造器链</a></p>
<blockquote>
<p>注意:</p>
<p>与方法、属性和下标不同,在重载构造器时你没有必要使用关键字<code>override</code></p>
<p>注意:<br>与方法、属性和下标不同,在重载构造器时你没有必要使用关键字<code>override</code></p>
</blockquote>
<p><a name="automatic_initializer_inheritance"></a></p>
<h3 id="-">自动构造器的继承</h3>
@ -886,22 +891,23 @@ struct Point {
<p>如果子类提供了所有父类指定构造器的实现--不管是通过规则1继承过来的还是通过自定义实现的--它将自动继承所有父类的便利构造器。</p>
<p>即使你在子类中添加了更多的便利构造器,这两条规则仍然适用。</p>
<blockquote>
<p>注意:</p>
<p>子类可以通过部分满足规则2的方式使用子类便利构造器来实现父类的指定构造器。</p>
<p>注意:<br>子类可以通过部分满足规则2的方式使用子类便利构造器来实现父类的指定构造器。</p>
</blockquote>
<h3 id="-">指定构造器和便利构造器的语法</h3>
<p>类的指定构造器的写法跟值类型简单构造器一样:</p>
<pre><code>init(parameters) {
<pre><code class="lang-swift">init(parameters) {
statements
}
</code></pre><p>便利构造器也采用相同样式的写法,但需要在<code>init</code>关键字之前放置<code>convenience</code>关键字,并使用空格将它们俩分开:</p>
<pre><code>convenience init(parameters) {
</code></pre>
<p>便利构造器也采用相同样式的写法,但需要在<code>init</code>关键字之前放置<code>convenience</code>关键字,并使用空格将它们俩分开:</p>
<pre><code class="lang-swift">convenience init(parameters) {
statements
}
</code></pre><h3 id="-">指定构造器和便利构造器实战</h3>
</code></pre>
<h3 id="-">指定构造器和便利构造器实战</h3>
<p>接下来的例子将在实战中展示指定构造器、便利构造器和自动构造器的继承。它定义了包含三个类<code>Food</code><code>RecipeIngredient</code>以及<code>ShoppingListItem</code>的类层次结构,并将演示它们的构造器是如何相互作用的。</p>
<p>类层次中的基类是<code>Food</code>,它是一个简单的用来封装食物名字的类。<code>Food</code>类引入了一个叫做<code>name</code><code>String</code>类型属性,并且提供了两个构造器来创建<code>Food</code>实例:</p>
<pre><code>class Food {
<pre><code class="lang-swift">class Food {
var name: String
init(name: String) {
self.name = name
@ -910,17 +916,20 @@ struct Point {
self.init(name: &quot;[Unnamed]&quot;)
}
}
</code></pre><p>下图中展示了<code>Food</code>的构造器链:</p>
</code></pre>
<p>下图中展示了<code>Food</code>的构造器链:</p>
<p><img src="https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/initializersExample01_2x.png" alt="Food构造器链"></p>
<p>类没有提供一个默认的逐一成员构造器,所以<code>Food</code>类提供了一个接受单一参数<code>name</code>的指定构造器。这个构造器可以使用一个特定的名字来创建新的<code>Food</code>实例:</p>
<pre><code>let namedMeat = Food(name: &quot;Bacon&quot;)
<pre><code class="lang-swift">let namedMeat = Food(name: &quot;Bacon&quot;)
// namedMeat 的名字是 &quot;Bacon”
</code></pre><p><code>Food</code>类中的构造器<code>init(name: String)</code>被定义为一个指定构造器,因为它能确保所有新<code>Food</code>实例的中存储型属性都被初始化。<code>Food</code>类没有父类,所以<code>init(name: String)</code>构造器不需要调用<code>super.init()</code>来完成构造。</p>
</code></pre>
<p><code>Food</code>类中的构造器<code>init(name: String)</code>被定义为一个指定构造器,因为它能确保所有新<code>Food</code>实例的中存储型属性都被初始化。<code>Food</code>类没有父类,所以<code>init(name: String)</code>构造器不需要调用<code>super.init()</code>来完成构造。</p>
<p><code>Food</code>类同样提供了一个没有参数的便利构造器 <code>init()</code>。这个<code>init()</code>构造器为新食物提供了一个默认的占位名字,通过代理调用同一类中定义的指定构造器<code>init(name: String)</code>并给参数<code>name</code>传值<code>[Unnamed]</code>来实现:</p>
<pre><code>let mysteryMeat = Food()
<pre><code class="lang-swift">let mysteryMeat = Food()
// mysteryMeat 的名字是 [Unnamed]
</code></pre><p>类层级中的第二个类是<code>Food</code>的子类<code>RecipeIngredient</code><code>RecipeIngredient</code>类构建了食谱中的一味调味剂。它引入了<code>Int</code>类型的数量属性<code>quantity</code>(以及从<code>Food</code>继承过来的<code>name</code>属性),并且定义了两个构造器来创建<code>RecipeIngredient</code>实例:</p>
<pre><code>class RecipeIngredient: Food {
</code></pre>
<p>类层级中的第二个类是<code>Food</code>的子类<code>RecipeIngredient</code><code>RecipeIngredient</code>类构建了食谱中的一味调味剂。它引入了<code>Int</code>类型的数量属性<code>quantity</code>(以及从<code>Food</code>继承过来的<code>name</code>属性),并且定义了两个构造器来创建<code>RecipeIngredient</code>实例:</p>
<pre><code class="lang-swift">class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
@ -930,19 +939,21 @@ struct Point {
self.init(name: name, quantity: 1)
}
}
</code></pre><p>下图中展示了<code>RecipeIngredient</code>类的构造器链:</p>
</code></pre>
<p>下图中展示了<code>RecipeIngredient</code>类的构造器链:</p>
<p><img src="https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/initializersExample02_2x.png" alt="RecipeIngredient构造器"></p>
<p><code>RecipeIngredient</code>类拥有一个指定构造器<code>init(name: String, quantity: Int)</code>,它可以用来产生新<code>RecipeIngredient</code>实例的所有属性值。这个构造器一开始先将传入的<code>quantity</code>参数赋值给<code>quantity</code>属性,这个属性也是唯一在<code>RecipeIngredient</code>中新引入的属性。随后,构造器将任务向上代理给父类<code>Food</code><code>init(name: String)</code>。这个过程满足<a href="#two_phase_initialization">两段式构造过程</a>中的安全检查1。</p>
<p><code>RecipeIngredient</code>也定义了一个便利构造器<code>init(name: String)</code>,它只通过<code>name</code>来创建<code>RecipeIngredient</code>的实例。这个便利构造器假设任意<code>RecipeIngredient</code>实例的<code>quantity</code>为1所以不需要显示指明数量即可创建出实例。这个便利构造器的定义可以让创建实例更加方便和快捷并且避免了使用重复的代码来创建多个<code>quantity</code>为 1 的<code>RecipeIngredient</code>实例。这个便利构造器只是简单的将任务代理给了同一类里提供的指定构造器。</p>
<p>注意,<code>RecipeIngredient</code>的便利构造器<code>init(name: String)</code>使用了跟<code>Food</code>中指定构造器<code>init(name: String)</code>相同的参数。尽管<code>RecipeIngredient</code>这个构造器是便利构造器,<code>RecipeIngredient</code>依然提供了对所有父类指定构造器的实现。因此,<code>RecipeIngredient</code>也能自动继承了所有父类的便利构造器。</p>
<p>在这个例子中,<code>RecipeIngredient</code>的父类是<code>Food</code>,它有一个便利构造器<code>init()</code>。这个构造器因此也被<code>RecipeIngredient</code>继承。这个继承的<code>init()</code>函数版本跟<code>Food</code>提供的版本是一样的,除了它是将任务代理给<code>RecipeIngredient</code>版本的<code>init(name: String)</code>而不是<code>Food</code>提供的版本。</p>
<p>所有的这三种构造器都可以用来创建新的<code>RecipeIngredient</code>实例:</p>
<pre><code>let oneMysteryItem = RecipeIngredient()
<pre><code class="lang-swift">let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: &quot;Bacon&quot;)
let sixEggs = RecipeIngredient(name: &quot;Eggs&quot;, quantity: 6)
</code></pre><p>类层级中第三个也是最后一个类是<code>RecipeIngredient</code>的子类,叫做<code>ShoppingListItem</code>。这个类构建了购物单中出现的某一种调味料。</p>
</code></pre>
<p>类层级中第三个也是最后一个类是<code>RecipeIngredient</code>的子类,叫做<code>ShoppingListItem</code>。这个类构建了购物单中出现的某一种调味料。</p>
<p>购物单中的每一项总是从<code>unpurchased</code>未购买状态开始的。为了展现这一事实,<code>ShoppingListItem</code>引入了一个布尔类型的属性<code>purchased</code>,它的默认值是<code>false</code><code>ShoppingListItem</code>还添加了一个计算型属性<code>description</code>,它提供了关于<code>ShoppingListItem</code>实例的一些文字描述:</p>
<pre><code>class ShoppingListItem: RecipeIngredient {
<pre><code class="lang-swift">class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = &quot;\(quantity) x \(name.lowercaseString)&quot;
@ -950,15 +961,15 @@ let sixEggs = RecipeIngredient(name: &quot;Eggs&quot;, quantity: 6)
return output
}
}
</code></pre><blockquote>
<p>注意:</p>
<p><code>ShoppingListItem</code>没有定义构造器来为<code>purchased</code>提供初始化值,这是因为任何添加到购物单的项的初始状态总是未购买。</p>
</code></pre>
<blockquote>
<p>注意:<br><code>ShoppingListItem</code>没有定义构造器来为<code>purchased</code>提供初始化值,这是因为任何添加到购物单的项的初始状态总是未购买。</p>
</blockquote>
<p>由于它为自己引入的所有属性都提供了默认值,并且自己没有定义任何构造器,<code>ShoppingListItem</code>将自动继承所有父类中的指定构造器和便利构造器。</p>
<p>下图种展示了所有三个类的构造器链:</p>
<p><img src="https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/initializersExample03_2x.png" alt="三类构造器图"></p>
<p>你可以使用全部三个继承来的构造器来创建<code>ShoppingListItem</code>的新实例:</p>
<pre><code>var breakfastList = [
<pre><code class="lang-swift">var breakfastList = [
ShoppingListItem(),
ShoppingListItem(name: &quot;Bacon&quot;),
ShoppingListItem(name: &quot;Eggs&quot;, quantity: 6),
@ -971,29 +982,30 @@ for item in breakfastList {
// 1 x orange juice ✔
// 1 x bacon ✘
// 6 x eggs ✘
</code></pre><p>如上所述,例子中通过字面量方式创建了一个新数组<code>breakfastList</code>,它包含了三个新的<code>ShoppingListItem</code>实例,因此数组的类型也能自动推导为<code>ShoppingListItem[]</code>。在数组创建完之后,数组中第一个<code>ShoppingListItem</code>实例的名字从<code>[Unnamed]</code>修改为<code>Orange juice</code>,并标记为已购买。接下来通过遍历数组每个元素并打印它们的描述值,展示了所有项当前的默认状态都已按照预期完成了赋值。</p>
</code></pre>
<p>如上所述,例子中通过字面量方式创建了一个新数组<code>breakfastList</code>,它包含了三个新的<code>ShoppingListItem</code>实例,因此数组的类型也能自动推导为<code>ShoppingListItem[]</code>。在数组创建完之后,数组中第一个<code>ShoppingListItem</code>实例的名字从<code>[Unnamed]</code>修改为<code>Orange juice</code>,并标记为已购买。接下来通过遍历数组每个元素并打印它们的描述值,展示了所有项当前的默认状态都已按照预期完成了赋值。</p>
<p><a name="setting_a_default_property_value_with_a_closure_or_function"></a></p>
<h2 id="-">通过闭包和函数来设置属性的默认值</h2>
<p>如果某个存储型属性的默认值需要特别的定制或准备,你就可以使用闭包或全局函数来为其属性提供定制的默认值。每当某个属性所属的新类型实例创建时,对应的闭包或函数会被调用,而它们的返回值会当做默认值赋值给这个属性。</p>
<p>这种类型的闭包或函数一般会创建一个跟属性类型相同的临时变量,然后修改它的值以满足预期的初始状态,最后将这个临时变量的值作为属性的默认值进行返回。</p>
<p>下面列举了闭包如何提供默认值的代码概要:</p>
<pre><code>class SomeClass {
<pre><code class="lang-swift">class SomeClass {
let someProperty: SomeType = {
// 在这个闭包中给 someProperty 创建一个默认值
// someValue 必须和 SomeType 类型相同
return someValue
}()
}
</code></pre><p>注意闭包结尾的大括号后面接了一对空的小括号。这是用来告诉 Swift 需要立刻执行此闭包。如果你忽略了这对括号,相当于是将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性。</p>
</code></pre>
<p>注意闭包结尾的大括号后面接了一对空的小括号。这是用来告诉 Swift 需要立刻执行此闭包。如果你忽略了这对括号,相当于是将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性。</p>
<blockquote>
<p>注意:</p>
<p>如果你使用闭包来初始化属性的值,请记住在闭包执行时,实例的其它部分都还没有初始化。这意味着你不能够在闭包里访问其它的属性,就算这个属性有默认值也不允许。同样,你也不能使用隐式的<code>self</code>属性,或者调用其它的实例方法。</p>
<p>注意:<br>如果你使用闭包来初始化属性的值,请记住在闭包执行时,实例的其它部分都还没有初始化。这意味着你不能够在闭包里访问其它的属性,就算这个属性有默认值也不允许。同样,你也不能使用隐式的<code>self</code>属性,或者调用其它的实例方法。</p>
</blockquote>
<p>下面例子中定义了一个结构体<code>Checkerboard</code>,它构建了西洋跳棋游戏的棋盘:</p>
<p><img src="https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Art/checkersBoard_2x.png" alt="西洋跳棋棋盘"></p>
<p>西洋跳棋游戏在一副黑白格交替的 10x10 的棋盘中进行。为了呈现这副游戏棋盘,<code>Checkerboard</code>结构体定义了一个属性<code>boardColors</code>,它是一个包含 100 个布尔值的数组。数组中的某元素布尔值为<code>true</code>表示对应的是一个黑格,布尔值为<code>false</code>表示对应的是一个白格。数组中第一个元素代表棋盘上左上角的格子,最后一个元素代表棋盘上右下角的格子。</p>
<p><code>boardColor</code>数组是通过一个闭包来初始化和组装颜色值的:</p>
<pre><code>struct Checkerboard {
<pre><code class="lang-swift">struct Checkerboard {
let boardColors: Bool[] = {
var temporaryBoard = Bool[]()
var isBlack = false
@ -1010,13 +1022,15 @@ for item in breakfastList {
return boardColors[(row * 10) + column]
}
}
</code></pre><p>每当一个新的<code>Checkerboard</code>实例创建时,对应的赋值闭包会执行,一系列颜色值会被计算出来作为默认值赋值给<code>boardColors</code>。上面例子中描述的闭包将计算出棋盘中每个格子合适的颜色,将这些颜色值保存到一个临时数组<code>temporaryBoard</code>中,并在构建完成时将此数组作为闭包返回值返回。这个返回的值将保存到<code>boardColors</code>中,并可以通<code>squareIsBlackAtRow</code>这个工具函数来查询。</p>
<pre><code>let board = Checkerboard()
</code></pre>
<p>每当一个新的<code>Checkerboard</code>实例创建时,对应的赋值闭包会执行,一系列颜色值会被计算出来作为默认值赋值给<code>boardColors</code>。上面例子中描述的闭包将计算出棋盘中每个格子合适的颜色,将这些颜色值保存到一个临时数组<code>temporaryBoard</code>中,并在构建完成时将此数组作为闭包返回值返回。这个返回的值将保存到<code>boardColors</code>中,并可以通<code>squareIsBlackAtRow</code>这个工具函数来查询。</p>
<pre><code class="lang-swift">let board = Checkerboard()
println(board.squareIsBlackAtRow(0, column: 1))
// 输出 &quot;true&quot;
println(board.squareIsBlackAtRow(9, column: 9))
// 输出 &quot;false&quot;
</code></pre>
</section>