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

357
chapter2/01_The_Basics.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.1" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.1" 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 " data-progress="2.11" style="left: 18.42105263157895%;"></a>
<a href="../chapter2/12_Subscripts.html" title="附属脚本" class="chapter " data-progress="2.12" style="left: 21.05263157894737%;"></a>
<a href="../chapter2/12_Subscripts.html" title="下标脚本" class="chapter " data-progress="2.12" style="left: 21.05263157894737%;"></a>
<a href="../chapter2/13_Inheritance.html" title="继承" class="chapter " data-progress="2.13" style="left: 23.68421052631579%;"></a>
@ -587,11 +587,10 @@
<div class="page-inner">
<section class="normal" id="section-gitbook_90">
<section class="normal" id="section-gitbook_37">
<blockquote>
<p>翻译numbbbbb, lyuka, JaySurplus</p>
<p>校对lslxdx</p>
<p>翻译numbbbbb, lyuka, JaySurplus<br>校对lslxdx </p>
</blockquote>
<h1 id="-">基础部分</h1>
<hr>
@ -602,7 +601,7 @@
<li><a href="#semicolons">分号</a></li>
<li><a href="#integers">整数</a></li>
<li><a href="#floating-point_numbers">浮点数</a></li>
<li><a href="#type_safety_and_type_inference">类型安全和类型推</a></li>
<li><a href="#type_safety_and_type_inference">类型安全和类型推</a></li>
<li><a href="#numeric_literals">数值型字面量</a></li>
<li><a href="#numeric_type_conversion">数值型类型转换</a></li>
<li><a href="#type_aliases">类型别名</a></li>
@ -622,92 +621,103 @@
<p>常量和变量把一个名字(比如<code>maximumNumberOfLoginAttempts</code>或者<code>welcomeMessage</code>)和一个指定类型的值(比如数字<code>10</code>或者字符串<code>&quot;Hello&quot;</code>)关联起来。常量的值一旦设定就不能改变,而变量的值可以随意更改。</p>
<h3 id="-">声明常量和变量</h3>
<p>常量和变量必须在使用前声明,用<code>let</code>来声明常量,用<code>var</code>来声明变量。下面的例子展示了如何用常量和变量来记录用户尝试登录的次数:</p>
<pre><code>let maximumNumberOfLoginAttempts = 10
<pre><code class="lang-swift">let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
</code></pre><p>这两行代码可以被理解为:</p>
</code></pre>
<p>这两行代码可以被理解为:</p>
<p>“声明一个名字是<code>maximumNumberOfLoginAttempts</code>的新常量,并给它一个值<code>10</code>。然后,声明一个名字是<code>currentLoginAttempt</code>的变量并将它的值初始化为<code>0</code>.”</p>
<p>在这个例子中,允许的最大尝试登录次数被声明为一个常量,因为这个值不会改变。当前尝试登录次数被声明为一个变量,因为每次尝试登录失败的时候都需要增加这个值。</p>
<p>你可以在一行中声明多个常量或者多个变量,用逗号隔开:</p>
<pre><code>var x = 0.0, y = 0.0, z = 0.0
</code></pre><blockquote>
<p>注意:</p>
<p>如果你的代码中有不需要改变的值,请使用<code>let</code>关键字将它声明为常量。只将需要改变的值声明为变量。</p>
<pre><code class="lang-swift">var x = 0.0, y = 0.0, z = 0.0
</code></pre>
<blockquote>
<p>注意:<br>如果你的代码中有不需要改变的值,请使用<code>let</code>关键字将它声明为常量。只将需要改变的值声明为变量。</p>
</blockquote>
<h3 id="-">类型标注</h3>
<p>当你声明常量或者变量的时候可以加上<em>类型标注type annotation</em>,说明常量或者变量中要存储的值的类型。如果要添加类型标注,需要在常量或者变量名后面加上一个冒号和空格,然后加上类型名称。</p>
<p>这个例子给<code>welcomeMessage</code>变量添加了类型标注,表示这个变量可以存储<code>String</code>类型的值:</p>
<pre><code>var welcomeMessage: String
</code></pre><p>声明中的冒号代表着“是...类型”,所以这行代码可以被理解为:</p>
<pre><code class="lang-swift">var welcomeMessage: String
</code></pre>
<p>声明中的冒号代表着“是...类型”,所以这行代码可以被理解为:</p>
<p>“声明一个类型为<code>String</code>,名字为<code>welcomeMessage</code>的变量。”</p>
<p>“类型为<code>String</code>”的意思是“可以存储任意<code>String</code>类型的值。”</p>
<p><code>welcomeMessage</code>变量现在可以被设置成任意字符串:</p>
<pre><code>welcomeMessage = &quot;Hello&quot;
</code></pre><blockquote>
<p>注意:</p>
<p>一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值Swift可以推断出这个常量或者变量的类型请参考<a href="#type_safety_and_type_inference">类型安全和类型推断</a>。在上面的例子中,没有给<code>welcomeMessage</code>赋初始值,所以变量<code>welcomeMessage</code>的类型是通过一个类型标注指定的,而不是通过初始值推断的。</p>
<pre><code class="lang-swift">welcomeMessage = &quot;Hello&quot;
</code></pre>
<blockquote>
<p>注意:<br>一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值Swift可以推断出这个常量或者变量的类型请参考<a href="#type_safety_and_type_inference">类型安全和类型推断</a>。在上面的例子中,没有给<code>welcomeMessage</code>赋初始值,所以变量<code>welcomeMessage</code>的类型是通过一个类型标注指定的,而不是通过初始值推断的。</p>
</blockquote>
<h3 id="-">常量和变量的命名</h3>
<p>你可以用任何你喜欢的字符作为常量和变量名,包括 Unicode 字符:</p>
<pre><code>let π = 3.14159
<pre><code class="lang-swift">let π = 3.14159
let 你好 = &quot;你好世界&quot;
let 🐶🐮 = &quot;dogcow&quot;
</code></pre><p>常量与变量名不能包含数学符号箭头保留的或者非法的Unicode 码位,连线与制表符。也不能以数字开头,但是可以在常量与变量名的其他地方包含数字。</p>
</code></pre>
<p>常量与变量名不能包含数学符号箭头保留的或者非法的Unicode 码位,连线与制表符。也不能以数字开头,但是可以在常量与变量名的其他地方包含数字。</p>
<p>一旦你将常量或者变量声明为确定的类型,你就不能使用相同的名字再次进行声明,或者改变其存储的值的类型。同时,你也不能将常量与变量进行互转。</p>
<blockquote>
<p>注意:</p>
<p>如果你需要使用与Swift保留关键字相同的名称作为常量或者变量名你可以使用反引号`)将关键字包围的方式将其作为名字使用。无论如何,你应当避免使用关键字作为常量或变量名,除非你别无选择。</p>
<p>注意:<br>如果你需要使用与Swift保留关键字相同的名称作为常量或者变量名你可以使用反引号`)将关键字包围的方式将其作为名字使用。无论如何,你应当避免使用关键字作为常量或变量名,除非你别无选择。</p>
</blockquote>
<p>你可以更改现有的变量值为其他同类型的值,在下面的例子中,<code>friendlyWelcome</code>的值从<code>&quot;Hello!&quot;</code>改为了<code>&quot;Bonjour!&quot;</code>:</p>
<pre><code>var friendlyWelcome = &quot;Hello!&quot;
<pre><code class="lang-swift">var friendlyWelcome = &quot;Hello!&quot;
friendlyWelcome = &quot;Bonjour!&quot;
// friendlyWelcome 现在是 &quot;Bonjour!&quot;
</code></pre><p>与变量不同,常量的值一旦被确定就不能更改了。尝试这样做会导致编译时报错:</p>
<pre><code>let languageName = &quot;Swift&quot;
</code></pre>
<p>与变量不同,常量的值一旦被确定就不能更改了。尝试这样做会导致编译时报错:</p>
<pre><code class="lang-swift">let languageName = &quot;Swift&quot;
languageName = &quot;Swift++&quot;
// 这会报编译时错误 - languageName 不可改变
</code></pre><h3 id="-">输出常量和变量</h3>
</code></pre>
<h3 id="-">输出常量和变量</h3>
<p>你可以用<code>println</code>函数来输出当前常量或变量的值:</p>
<pre><code>println(friendlyWelcome)
<pre><code class="lang-swift">println(friendlyWelcome)
// 输出 &quot;Bonjour!&quot;
</code></pre><p><code>println</code>是一个用来输出的全局函数,输出的内容会在最后换行。如果你用 Xcode<code>println</code>将会输出内容到“console”面板上。(另一种函数叫<code>print</code>,唯一区别是在输出内容最后不会换行。)</p>
</code></pre>
<p><code>println</code>是一个用来输出的全局函数,输出的内容会在最后换行。如果你用 Xcode<code>println</code>将会输出内容到“console”面板上。(另一种函数叫<code>print</code>,唯一区别是在输出内容最后不会换行。)</p>
<p><code>println</code>函数输出传入的<code>String</code>值:</p>
<pre><code>println(&quot;This is a string&quot;)
<pre><code class="lang-swift">println(&quot;This is a string&quot;)
// 输出 &quot;This is a string&quot;
</code></pre><p>与 Cocoa 里的<code>NSLog</code>函数类似的是,<code>println</code>函数可以输出更复杂的信息。这些信息可以包含当前常量和变量的值。</p>
</code></pre>
<p>与 Cocoa 里的<code>NSLog</code>函数类似的是,<code>println</code>函数可以输出更复杂的信息。这些信息可以包含当前常量和变量的值。</p>
<p>Swift 用<em>字符串插值string interpolation</em>的方式把常量名或者变量名当做占位符加入到长字符串中Swift 会用当前常量或变量的值替换这些占位符。将常量或变量名放入圆括号中,并在开括号前使用反斜杠将其转义:</p>
<pre><code>println(&quot;The current value of friendlyWelcome is \(friendlyWelcome)&quot;)
<pre><code class="lang-swift">println(&quot;The current value of friendlyWelcome is \(friendlyWelcome)&quot;)
// 输出 &quot;The current value of friendlyWelcome is Bonjour!
</code></pre><blockquote>
<p>注意:</p>
<p>字符串插值所有可用的选项,请参考<a href="03_Strings_and_Characters.html#string_interpolation">字符串插值</a></p>
</code></pre>
<blockquote>
<p>注意:<br>字符串插值所有可用的选项,请参考<a href="03_Strings_and_Characters.html#string_interpolation">字符串插值</a></p>
</blockquote>
<p><a name="comments"></a></p>
<h2 id="-">注释</h2>
<p>请将你的代码中的非执行文本注释成提示或者笔记以方便你将来阅读。Swift 的编译器将会在编译代码时自动忽略掉注释部分。</p>
<p>Swift 中的注释与C 语言的注释非常相似。单行注释以双正斜杠(<code>//</code>)作为起始标记:</p>
<pre><code>// 这是一个注释
</code></pre><p>你也可以进行多行注释,其起始标记为单个正斜杠后跟随一个星号(<code>/*</code>),终止标记为一个星号后跟随单个正斜杠(<code>*/</code>:</p>
<pre><code>/* 这是一个,
<pre><code class="lang-swift">// 这是一个注释
</code></pre>
<p>你也可以进行多行注释,其起始标记为单个正斜杠后跟随一个星号(<code>/*</code>),终止标记为一个星号后跟随单个正斜杠(<code>*/</code>:</p>
<pre><code class="lang-swift">/* 这是一个,
多行注释 */
</code></pre><p>与 C 语言多行注释不同Swift 的多行注释可以嵌套在其它的多行注释之中。你可以先生成一个多行注释块,然后在这个注释块之中再嵌套成第二个多行注释。终止注释时先插入第二个注释块的终止标记,然后再插入第一个注释块的终止标记:</p>
<pre><code>/* 这是第一个多行注释的开头
</code></pre>
<p>与 C 语言多行注释不同Swift 的多行注释可以嵌套在其它的多行注释之中。你可以先生成一个多行注释块,然后在这个注释块之中再嵌套成第二个多行注释。终止注释时先插入第二个注释块的终止标记,然后再插入第一个注释块的终止标记:</p>
<pre><code class="lang-swift">/* 这是第一个多行注释的开头
/* 这是第二个被嵌套的多行注释 */
这是第一个多行注释的结尾 */
</code></pre><p>通过运用嵌套多行注释,你可以快速方便的注释掉一大段代码,即使这段代码之中已经含有了多行注释块。</p>
</code></pre>
<p>通过运用嵌套多行注释,你可以快速方便的注释掉一大段代码,即使这段代码之中已经含有了多行注释块。</p>
<p><a name="semicolons"></a></p>
<h2 id="-">分号</h2>
<p>与其他大部分编程语言不同Swift 并不强制要求你在每条语句的结尾处使用分号(<code>;</code>),当然,你也可以按照你自己的习惯添加分号。有一种情况下必须要用分号,即你打算在同一行内写多条独立的语句:</p>
<pre><code>let cat = &quot;🐱&quot;; println(cat)
<pre><code class="lang-swift">let cat = &quot;🐱&quot;; println(cat)
// 输出 &quot;🐱&quot;
</code></pre><p><a name="integers"></a></p>
</code></pre>
<p><a name="integers"></a></p>
<h2 id="-">整数</h2>
<p>整数就是没有小数部分的数字,比如<code>42</code><code>-23</code>。整数可以是<code>有符号</code>(正、负、零)或者<code>无符号</code>(正、零)。</p>
<p>Swift 提供了81632和64位的有符号和无符号整数类型。这些整数类型和 C 语言的命名方式很像比如8位无符号整数类型是<code>UInt8</code>32位有符号整数类型是<code>Int32</code>。就像 Swift 的其他类型一样,整数类型采用大写命名法。</p>
<h3 id="-">整数范围</h3>
<p>你可以访问不同整数类型的<code>min</code><code>max</code>属性来获取对应类型的最大值和最小值:</p>
<pre><code>let minValue = UInt8.min // minValue 为 0是 UInt8 类型的最小值
<pre><code class="lang-swift">let minValue = UInt8.min // minValue 为 0是 UInt8 类型的最小值
let maxValue = UInt8.max // maxValue 为 255是 UInt8 类型的最大值
</code></pre><h3 id="int">Int</h3>
</code></pre>
<h3 id="int">Int</h3>
<p>一般来说你不需要专门指定整数的长度。Swift 提供了一个特殊的整数类型<code>Int</code>,长度与当前平台的原生字长相同:</p>
<ul>
<li>在32位平台上<code>Int</code><code>Int32</code>长度相同。</li>
@ -722,7 +732,7 @@ let maxValue = UInt8.max // maxValue 为 255是 UInt8 类型的最大值
</ul>
<blockquote>
<p>注意:</p>
<p>尽量不要使用<code>UInt</code>,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用<code>Int</code>,即使你要存储的值已知是非负的。统一使用<code>Int</code>可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推,请参考<a href="#type_safety_and_type_inference">类型安全和类型推</a></p>
<p>尽量不要使用<code>UInt</code>,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用<code>Int</code>,即使你要存储的值已知是非负的。统一使用<code>Int</code>可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推,请参考<a href="#type_safety_and_type_inference">类型安全和类型推</a></p>
</blockquote>
<p><a name="floating-point_numbers"></a></p>
<h2 id="-">浮点数</h2>
@ -733,27 +743,29 @@ let maxValue = UInt8.max // maxValue 为 255是 UInt8 类型的最大值
<li><code>Float</code>表示32位浮点数。精度要求不高的话可以使用此类型。</li>
</ul>
<blockquote>
<p>注意:</p>
<p><code>Double</code>精确度很高至少有15位数字<code>Float</code>最少只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围。</p>
<p>注意:<br><code>Double</code>精确度很高至少有15位数字<code>Float</code>最少只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围。</p>
</blockquote>
<p><a name="type_safety_and_type_inference"></a></p>
<h2 id="-">类型安全和类型推</h2>
<h2 id="-">类型安全和类型推</h2>
<p>Swift 是一个<em>类型安全type safe</em>的语言。类型安全的语言可以让你清楚地知道代码要处理的值的类型。如果你的代码需要一个<code>String</code>,你绝对不可能不小心传进去一个<code>Int</code></p>
<p>由于 Swift 是类型安全的,所以它会在编译你的代码时进行<em>类型检查type checks</em>,并把不匹配的类型标记为错误。这可以让你在开发的时候尽早发现并修复错误。</p>
<p>当你要处理不同类型的值时类型检查可以帮你避免错误。然而这并不是说你每次声明常量和变量的时候都需要显式指定类型。如果你没有显式指定类型Swift 会使用<em>类型推type inference</em>来选择合适的类型。有了类型推,编译器可以在编译代码的时候自动推出表达式的类型。原理很简单,只要检查你赋的值即可。</p>
<p>因为有类型推,和 C 或者 Objective-C 比起来 Swift 很少需要声明类型。常量和变量虽然需要明确类型,但是大部分工作并不需要你自己来完成。</p>
<p>当你声明常量或者变量并赋初值的时候类型推非常有用。当你在声明常量或者变量的时候赋给它们一个<em>字面量literal value 或 literal</em>即可触发类型推。(字面量就是会直接出现在你代码中的值,比如<code>42</code><code>3.14159</code>。)</p>
<p>例如,如果你给一个新常量赋值<code>42</code>并且没有标明类型Swift 可以推出常量类型是<code>Int</code>,因为你给它赋的初始值看起来像一个整数:</p>
<pre><code>let meaningOfLife = 42
<p>当你要处理不同类型的值时类型检查可以帮你避免错误。然而这并不是说你每次声明常量和变量的时候都需要显式指定类型。如果你没有显式指定类型Swift 会使用<em>类型推type inference</em>来选择合适的类型。有了类型推,编译器可以在编译代码的时候自动推出表达式的类型。原理很简单,只要检查你赋的值即可。</p>
<p>因为有类型推,和 C 或者 Objective-C 比起来 Swift 很少需要声明类型。常量和变量虽然需要明确类型,但是大部分工作并不需要你自己来完成。</p>
<p>当你声明常量或者变量并赋初值的时候类型推非常有用。当你在声明常量或者变量的时候赋给它们一个<em>字面量literal value 或 literal</em>即可触发类型推。(字面量就是会直接出现在你代码中的值,比如<code>42</code><code>3.14159</code>。)</p>
<p>例如,如果你给一个新常量赋值<code>42</code>并且没有标明类型Swift 可以推出常量类型是<code>Int</code>,因为你给它赋的初始值看起来像一个整数:</p>
<pre><code class="lang-swift">let meaningOfLife = 42
// meaningOfLife 会被推测为 Int 类型
</code></pre><p>同理如果你没有给浮点字面量标明类型Swift 会推测你想要的是<code>Double</code></p>
<pre><code>let pi = 3.14159
</code></pre>
<p>同理如果你没有给浮点字面量标明类型Swift 会推断你想要的是<code>Double</code></p>
<pre><code class="lang-swift">let pi = 3.14159
// pi 会被推测为 Double 类型
</code></pre><p>当推测浮点数的类型时Swift 总是会选择<code>Double</code>而不是<code>Float</code></p>
<p>如果表达式中同时出现了整数和浮点数,会被推测为<code>Double</code>类型:</p>
<pre><code>let anotherPi = 3 + 0.14159
</code></pre>
<p>当推断浮点数的类型时Swift 总是会选择<code>Double</code>而不是<code>Float</code></p>
<p>如果表达式中同时出现了整数和浮点数,会被推断为<code>Double</code>类型:</p>
<pre><code class="lang-swift">let anotherPi = 3 + 0.14159
// anotherPi 会被推测为 Double 类型
</code></pre><p>原始值<code>3</code>没有显式声明类型,而表达式中出现了一个浮点字面量,所以表达式会被推测为<code>Double</code>类型。</p>
</code></pre>
<p>原始值<code>3</code>没有显式声明类型,而表达式中出现了一个浮点字面量,所以表达式会被推断为<code>Double</code>类型。</p>
<p><a name="numeric_literals"></a></p>
<h2 id="-">数值型字面量</h2>
<p>整数字面量可以被写作:</p>
@ -764,11 +776,12 @@ let maxValue = UInt8.max // maxValue 为 255是 UInt8 类型的最大值
<li>一个十六进制数,前缀是<code>0x</code></li>
</ul>
<p>下面的所有整数字面量的十进制值都是<code>17</code>:</p>
<pre><code>let decimalInteger = 17
<pre><code class="lang-swift">let decimalInteger = 17
let binaryInteger = 0b10001 // 二进制的17
let octalInteger = 0o21 // 八进制的17
let hexadecimalInteger = 0x11 // 十六进制的17
</code></pre><p>浮点字面量可以是十进制(没有前缀)或者是十六进制(前缀是<code>0x</code>)。小数点两边必须有至少一个十进制数字(或者是十六进制的数字)。浮点字面量还有一个可选的<em>指数exponent</em>,在十进制浮点数中通过大写或者小写的<code>e</code>来指定,在十六进制浮点数中通过大写或者小写的<code>p</code>来指定。</p>
</code></pre>
<p>浮点字面量可以是十进制(没有前缀)或者是十六进制(前缀是<code>0x</code>)。小数点两边必须有至少一个十进制数字(或者是十六进制的数字)。浮点字面量还有一个可选的<em>指数exponent</em>,在十进制浮点数中通过大写或者小写的<code>e</code>来指定,在十六进制浮点数中通过大写或者小写的<code>p</code>来指定。</p>
<p>如果一个十进制数的指数为<code>exp</code>那这个数相当于基数和10^exp的乘积</p>
<ul>
<li><code>1.25e2</code> 表示 1.25 × 10^2等于 <code>125.0</code></li>
@ -780,118 +793,134 @@ let hexadecimalInteger = 0x11 // 十六进制的17
<li><code>0xFp-2</code> 表示 15 × 2^-2等于 <code>3.75</code></li>
</ul>
<p>下面的这些浮点字面量都等于十进制的<code>12.1875</code></p>
<pre><code>let decimalDouble = 12.1875
<pre><code class="lang-swift">let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0
</code></pre><p>数值类字面量可以包括额外的格式来增强可读性。整数和浮点数都可以添加额外的零并且包含下划线,并不会影响字面量:</p>
<pre><code>let paddedDouble = 000123.456
</code></pre>
<p>数值类字面量可以包括额外的格式来增强可读性。整数和浮点数都可以添加额外的零并且包含下划线,并不会影响字面量:</p>
<pre><code class="lang-swift">let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1
</code></pre><p><a name="numeric_type_conversion"></a></p>
</code></pre>
<p><a name="numeric_type_conversion"></a></p>
<h2 id="-">数值型类型转换</h2>
<p>通常来讲,即使代码中的整数常量和变量已知非负,也请使用<code>Int</code>类型。总是使用默认的整数类型可以保证你的整数常量和变量可以直接被复用并且可以匹配整数类字面量的类型推
<p>通常来讲,即使代码中的整数常量和变量已知非负,也请使用<code>Int</code>类型。总是使用默认的整数类型可以保证你的整数常量和变量可以直接被复用并且可以匹配整数类字面量的类型推
只有在必要的时候才使用其他整数类型,比如要处理外部的长度明确的数据或者为了优化性能、内存占用等等。使用显式指定长度的类型可以及时发现值溢出并且可以暗示正在处理特殊数据。</p>
<h3 id="-">整数转换</h3>
<p>不同整数类型的变量和常量可以存储不同范围的数字。<code>Int8</code>类型的常量或者变量可以存储的数字范围是<code>-128</code>~<code>127</code>,而<code>UInt8</code>类型的常量或者变量能存储的数字范围是<code>0</code>~<code>255</code>。如果数字超出了常量或者变量可存储的范围,编译的时候会报错:</p>
<pre><code>let cannotBeNegative: UInt8 = -1
<pre><code class="lang-swift">let cannotBeNegative: UInt8 = -1
// UInt8 类型不能存储负数,所以会报错
let tooBig: Int8 = Int8.max + 1
// Int8 类型不能存储超过最大值的数,所以会报错
</code></pre><p>由于每种整数类型都可以存储不同范围的值,所以你必须根据不同情况选择性使用数值型类型转换。这种选择性使用的方式,可以预防隐式转换的错误并让你的代码中的类型转换意图变得清晰。</p>
</code></pre>
<p>由于每种整数类型都可以存储不同范围的值,所以你必须根据不同情况选择性使用数值型类型转换。这种选择性使用的方式,可以预防隐式转换的错误并让你的代码中的类型转换意图变得清晰。</p>
<p>要将一种数字类型转换成另一种,你要用当前值来初始化一个期望类型的新数字,这个数字的类型就是你的目标类型。在下面的例子中,常量<code>twoThousand</code><code>UInt16</code>类型,然而常量<code>one</code><code>UInt8</code>类型。它们不能直接相加,因为它们类型不同。所以要调用<code>UInt16(one)</code>来创建一个新的<code>UInt16</code>数字并用<code>one</code>的值来初始化,然后使用这个新数字来计算:</p>
<pre><code>let twoThousand: UInt16 = 2_000
<pre><code class="lang-swift">let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
</code></pre><p>现在两个数字的类型都是<code>UInt16</code>,可以进行相加。目标常量<code>twoThousandAndOne</code>的类型被推测为<code>UInt16</code>,因为它是两个<code>UInt16</code>值的和。</p>
</code></pre>
<p>现在两个数字的类型都是<code>UInt16</code>,可以进行相加。目标常量<code>twoThousandAndOne</code>的类型被推断为<code>UInt16</code>,因为它是两个<code>UInt16</code>值的和。</p>
<p><code>SomeType(ofInitialValue)</code>是调用 Swift 构造器并传入一个初始值的默认方法。在语言内部,<code>UInt16</code>有一个构造器,可以接受一个<code>UInt8</code>类型的值,所以这个构造器可以用现有的<code>UInt8</code>来创建一个新的<code>UInt16</code>。注意,你并不能传入任意类型的值,只能传入<code>UInt16</code>内部有对应构造器的值。不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型),请参考<a href="20_Extensions.html">扩展</a></p>
<h3 id="-">整数和浮点数转换</h3>
<p>整数和浮点数的转换必须显式指定类型:</p>
<pre><code>let three = 3
<pre><code class="lang-swift">let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi 等于 3.14159,所以被推测为 Double 类型
</code></pre><p>这个例子中,常量<code>three</code>的值被用来创建一个<code>Double</code>类型的值,所以加号两边的数类型相同。如果不进行转换,两者无法相加。</p>
</code></pre>
<p>这个例子中,常量<code>three</code>的值被用来创建一个<code>Double</code>类型的值,所以加号两边的数类型须相同。如果不进行转换,两者无法相加。</p>
<p>浮点数到整数的反向转换同样行,整数类型可以用<code>Double</code>或者<code>Float</code>类型来初始化:</p>
<pre><code>let integerPi = Int(pi)
<pre><code class="lang-swift">let integerPi = Int(pi)
// integerPi 等于 3所以被推测为 Int 类型
</code></pre><p>当用这种方式来初始化一个新的整数值时,浮点值会被截断。也就是说<code>4.75</code>会变成<code>4</code><code>-3.9</code>会变成<code>-3</code></p>
</code></pre>
<p>当用这种方式来初始化一个新的整数值时,浮点值会被截断。也就是说<code>4.75</code>会变成<code>4</code><code>-3.9</code>会变成<code>-3</code></p>
<blockquote>
<p>注意:</p>
<p>结合数字类常量和变量不同于结合数字类字面量。字面量<code>3</code>可以直接和字面量<code>0.14159</code>相加,因为数字字面量本身没有明确的类型。它们的类型只在编译器需要求值的时候被推测。</p>
<p>注意:<br>结合数字类常量和变量不同于结合数字类字面量。字面量<code>3</code>可以直接和字面量<code>0.14159</code>相加,因为数字字面量本身没有明确的类型。它们的类型只在编译器需要求值的时候被推测。</p>
</blockquote>
<p><a name="type_aliases"></a></p>
<h2 id="-">类型别名</h2>
<p><em>类型别名type aliases</em>就是给现有类型定义另一个名字。你可以使用<code>typealias</code>关键字来定义类型别名。</p>
<p>当你想要给现有类型起一个更有意义的名字时,类型别名非常有用。假设你正在处理特定长度的外部资源的数据:</p>
<pre><code>typealias AudioSample = UInt16
</code></pre><p>定义了一个类型别名之后,你可以在任何使用原始名的地方使用别名:</p>
<pre><code>var maxAmplitudeFound = AudioSample.min
<pre><code class="lang-swift">typealias AudioSample = UInt16
</code></pre>
<p>定义了一个类型别名之后,你可以在任何使用原始名的地方使用别名:</p>
<pre><code class="lang-swift">var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound 现在是 0
</code></pre><p>本例中,<code>AudioSample</code>被定义为<code>UInt16</code>的一个别名。因为它是别名,<code>AudioSample.min</code>实际上是<code>UInt16.min</code>,所以会给<code>maxAmplitudeFound</code>赋一个初值<code>0</code></p>
</code></pre>
<p>本例中,<code>AudioSample</code>被定义为<code>UInt16</code>的一个别名。因为它是别名,<code>AudioSample.min</code>实际上是<code>UInt16.min</code>,所以会给<code>maxAmplitudeFound</code>赋一个初值<code>0</code></p>
<p><a name="booleans"></a></p>
<h2 id="-">布尔值</h2>
<p>Swift 有一个基本的<em>布尔Boolean</em>类型,叫做<code>Bool</code>。布尔值指<em>逻辑上的logical</em>因为它们只能是真或者假。Swift 有两个布尔常量,<code>true</code><code>false</code></p>
<pre><code>let orangesAreOrange = true
<pre><code class="lang-swift">let orangesAreOrange = true
let turnipsAreDelicious = false
</code></pre><p><code>orangesAreOrange</code><code>turnipsAreDelicious</code>的类型会被推测为<code>Bool</code>,因为它们的初值是布尔字面量。就像之前提到的<code>Int</code><code>Double</code>一样,如果你创建变量的时候给它们赋值<code>true</code>或者<code>false</code>,那你不需要将常量或者变量声明为<code>Bool</code>类型。初始化常量或者变量的时候如果所赋的值类型已知,就可以触发类型推测,这让 Swift 代码更加简洁并且可读性更高。</p>
</code></pre>
<p><code>orangesAreOrange</code><code>turnipsAreDelicious</code>的类型会被推断为<code>Bool</code>,因为它们的初值是布尔字面量。就像之前提到的<code>Int</code><code>Double</code>一样,如果你创建变量的时候给它们赋值<code>true</code>或者<code>false</code>,那你不需要将常量或者变量声明为<code>Bool</code>类型。初始化常量或者变量的时候如果所赋的值类型已知,就可以触发类型推断,这让 Swift 代码更加简洁并且可读性更高。</p>
<p>当你编写条件语句比如<code>if</code>语句的时候,布尔值非常有用:</p>
<pre><code>if turnipsAreDelicious {
<pre><code class="lang-swift">if turnipsAreDelicious {
println(&quot;Mmm, tasty turnips!&quot;)
} else {
println(&quot;Eww, turnips are horrible.&quot;)
}
// 输出 &quot;Eww, turnips are horrible.&quot;
</code></pre><p>条件语句,例如<code>if</code>,请参考<a href="05_Control_Flow.html">控制流</a></p>
</code></pre>
<p>条件语句,例如<code>if</code>,请参考<a href="05_Control_Flow.html">控制流</a></p>
<p>如果你在需要使用<code>Bool</code>类型的地方使用了非布尔值Swift 的类型安全机制会报错。下面的例子会报告一个编译时错误:</p>
<pre><code>let i = 1
<pre><code class="lang-swift">let i = 1
if i {
// 这个例子不会通过编译,会报错
}
</code></pre><p>然而,下面的例子是合法的:</p>
<pre><code>let i = 1
</code></pre>
<p>然而,下面的例子是合法的:</p>
<pre><code class="lang-swift">let i = 1
if i == 1 {
// 这个例子会编译成功
}
</code></pre><p><code>i == 1</code>的比较结果是<code>Bool</code>类型,所以第二个例子可以通过类型检查。类似<code>i == 1</code>这样的比较,请参考<a href="05_Control_Flow.html">基本操作符</a></p>
</code></pre>
<p><code>i == 1</code>的比较结果是<code>Bool</code>类型,所以第二个例子可以通过类型检查。类似<code>i == 1</code>这样的比较,请参考<a href="05_Control_Flow.html">基本操作符</a></p>
<p>和 Swift 中的其他类型安全的例子一样,这个方法可以避免错误并保证这块代码的意图总是清晰的。</p>
<p><a name="tuples"></a></p>
<h2 id="-">元组</h2>
<p><em>元组tuples</em>把多个值组合成一个复合值。元组内的值可以使任意类型,并不要求是相同类型。</p>
<p>下面这个例子中,<code>(404, &quot;Not Found&quot;)</code>是一个描述 <em>HTTP 状态码HTTP status code</em>的元组。HTTP 状态码是当你请求网页的时候 web 服务器返回的一个特殊值。如果你请求的网页不存在就会返回一个<code>404 Not Found</code>状态码。</p>
<pre><code>let http404Error = (404, &quot;Not Found&quot;)
<pre><code class="lang-swift">let http404Error = (404, &quot;Not Found&quot;)
// http404Error 的类型是 (Int, String),值是 (404, &quot;Not Found&quot;)
</code></pre><p><code>(404, &quot;Not Found&quot;)</code>元组把一个<code>Int</code>值和一个<code>String</code>值组合起来表示 HTTP 状态码的两个部分:一个数字和一个人类可读的描述。这个元组可以被描述为“一个类型为<code>(Int, String)</code>的元组”。</p>
</code></pre>
<p><code>(404, &quot;Not Found&quot;)</code>元组把一个<code>Int</code>值和一个<code>String</code>值组合起来表示 HTTP 状态码的两个部分:一个数字和一个人类可读的描述。这个元组可以被描述为“一个类型为<code>(Int, String)</code>的元组”。</p>
<p>你可以把任意顺序的类型组合成一个元组,这个元组可以包含所有类型。只要你想,你可以创建一个类型为<code>(Int, Int, Int)</code>或者<code>(String, Bool)</code>或者其他任何你想要的组合的元组。</p>
<p>你可以将一个元组的内容<em>分解decompose</em>成单独的常量和变量,然后你就可以正常使用它们了:</p>
<pre><code>let (statusCode, statusMessage) = http404Error
<pre><code class="lang-swift">let (statusCode, statusMessage) = http404Error
println(&quot;The status code is \(statusCode)&quot;)
// 输出 &quot;The status code is 404&quot;
println(&quot;The status message is \(statusMessage)&quot;)
// 输出 &quot;The status message is Not Found&quot;
</code></pre><p>如果你只需要一部分元组值,分解的时候可以把要忽略的部分用下划线(<code>_</code>)标记:</p>
<pre><code>let (justTheStatusCode, _) = http404Error
</code></pre>
<p>如果你只需要一部分元组值,分解的时候可以把要忽略的部分用下划线(<code>_</code>)标记:</p>
<pre><code class="lang-swift">let (justTheStatusCode, _) = http404Error
println(&quot;The status code is \(justTheStatusCode)&quot;)
// 输出 &quot;The status code is 404&quot;
</code></pre><p>此外,你还可以通过下标来访问元组中的单个元素,下标从零开始:</p>
<pre><code>println(&quot;The status code is \(http404Error.0)&quot;)
</code></pre>
<p>此外,你还可以通过下标来访问元组中的单个元素,下标从零开始:</p>
<pre><code class="lang-swift">println(&quot;The status code is \(http404Error.0)&quot;)
// 输出 &quot;The status code is 404&quot;
println(&quot;The status message is \(http404Error.1)&quot;)
// 输出 &quot;The status message is Not Found&quot;
</code></pre><p>你可以在定义元组的时候给单个元素命名:</p>
<pre><code>let http200Status = (statusCode: 200, description: &quot;OK&quot;)
</code></pre><p>给元组中的元素命名后,你可以通过名字来获取这些元素的值:</p>
<pre><code>println(&quot;The status code is \(http200Status.statusCode)&quot;)
</code></pre>
<p>你可以在定义元组的时候给单个元素命名:</p>
<pre><code class="lang-swift">let http200Status = (statusCode: 200, description: &quot;OK&quot;)
</code></pre>
<p>给元组中的元素命名后,你可以通过名字来获取这些元素的值:</p>
<pre><code class="lang-swift">println(&quot;The status code is \(http200Status.statusCode)&quot;)
// 输出 &quot;The status code is 200&quot;
println(&quot;The status message is \(http200Status.description)&quot;)
// 输出 &quot;The status message is OK&quot;
</code></pre><p>作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个<code>(Int, String)</code>元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。请参考<a href="06_Functions.html#Function_Parameters_and_Return_Values">函数参数与返回值</a></p>
</code></pre>
<p>作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个<code>(Int, String)</code>元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。请参考<a href="06_Functions.html#Function_Parameters_and_Return_Values">函数参数与返回值</a></p>
<blockquote>
<p>注意:</p>
<p>元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。请参考<a href="09_Classes_and_Structures.html">类和结构体</a></p>
<p>注意:<br>元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。请参考<a href="09_Classes_and_Structures.html">类和结构体</a></p>
</blockquote>
<p><a name="optionals"></a></p>
<h2 id="-">可选</h2>
<p>使用<em>可选optionals</em>来处理值可能缺失的情况。可选表示:</p>
<h2 id="-">可选类型</h2>
<p>使用<em>可选类型optionals</em>来处理值可能缺失的情况。可选类型表示:</p>
<ul>
<li><em></em>值,等于 x</li>
</ul>
@ -901,119 +930,127 @@ println(&quot;The status message is \(http200Status.description)&quot;)
</ul>
<blockquote>
<p>注意:</p>
<p>C 和 Objective-C 中并没有可选这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回<code>nil</code><code>nil</code>表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型Objective-C 方法一般会返回一个特殊值(比如<code>NSNotFound</code>来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而Swift 的可选可以让你暗示<em>任意类型</em>的值缺失,并不需要一个特殊值。</p>
<p>C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回<code>nil</code><code>nil</code>表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型Objective-C 方法一般会返回一个特殊值(比如<code>NSNotFound</code>来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而Swift 的可选类型可以让你暗示<em>任意类型</em>的值缺失,并不需要一个特殊值。</p>
</blockquote>
<p>来看一个例子。Swift 的<code>String</code>类型有一个叫做<code>toInt</code>的方法,作用是将一个<code>String</code>值转换成一个<code>Int</code>值。然而,并不是所有的字符串都可以转换成一个整数。字符串<code>&quot;123&quot;</code>可以被转换成数字<code>123</code>,但是字符串<code>&quot;hello, world&quot;</code>不行。</p>
<p>下面的例子使用<code>toInt</code>方法来尝试将一个<code>String</code>转换成<code>Int</code></p>
<pre><code>let possibleNumber = &quot;123&quot;
<pre><code class="lang-swift">let possibleNumber = &quot;123&quot;
let convertedNumber = possibleNumber.toInt()
// convertedNumber 被推测为类型 &quot;Int?&quot; 或者类型 &quot;optional Int&quot;
</code></pre><p>因为<code>toInt</code>方法可能会失败,所以它返回一个<em>可选的optional</em><code>Int</code>,而不是一个<code>Int</code>。一个可选的<code>Int</code>被写作<code>Int?</code>而不是<code>Int</code>。问号暗示包含的值是可选,也就是说可能包含<code>Int</code>值也可能不包含值。(不能包含其他任何值比如<code>Bool</code>值或者<code>String</code>值。只能是<code>Int</code>或者什么都没有。)</p>
</code></pre>
<p>因为<code>toInt</code>方法可能会失败,所以它返回一个<em>可选类型optional</em><code>Int</code>,而不是一个<code>Int</code>。一个可选的<code>Int</code>被写作<code>Int?</code>而不是<code>Int</code>。问号暗示包含的值是可选类型,也就是说可能包含<code>Int</code>值也可能不包含值。(不能包含其他任何值比如<code>Bool</code>值或者<code>String</code>值。只能是<code>Int</code>或者什么都没有。)</p>
<h3 id="if-">if 语句以及强制解析</h3>
<p>你可以使用<code>if</code>语句来判断一个可选是否包含值。如果可选有值,结果是<code>true</code>;如果没有值,结果是<code>false</code></p>
<p>当你确定可选<em>确实</em>包含值之后,你可以在可选的名字后面加一个感叹号(<code>!</code>)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的<em>强制解析forced unwrapping</em></p>
<pre><code>if convertedNumber {
<p>你可以使用<code>if</code>语句来判断一个可选是否包含值。如果可选类型有值,结果是<code>true</code>;如果没有值,结果是<code>false</code></p>
<p>当你确定可选类型<em>确实</em>包含值之后,你可以在可选的名字后面加一个感叹号(<code>!</code>)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的<em>强制解析forced unwrapping</em></p>
<pre><code class="lang-swift">if convertedNumber {
println(&quot;\(possibleNumber) has an integer value of \(convertedNumber!)&quot;)
} else {
println(&quot;\(possibleNumber) could not be converted to an integer&quot;)
}
// 输出 &quot;123 has an integer value of 123&quot;
</code></pre><p>更多关于<code>if</code>语句的内容,请参考<a href="05_Control_Flow.html">控制流</a></p>
</code></pre>
<p>更多关于<code>if</code>语句的内容,请参考<a href="05_Control_Flow.html">控制流</a></p>
<blockquote>
<p>注意:</p>
<p>使用<code>!</code>来获取一个不存在的可选值会导致运行时错误。使用<code>!</code>来强制解析值之前,一定要确定可选包含一个非<code>nil</code>的值。</p>
<p>注意:<br>使用<code>!</code>来获取一个不存在的可选值会导致运行时错误。使用<code>!</code>来强制解析值之前,一定要确定可选包含一个非<code>nil</code>的值。</p>
</blockquote>
<p><a name="optional_binding"></a></p>
<h3 id="-">可选绑定</h3>
<p>使用<em>可选绑定optional binding</em>来判断可选是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在<code>if</code><code>while</code>语句中来对可选的值进行判断并把值赋给一个常量或者变量。<code>if</code><code>while</code>语句,请参考<a href="05_Control_Flow.html">控制流</a></p>
<p>使用<em>可选绑定optional binding</em>来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在<code>if</code><code>while</code>语句中来对可选类型的值进行判断并把值赋给一个常量或者变量。<code>if</code><code>while</code>语句,请参考<a href="05_Control_Flow.html">控制流</a></p>
<p>像下面这样在<code>if</code>语句中写一个可选绑定:</p>
<pre><code>if let constantName = someOptional {
<pre><code class="lang-swift">if let constantName = someOptional {
statements
}
</code></pre><p>你可以像上面这样使用可选绑定来重写<code>possibleNumber</code>这个例子:</p>
<pre><code>if let actualNumber = possibleNumber.toInt() {
</code></pre>
<p>你可以像上面这样使用可选绑定来重写<code>possibleNumber</code>这个例子:</p>
<pre><code class="lang-swift">if let actualNumber = possibleNumber.toInt() {
println(&quot;\(possibleNumber) has an integer value of \(actualNumber)&quot;)
} else {
println(&quot;\(possibleNumber) could not be converted to an integer&quot;)
}
// 输出 &quot;123 has an integer value of 123&quot;
</code></pre><p>这段代码可以被理解为:</p>
</code></pre>
<p>这段代码可以被理解为:</p>
<p>“如果<code>possibleNumber.toInt</code>返回的可选<code>Int</code>包含一个值,创建一个叫做<code>actualNumber</code>的新常量并将可选包含的值赋给它。”</p>
<p>如果转换成功,<code>actualNumber</code>常量可以在<code>if</code>语句的第一个分支中使用。它已经被可选<em>包含的</em>值初始化过,所以不需要再使用<code>!</code>后缀来获取它的值。在这个例子中,<code>actualNumber</code>只被用来输出转换结果。</p>
<p>你可以在可选绑定中使用常量和变量。如果你想在<code>if</code>语句的第一个分支中操作<code>actualNumber</code>的值,你可以改成<code>if var actualNumber</code>,这样可选包含的值就会被赋给一个变量而非常量。</p>
<p>如果转换成功,<code>actualNumber</code>常量可以在<code>if</code>语句的第一个分支中使用。它已经被可选类型<em>包含的</em>值初始化过,所以不需要再使用<code>!</code>后缀来获取它的值。在这个例子中,<code>actualNumber</code>只被用来输出转换结果。</p>
<p>你可以在可选绑定中使用常量和变量。如果你想在<code>if</code>语句的第一个分支中操作<code>actualNumber</code>的值,你可以改成<code>if var actualNumber</code>,这样可选类型包含的值就会被赋给一个变量而非常量。</p>
<h3 id="nil">nil</h3>
<p>你可以给可选变量赋值为<code>nil</code>来表示它没有值:</p>
<pre><code>var serverResponseCode: Int? = 404
<pre><code class="lang-swift">var serverResponseCode: Int? = 404
// serverResponseCode 包含一个可选的 Int 值 404
serverResponseCode = nil
// serverResponseCode 现在不包含值
</code></pre><blockquote>
<p>注意:</p>
<p><code>nil</code>不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。</p>
</code></pre>
<blockquote>
<p>注意:<br><code>nil</code>不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。</p>
</blockquote>
<p>如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为<code>nil</code></p>
<pre><code>var surveyAnswer: String?
<pre><code class="lang-swift">var surveyAnswer: String?
// surveyAnswer 被自动设置为 nil
</code></pre><blockquote>
<p>注意:</p>
<p>Swift 的<code>nil</code>和 Objective-C 中的<code>nil</code>并不一样。在 Objective-C 中,<code>nil</code>是一个指向不存在对象的指针。在 Swift 中,<code>nil</code>不是指针——它是一个确定的值,用来表示值缺失。<em>任何</em>类型的可选都可以被设置为<code>nil</code>,不只是对象类型。</p>
</blockquote>
<h3 id="-">隐式解析可选</h3>
<p>如上所述,可选暗示了常量或者变量可以“没有值”。可选可以通过<code>if</code>语句来判断是否有值,如果有值的话可以通过可选绑定来解析值。</p>
<p>有时候在程序架构中,第一次被赋值之后,可以确定一个可选<em>总会</em>有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。</p>
<p>这种类型的可选被定义为<em>隐式解析可选implicitly unwrapped optionals</em>。把想要用作可选的类型的后面的问号(<code>String?</code>)改成感叹号(<code>String!</code>)来声明一个隐式解析可选。</p>
<p>当可选被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选非常有用。隐式解析可选主要被用在 Swift 中类的构造过程中,请参考<a href="16_Automatic_Reference_Counting.html#strong_reference_cycles_between_class_instances">类实例之间的循环强引用</a></p>
<p>一个隐式解析可选其实就是一个普通的可选,但是可以被当做非可选来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选<code>String</code>和隐式解析可选<code>String</code>之间的区别:</p>
<pre><code>let possibleString: String? = &quot;An optional string.&quot;
println(possibleString!) // 需要惊叹号来获取值
// 输出 &quot;An optional string.&quot;
let assumedString: String! = &quot;An implicitly unwrapped optional string.&quot;
println(assumedString) // 不需要感叹号
// 输出 &quot;An implicitly unwrapped optional string.&quot;
</code></pre><p>你可以把隐式解析可选当做一个可以自动解析的可选。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。</p>
</code></pre>
<blockquote>
<p>注意:</p>
<p>如果你在隐式解析可选没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选后面加一个惊叹号一样</p>
<p>Swift 的<code>nil</code>和 Objective-C 中的<code>nil</code>并不一样。在 Objective-C 中,<code>nil</code>是一个指向不存在对象的指针。在 Swift 中,<code>nil</code>不是指针——它是一个确定的值,用来表示值缺失。<em>任何</em>类型的可选状态都可以被设置为<code>nil</code>,不只是对象类型</p>
</blockquote>
<p>你仍然可以把隐式解析可选当做普通可选来判断它是否包含值:</p>
<pre><code>if assumedString {
<h3 id="-">隐式解析可选类型</h3>
<p>如上所述,可选类型暗示了常量或者变量可以“没有值”。可选可以通过<code>if</code>语句来判断是否有值,如果有值的话可以通过可选绑定来解析值。</p>
<p>有时候在程序架构中,第一次被赋值之后,可以确定一个可选类型<em>总会</em>有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。</p>
<p>这种类型的可选状态被定义为<em>隐式解析可选类型implicitly unwrapped optionals</em>。把想要用作可选的类型的后面的问号(<code>String?</code>)改成感叹号(<code>String!</code>)来声明一个隐式解析可选类型。</p>
<p>当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考<a href="16_Automatic_Reference_Counting.html#strong_reference_cycles_between_class_instances">类实例之间的循环强引用</a></p>
<p>一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型<code>String</code>和隐式解析可选类型<code>String</code>之间的区别:</p>
<pre><code class="lang-swift">let possibleString: String? = &quot;An optional string.&quot;
println(possibleString!) // 需要惊叹号来获取值
// 输出 &quot;An optional string.&quot;
</code></pre>
<pre><code class="lang-swift">let assumedString: String! = &quot;An implicitly unwrapped optional string.&quot;
println(assumedString) // 不需要感叹号
// 输出 &quot;An implicitly unwrapped optional string.&quot;
</code></pre>
<p>你可以把隐式解析可选类型当做一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。</p>
<blockquote>
<p>注意:</p>
<p>如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。</p>
</blockquote>
<p>你仍然可以把隐式解析可选类型当做普通可选类型来判断它是否包含值:</p>
<pre><code class="lang-swift">if assumedString {
println(assumedString)
}
// 输出 &quot;An implicitly unwrapped optional string.&quot;
</code></pre><p>你也可以在可选绑定中使用隐式解析可选来检查并解析它的值:</p>
<pre><code>if let definiteString = assumedString {
</code></pre>
<p>你也可以在可选绑定中使用隐式解析可选类型来检查并解析它的值:</p>
<pre><code class="lang-swift">if let definiteString = assumedString {
println(definiteString)
}
// 输出 &quot;An implicitly unwrapped optional string.&quot;
</code></pre><blockquote>
</code></pre>
<blockquote>
<p>注意:</p>
<p>如果一个变量之后可能变成<code>nil</code>的话请不要使用隐式解析可选。如果你需要在变量的生命周期中判断是否是<code>nil</code>的话,请使用普通可选类型。</p>
<p>如果一个变量之后可能变成<code>nil</code>的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是<code>nil</code>的话,请使用普通可选类型。</p>
</blockquote>
<p><a name="assertions"></a></p>
<h2 id="-">断言</h2>
<p>可选可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。然而,在某些情况下,如果值缺失或者值并不满足特定的条件,你的代码可能并不需要继续执行。这时,你可以在你的代码中触发一个<em>断言assertion</em>来结束代码运行并通过调试来找到值缺失的原因。</p>
<p>可选类型可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。然而,在某些情况下,如果值缺失或者值并不满足特定的条件,你的代码可能并不需要继续执行。这时,你可以在你的代码中触发一个<em>断言assertion</em>来结束代码运行并通过调试来找到值缺失的原因。</p>
<h3 id="-">使用断言进行调试</h3>
<p>断言会在运行时判断一个逻辑条件是否为<code>true</code>。从字面意思来说,断言“断言”一个条件是否为真。你可以使用断言来保证在运行其他代码之前,某些重要的条件已经被满足。如果条件判断为<code>true</code>,代码运行会继续进行;如果条件判断为<code>false</code>,代码运行停止,你的应用被终止。</p>
<p>如果你的代码在调试环境下触发了一个断言,比如你在 Xcode 中构建并运行一个应用,你可以清楚地看到不合法的状态发生在哪里并检查断言被触发时你的应用的状态。此外,断言允许你附加一条调试信息。</p>
<p>你可以使用全局<code>assert</code>函数来写一个断言。向<code>assert</code>函数传入一个结果为<code>true</code>或者<code>false</code>的表达式以及一条信息,当表达式为<code>false</code>的时候这条信息会被显示:</p>
<pre><code>let age = -3
<pre><code class="lang-swift">let age = -3
assert(age &gt;= 0, &quot;A person&#39;s age cannot be less than zero&quot;)
// 因为 age &lt; 0所以断言会触发
</code></pre><p>在这个例子中,只有<code>age &gt;= 0</code><code>true</code>的时候代码运行才会继续,也就是说,当<code>age</code>的值非负的时候。如果<code>age</code>的值是负数,就像代码中那样,<code>age &gt;= 0</code><code>false</code>,断言被触发,结束应用。</p>
</code></pre>
<p>在这个例子中,只有<code>age &gt;= 0</code><code>true</code>的时候代码运行才会继续,也就是说,当<code>age</code>的值非负的时候。如果<code>age</code>的值是负数,就像代码中那样,<code>age &gt;= 0</code><code>false</code>,断言被触发,结束应用。</p>
<p>断言信息不能使用字符串插值。断言信息可以省略,就像这样:</p>
<pre><code>assert(age &gt;= 0)
</code></pre><h3 id="-">何时使用断言</h3>
<pre><code class="lang-swift">assert(age &gt;= 0)
</code></pre>
<h3 id="-">何时使用断言</h3>
<p>当条件可能为假时使用断言,但是最终一定要<em>保证</em>条件为真,这样你的代码才能继续运行。断言的适用情景:</p>
<ul>
<li>整数的附属脚本索引被传入一个自定义附属脚本实现,但是下标索引值可能太小或者太大。</li>
<li>整数类型的下标索引被传入一个自定义下标脚本实现,但是下标索引值可能太小或者太大。</li>
<li>需要给函数传入一个值,但是非法的值可能导致函数不能正常执行。</li>
<li>一个可选值现在是<code>nil</code>,但是后面的代码运行需要一个非<code>nil</code>值。</li>
</ul>
<p>请参考<a href="12_Subscripts.html">附属脚本</a><a href="06_Functions.html">函数</a></p>
<p>请参考<a href="12_Subscripts.html">下标脚本</a><a href="06_Functions.html">函数</a></p>
<blockquote>
<p>注意:</p>
<p>断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。</p>
<p>注意:<br>断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。</p>
</blockquote>

34
chapter2/02_Basic_Operators.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.2" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.2" 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_111">
<section class="normal" id="section-gitbook_55">
<blockquote>
<p>翻译xielingwang</p>
<p>校对Evilcome</p>
<p>翻译xielingwang<br>校对Evilcome </p>
</blockquote>
<h1 id="-">基本运算符</h1>
<hr>
@ -628,9 +627,10 @@ a = b
// a 现在等于 10
</code></pre>
<p>如果赋值的右边是一个多元组,它的元素可以马上被分解多个变量或变量:</p>
<pre><code>let (x, y) = (1, 2)
<pre><code class="lang-swiflt">let (x, y) = (1, 2)
// 现在 x 等于 1, y 等于 2
</code></pre><p>与 C 语言和 Objective-C 不同Swift 的赋值操作并不返回任何值。所以以下代码是错误的:</p>
</code></pre>
<p>与 C 语言和 Objective-C 不同Swift 的赋值操作并不返回任何值。所以以下代码是错误的:</p>
<pre><code class="lang-swift">if x = y {
// 此句错误, 因为 x = y 并不返回任何值
}
@ -665,8 +665,7 @@ let dogCow = dog + cow
<h3 id="-">求余运算</h3>
<p>求余运算(<code>a % b</code>)是计算<code>b</code>的多少倍刚刚好可以容入<code>a</code>,返回多出来的那部分(余数)。</p>
<blockquote>
<p>注意:</p>
<p>求余运算(<code>%</code>)在其他语言也叫取模运算。然而严格说来,我们看该运算符对负数的操作结果,&quot;求余&quot;&quot;取模&quot;更合适些。</p>
<p>注意:<br>求余运算(<code>%</code>)在其他语言也叫取模运算。然而严格说来,我们看该运算符对负数的操作结果,&quot;求余&quot;&quot;取模&quot;更合适些。</p>
</blockquote>
<p>我们来谈谈取余是怎么回事,计算<code>9 % 4</code>,你先计算出<code>4</code>的多少倍会刚好可以容入<code>9</code>中:</p>
<p><img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/remainderInteger_2x.png" alt="Art/remainderInteger_2x.png" title="Art/remainderInteger_2x.png"></p>
@ -738,8 +737,7 @@ a += 2 // a 现在是 3
</code></pre>
<p>表达式<code>a += 2</code><code>a = a + 2</code>的简写,一个加赋运算就把加法和赋值两件事完成了。</p>
<blockquote>
<p>注意:</p>
<p>复合赋值运算没有返回值,<code>let b = a += 2</code>这类代码是错误。这不同于上面提到的自增和自减运算符。</p>
<p>注意:<br>复合赋值运算没有返回值,<code>let b = a += 2</code>这类代码是错误。这不同于上面提到的自增和自减运算符。</p>
</blockquote>
<p><a href="../chapter3/04_Expressions.html">表达式</a>章节里有复合运算符的完整列表。
@ -755,8 +753,7 @@ a += 2 // a 现在是 3
<li>小于等于(<code>a &lt;= b</code></li>
</ul>
<blockquote>
<p>注意:</p>
<p>Swift 也提供恒等<code>===</code>和不恒等<code>!==</code>这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在<a href="09_Classes_and_Structures.html">类与结构</a></p>
<p>注意:<br>Swift 也提供恒等<code>===</code>和不恒等<code>!==</code>这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在<a href="09_Classes_and_Structures.html">类与结构</a></p>
</blockquote>
<p>每个比较运算都返回了一个标识表达式是否成立的布尔值:</p>
<pre><code class="lang-swift">1 == 1 // true, 因为 1 等于 1
@ -781,10 +778,9 @@ if name == &quot;world&quot; {
<p>三元条件运算的特殊在于它是有三个操作数的运算符,它的原型是 <code>问题 ? 答案1 : 答案2</code>。它简洁地表达根据<code>问题</code>成立与否作出二选一的操作。如果<code>问题</code>成立,返回<code>答案1</code>的结果; 如果不成立,返回<code>答案2</code>的结果。</p>
<p>使用三元条件运算简化了以下代码:</p>
<pre><code class="lang-swift">if question: {
answer1
}
else {
answer2
answer1
} else {
answer2
}
</code></pre>
<p>这里有个计算表格行高的例子。如果有表头那行高应比内容高度要高出50像素; 如果没有表头只需高出20像素。</p>
@ -814,7 +810,7 @@ if hasHeader {
闭区间运算符在迭代一个区间的所有值时是非常有用的,如在<code>for-in</code>循环中:</p>
<pre><code class="lang-swift">for index in 1...5 {
println(&quot;\(index) * 5 = \(index * 5)&quot;)
println(&quot;\(index) * 5 = \(index * 5)&quot;)
}
// 1 * 5 = 5
// 2 * 5 = 10

145
chapter2/03_Strings_and_Characters.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.3" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.3" 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_92">
<section class="normal" id="section-gitbook_39">
<blockquote>
<p>翻译wh1100717</p>
<p>校对Hawstein</p>
<p>翻译wh1100717<br>校对Hawstein </p>
</blockquote>
<h1 id="-strings-and-characters-">字符串和字符Strings and Characters</h1>
<hr>
@ -618,19 +617,18 @@
每一个字符串都是由独立编码的 Unicode 字符组成,并提供了以不同 Unicode 表示representations来访问这些字符的支持。</p>
<p>Swift 可以在常量、变量、字面量和表达式中进行字符串插值操作,可以轻松创建用于展示、存储和打印的自定义字符串。</p>
<blockquote>
<p>注意:</p>
<p>Swift 的<code>String</code>类型与 Foundation <code>NSString</code>类进行了无缝桥接。如果您利用 Cocoa 或 Cocoa Touch 中的 Foundation 框架进行工作。所有<code>NSString</code> API 都可以调用您创建的任意<code>String</code>类型的值。除此之外,还可以使用本章介绍的<code>String</code>特性。您也可以在任意要求传入<code>NSString</code>实例作为参数的 API 中使用<code>String</code>类型的值作为替代。</p>
<p>更多关于在 Foundation 和 Cocoa 中使用<code>String</code>的信息请查看 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216" target="_blank">Using Swift with Cocoa and Objective-C</a></p>
<p>注意:<br>Swift 的<code>String</code>类型与 Foundation <code>NSString</code>类进行了无缝桥接。如果您利用 Cocoa 或 Cocoa Touch 中的 Foundation 框架进行工作。所有<code>NSString</code> API 都可以调用您创建的任意<code>String</code>类型的值。除此之外,还可以使用本章介绍的<code>String</code>特性。您也可以在任意要求传入<code>NSString</code>实例作为参数的 API 中使用<code>String</code>类型的值作为替代。
更多关于在 Foundation 和 Cocoa 中使用<code>String</code>的信息请查看 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216" target="_blank">Using Swift with Cocoa and Objective-C</a></p>
</blockquote>
<p><a name="string_literals"></a></p>
<h2 id="-string-literals-">字符串字面量String Literals</h2>
<p>您可以在您的代码中包含一段预定义的字符串值作为字符串字面量。
字符串字面量是由双引号 (&quot;&quot;) 包裹着的具有固定顺序的文本字符集。</p>
<p>字符串字面量可以用于为常量和变量提供初始值。</p>
<pre><code>let someString = &quot;Some string literal value&quot;
</code></pre><blockquote>
<p>注意:</p>
<p><code>someString</code>变量通过字符串字面量进行初始化Swift 因此推断该变量为<code>String</code>类型。</p>
<pre><code class="lang-swift">let someString = &quot;Some string literal value&quot;
</code></pre>
<blockquote>
<p>注意:<br><code>someString</code>变量通过字符串字面量进行初始化Swift 因此推断该变量为<code>String</code>类型。</p>
</blockquote>
<p>字符串字面量可以包含以下特殊字符:</p>
<ul>
@ -642,35 +640,38 @@
<p>下面的代码为各种特殊字符的使用示例。
<code>wiseWords</code>常量包含了两个转移特殊字符 (双括号)
<code>dollarSign</code><code>blackHeart</code><code>sparklingHeart</code>常量演示了三种不同格式的 Unicode 标量:</p>
<pre><code>let wiseWords = &quot;\&quot;我是要成为海贼王的男人\&quot; - 路飞&quot;
<pre><code class="lang-swift">let wiseWords = &quot;\&quot;我是要成为海贼王的男人\&quot; - 路飞&quot;
// &quot;我是要成为海贼王的男人&quot; - 路飞
let dollarSign = &quot;\x24&quot; // $, Unicode 标量 U+0024
let blackHeart = &quot;\u2665&quot; // ♥, Unicode 标量 U+2665
let sparklingHeart = &quot;\U0001F496&quot; // 💖, Unicode 标量 U+1F496
</code></pre><p><a name="initializing_an_empty_string"></a></p>
let dollarSign = &quot;\x24&quot; // $, Unicode 标量 U+0024
let blackHeart = &quot;\u2665&quot; // ♥, Unicode 标量 U+2665
let sparklingHeart = &quot;\U0001F496&quot; // 💖, Unicode 标量 U+1F496
</code></pre>
<p><a name="initializing_an_empty_string"></a></p>
<h2 id="-initializing-an-empty-string-">初始化空字符串 (Initializing an Empty String)</h2>
<p>为了构造一个很长的字符串,可以创建一个空字符串作为初始值。
可以将空的字符串字面量赋值给变量,也可以初始化一个新的<code>String</code>实例:</p>
<pre><code>var emptyString = &quot;&quot; // 空字符串字面量
<pre><code class="lang-swift">var emptyString = &quot;&quot; // 空字符串字面量
var anotherEmptyString = String() // 初始化 String 实例
// 两个字符串均为空并等价。
</code></pre><p>您可以通过检查其<code>Boolean</code>类型的<code>isEmpty</code>属性来判断该字符串是否为空:</p>
<pre><code>if emptyString.isEmpty {
</code></pre>
<p>您可以通过检查其<code>Boolean</code>类型的<code>isEmpty</code>属性来判断该字符串是否为空:</p>
<pre><code class="lang-swift">if emptyString.isEmpty {
println(&quot;什么都没有&quot;)
}
// 打印输出:&quot;什么都没有&quot;
</code></pre><p><a name="string_mutability"></a></p>
</code></pre>
<p><a name="string_mutability"></a></p>
<h2 id="-string-mutability-">字符串可变性 (String Mutability)</h2>
<p>您可以通过将一个特定字符串分配给一个变量来对其进行修改,或者分配给一个常量来保证其不会被修改:</p>
<pre><code>var variableString = &quot;Horse&quot;
<pre><code class="lang-swift">var variableString = &quot;Horse&quot;
variableString += &quot; and carriage&quot;
// variableString 现在为 &quot;Horse and carriage&quot;
let constantString = &quot;Highlander&quot;
constantString += &quot; and another Highlander&quot;
// 这会报告一个编译错误 (compile-time error) - 常量不可以被修改。
</code></pre><blockquote>
<p>注意:</p>
<p>在 Objective-C 和 Cocoa 中,您通过选择两个不同的类(<code>NSString</code><code>NSMutableString</code>)来指定该字符串是否可以被修改Swift 中的字符串是否可以修改仅通过定义的是变量还是常量来决定,实现了多种类型可变性操作的统一。</p>
</code></pre>
<blockquote>
<p>注意:<br>在 Objective-C 和 Cocoa 中,您通过选择两个不同的类(<code>NSString</code><code>NSMutableString</code>)来指定该字符串是否可以被修改Swift 中的字符串是否可以修改仅通过定义的是变量还是常量来决定,实现了多种类型可变性操作的统一。</p>
</blockquote>
<p><a name="strings_are_value_types"></a></p>
<h2 id="-strings-are-value-types-">字符串是值类型Strings Are Value Types</h2>
@ -679,8 +680,7 @@ constantString += &quot; and another Highlander&quot;
任何情况下,都会对已有字符串值创建新副本,并对该新副本进行传递或赋值操作。
值类型在 <a href="09_Classes_and_Structures.html#structures_and_enumerations_are_value_types">结构体和枚举是值类型</a> 中进行了说明。</p>
<blockquote>
<p>注意:</p>
<p>与 Cocoa 中的<code>NSString</code>不同,当您在 Cocoa 中创建了一个<code>NSString</code>实例,并将其传递给一个函数/方法,或者赋值给一个变量,您传递或赋值的是该<code>NSString</code>实例的一个引用,除非您特别要求进行值拷贝,否则字符串不会生成新的副本来进行赋值操作。</p>
<p>注意:<br>与 Cocoa 中的<code>NSString</code>不同,当您在 Cocoa 中创建了一个<code>NSString</code>实例,并将其传递给一个函数/方法,或者赋值给一个变量,您传递或赋值的是该<code>NSString</code>实例的一个引用,除非您特别要求进行值拷贝,否则字符串不会生成新的副本来进行赋值操作。</p>
</blockquote>
<p>Swift 默认字符串拷贝的方式保证了在函数/方法中传递的是字符串的值。
很明显无论该值来自于哪里,都是您独自拥有的。
@ -691,7 +691,7 @@ constantString += &quot; and another Highlander&quot;
<p>Swift 的<code>String</code>类型表示特定序列的<code>Character</code>(字符) 类型值的集合。
每一个字符值代表一个 Unicode 字符。
您可利用<code>for-in</code>循环来遍历字符串中的每一个字符:</p>
<pre><code>for character in &quot;Dog!🐶&quot; {
<pre><code class="lang-swift">for character in &quot;Dog!🐶&quot; {
println(character)
}
// D
@ -699,24 +699,26 @@ constantString += &quot; and another Highlander&quot;
// g
// !
// 🐶
</code></pre><p>for-in 循环在 <a href="05_Control_Flow.html#for_loops">For Loops</a> 中进行了详细描述。</p>
</code></pre>
<p>for-in 循环在 <a href="05_Control_Flow.html#for_loops">For Loops</a> 中进行了详细描述。</p>
<p>另外,通过标明一个<code>Character</code>类型注解并通过字符字面量进行赋值,可以建立一个独立的字符常量或变量:</p>
<pre><code>let yenSign: Character = &quot;¥&quot;
</code></pre><p><a name="counting_characters"></a></p>
<pre><code class="lang-swift">let yenSign: Character = &quot;¥&quot;
</code></pre>
<p><a name="counting_characters"></a></p>
<h2 id="-counting-characters-">计算字符数量 (Counting Characters)</h2>
<p>通过调用全局<code>countElements</code>函数,并将字符串作为参数进行传递,可以获取该字符串的字符数量。</p>
<pre><code>let unusualMenagerie = &quot;Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪&quot;
<pre><code class="lang-swift">let unusualMenagerie = &quot;Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪&quot;
println(&quot;unusualMenagerie has \(countElements(unusualMenagerie)) characters&quot;)
// 打印输出:&quot;unusualMenagerie has 40 characters&quot;
</code></pre><blockquote>
<p>注意:</p>
<p>不同的 Unicode 字符以及相同 Unicode 字符的不同表示方式可能需要不同数量的内存空间来存储。所以 Swift 中的字符在一个字符串中并不一定占用相同的内存空间。因此字符串的长度不得不通过迭代字符串中每一个字符的长度来进行计算。如果您正在处理一个长字符串,需要注意<code>countElements</code>函数必须遍历字符串中的字符以精准计算字符串的长度。</p>
<p>另外需要注意的是通过<code>countElements</code>返回的字符数量并不总是与包含相同字符的<code>NSString</code><code>length</code>属性相同。<code>NSString</code><code>length</code>属性是基于利用 UTF-16 表示的十六位代码单元数字,而不是基于 Unicode 字符。为了解决这个问题,<code>NSString</code><code>length</code>属性在被 Swift 的<code>String</code>访问时会成为<code>utf16count</code></p>
</code></pre>
<blockquote>
<p>注意:<br>不同的 Unicode 字符以及相同 Unicode 字符的不同表示方式可能需要不同数量的内存空间来存储。所以 Swift 中的字符在一个字符串中并不一定占用相同的内存空间。因此字符串的长度不得不通过迭代字符串中每一个字符的长度来进行计算。如果您正在处理一个长字符串,需要注意<code>countElements</code>函数必须遍历字符串中的字符以精准计算字符串的长度。
另外需要注意的是通过<code>countElements</code>返回的字符数量并不总是与包含相同字符的<code>NSString</code><code>length</code>属性相同。<code>NSString</code><code>length</code>属性是基于利用 UTF-16 表示的十六位代码单元数字,而不是基于 Unicode 字符。为了解决这个问题,<code>NSString</code><code>length</code>属性在被 Swift 的<code>String</code>访问时会成为<code>utf16count</code> </p>
</blockquote>
<p><a name="concatenating_strings_and_characters"></a></p>
<h2 id="-concatenating-strings-and-characters-">连接字符串和字符 (Concatenating Strings and Characters)</h2>
<p>字符串和字符的值可以通过加法运算符(<code>+</code>)相加在一起并创建一个新的字符串值:</p>
<pre><code>let string1 = &quot;hello&quot;
<pre><code class="lang-swift">let string1 = &quot;hello&quot;
let string2 = &quot; there&quot;
let character1: Character = &quot;!&quot;
let character2: Character = &quot;?&quot;
@ -725,33 +727,34 @@ let stringPlusCharacter = string1 + character1 // 等于 &quot;hello!&quo
let stringPlusString = string1 + string2 // 等于 &quot;hello there&quot;
let characterPlusString = character1 + string1 // 等于 &quot;!hello&quot;
let characterPlusCharacter = character1 + character2 // 等于 &quot;!?&quot;
</code></pre><p>您也可以通过加法赋值运算符 (<code>+=</code>) 将一个字符串或者字符添加到一个已经存在字符串变量上:</p>
<pre><code>var instruction = &quot;look over&quot;
</code></pre>
<p>您也可以通过加法赋值运算符 (<code>+=</code>) 将一个字符串或者字符添加到一个已经存在字符串变量上:</p>
<pre><code class="lang-swift">var instruction = &quot;look over&quot;
instruction += string2
// instruction 现在等于 &quot;look over there&quot;
var welcome = &quot;good morning&quot;
welcome += character1
// welcome 现在等于 &quot;good morning!&quot;
</code></pre><blockquote>
<p>注意:</p>
<p>您不能将一个字符串或者字符添加到一个已经存在的字符变量上,因为字符变量只能包含一个字符。</p>
</code></pre>
<blockquote>
<p>注意:<br>您不能将一个字符串或者字符添加到一个已经存在的字符变量上,因为字符变量只能包含一个字符。</p>
</blockquote>
<p><a name="string_interpolation"></a></p>
<h2 id="-string-interpolation-">字符串插值 (String Interpolation)</h2>
<p>字符串插值是一种构建新字符串的方式,可以在其中包含常量、变量、字面量和表达式。
您插入的字符串字面量的每一项都被包裹在以反斜线为前缀的圆括号中:</p>
<pre><code>let multiplier = 3
<pre><code class="lang-swift">let multiplier = 3
let message = &quot;\(multiplier) 乘以 2.5 是 \(Double(multiplier) * 2.5)&quot;
// message 是 &quot;3 乘以 2.5 是 7.5&quot;
</code></pre><p>在上面的例子中,<code>multiplier</code>作为<code>\(multiplier)</code>被插入到一个字符串字面量中。
</code></pre>
<p>在上面的例子中,<code>multiplier</code>作为<code>\(multiplier)</code>被插入到一个字符串字面量中。
当创建字符串执行插值计算时此占位符会被替换为<code>multiplier</code>实际的值。</p>
<p><code>multiplier</code>的值也作为字符串中后面表达式的一部分。
该表达式计算<code>Double(multiplier) * 2.5</code>的值并将结果 (7.5) 插入到字符串中。
在这个例子中,表达式写为<code>\(Double(multiplier) * 2.5)</code>并包含在字符串字面量中。</p>
<blockquote>
<p>注意:</p>
<p>插值字符串中写在括号中的表达式不能包含非转义双引号 (<code>&quot;</code>) 和反斜杠 (<code>\</code>),并且不能包含回车或换行符。</p>
<p>注意:<br>插值字符串中写在括号中的表达式不能包含非转义双引号 (<code>&quot;</code>) 和反斜杠 (<code>\</code>),并且不能包含回车或换行符。</p>
</blockquote>
<p><a name="comparing_strings"></a></p>
<h2 id="-comparing-strings-">比较字符串 (Comparing Strings)</h2>
@ -759,19 +762,20 @@ let message = &quot;\(multiplier) 乘以 2.5 是 \(Double(multiplier) * 2.5)&quo
<p><a name="string_equality"></a></p>
<h3 id="-string-equality-">字符串相等 (String Equality)</h3>
<p>如果两个字符串以同一顺序包含完全相同的字符,则认为两者字符串相等:</p>
<pre><code>let quotation = &quot;我们是一样一样滴.&quot;
<pre><code class="lang-swift">let quotation = &quot;我们是一样一样滴.&quot;
let sameQuotation = &quot;我们是一样一样滴.&quot;
if quotation == sameQuotation {
println(&quot;这两个字符串被认为是相同的&quot;)
}
// 打印输出:&quot;这两个字符串被认为是相同的&quot;
</code></pre><p><a name="prefix_and_suffix_equality"></a></p>
</code></pre>
<p><a name="prefix_and_suffix_equality"></a></p>
<h3 id="-prefix-and-suffix-equality-">前缀/后缀相等 (Prefix and Suffix Equality)</h3>
<p>通过调用字符串的<code>hasPrefix</code>/<code>hasSuffix</code>方法来检查字符串是否拥有特定前缀/后缀。
两个方法均需要以字符串作为参数传入并传出<code>Boolean</code>值。
两个方法均执行基本字符串和前缀/后缀字符串之间逐个字符的比较操作。</p>
<p>下面的例子以一个字符串数组表示莎士比亚话剧《罗密欧与朱丽叶》中前两场的场景位置:</p>
<pre><code>let romeoAndJuliet = [
<pre><code class="lang-swift">let romeoAndJuliet = [
&quot;Act 1 Scene 1: Verona, A public place&quot;,
&quot;Act 1 Scene 2: Capulet&#39;s mansion&quot;,
&quot;Act 1 Scene 3: A room in Capulet&#39;s mansion&quot;,
@ -784,8 +788,9 @@ if quotation == sameQuotation {
&quot;Act 2 Scene 5: Capulet&#39;s mansion&quot;,
&quot;Act 2 Scene 6: Friar Lawrence&#39;s cell&quot;
]
</code></pre><p>您可以利用<code>hasPrefix</code>方法来计算话剧中第一幕的场景数:</p>
<pre><code>var act1SceneCount = 0
</code></pre>
<p>您可以利用<code>hasPrefix</code>方法来计算话剧中第一幕的场景数:</p>
<pre><code class="lang-swift">var act1SceneCount = 0
for scene in romeoAndJuliet {
if scene.hasPrefix(&quot;Act 1 &quot;) {
++act1SceneCount
@ -793,8 +798,9 @@ for scene in romeoAndJuliet {
}
println(&quot;There are \(act1SceneCount) scenes in Act 1&quot;)
// 打印输出:&quot;There are 5 scenes in Act 1&quot;
</code></pre><p>相似地,您可以用<code>hasSuffix</code>方法来计算发生在不同地方的场景数:</p>
<pre><code>var mansionCount = 0
</code></pre>
<p>相似地,您可以用<code>hasSuffix</code>方法来计算发生在不同地方的场景数:</p>
<pre><code class="lang-swift">var mansionCount = 0
var cellCount = 0
for scene in romeoAndJuliet {
if scene.hasSuffix(&quot;Capulet&#39;s mansion&quot;) {
@ -805,15 +811,17 @@ for scene in romeoAndJuliet {
}
println(&quot;\(mansionCount) mansion scenes; \(cellCount) cell scenes&quot;)
// 打印输出:&quot;6 mansion scenes; 2 cell scenes”
</code></pre><p><a name="uppercase_and_lowercase_strings"></a></p>
</code></pre>
<p><a name="uppercase_and_lowercase_strings"></a></p>
<h3 id="-uppercase-and-lowercase-strings-">大写和小写字符串Uppercase and Lowercase Strings</h3>
<p>您可以通过字符串的<code>uppercaseString</code><code>lowercaseString</code>属性来访问大写/小写版本的字符串。</p>
<pre><code>let normal = &quot;Could you help me, please?&quot;
<pre><code class="lang-swift">let normal = &quot;Could you help me, please?&quot;
let shouty = normal.uppercaseString
// shouty 值为 &quot;COULD YOU HELP ME, PLEASE?&quot;
let whispered = normal.lowercaseString
// whispered 值为 &quot;could you help me, please?&quot;
</code></pre><p><a name="unicode"></a></p>
</code></pre>
<p><a name="unicode"></a></p>
<h2 id="unicode">Unicode</h2>
<p>Unicode 是一个国际标准,用于文本的编码和表示。
它使您可以用标准格式表示来自任意语言几乎所有的字符,并能够对文本文件或网页这样的外部资源中的字符进行读写操作。</p>
@ -835,28 +843,31 @@ let whispered = normal.lowercaseString
<li>21位的 Unicode 标量值集合 (利用字符串的<code>unicodeScalars</code>属性进行访问)</li>
</ul>
<p>下面由<code>D``o``g``!</code><code>🐶</code>(<code>DOG FACE</code>Unicode 标量为<code>U+1F436</code>)组成的字符串中的每一个字符代表着一种不同的表示:</p>
<pre><code>let dogString = &quot;Dog!🐶&quot;
</code></pre><p><a name="UTF-8"></a></p>
<pre><code class="lang-swift">let dogString = &quot;Dog!🐶&quot;
</code></pre>
<p><a name="UTF-8"></a></p>
<h3 id="utf-8">UTF-8</h3>
<p>您可以通过遍历字符串的<code>utf8</code>属性来访问它的<code>UTF-8</code>表示。
其为<code>UTF8View</code>类型的属性,<code>UTF8View</code>是无符号8位 (<code>UInt8</code>) 值的集合,每一个<code>UInt8</code>值都是一个字符的 UTF-8 表示:</p>
<pre><code>for codeUnit in dogString.utf8 {
<pre><code class="lang-swift">for codeUnit in dogString.utf8 {
print(&quot;\(codeUnit) &quot;)
}
print(&quot;\n&quot;)
// 68 111 103 33 240 159 144 182
</code></pre><p>上面的例子中前四个10进制代码单元值 (68, 111, 103, 33) 代表了字符<code>D</code> <code>o</code> <code>g</code><code>!</code>,他们的 UTF-8 表示与 ASCII 表示相同。
</code></pre>
<p>上面的例子中前四个10进制代码单元值 (68, 111, 103, 33) 代表了字符<code>D</code> <code>o</code> <code>g</code><code>!</code>,它们的 UTF-8 表示与 ASCII 表示相同。
后四个代码单元值 (240, 159, 144, 182) 是<code>DOG FACE</code>的4字节 UTF-8 表示。</p>
<p><a name="UTF-16"></a></p>
<h3 id="utf-16">UTF-16</h3>
<p>您可以通过遍历字符串的<code>utf16</code>属性来访问它的<code>UTF-16</code>表示。
其为<code>UTF16View</code>类型的属性,<code>UTF16View</code>是无符号16位 (<code>UInt16</code>) 值的集合,每一个<code>UInt16</code>都是一个字符的 UTF-16 表示:</p>
<pre><code>for codeUnit in dogString.utf16 {
<pre><code class="lang-swift">for codeUnit in dogString.utf16 {
print(&quot;\(codeUnit) &quot;)
}
print(&quot;\n&quot;)
// 68 111 103 33 55357 56374
</code></pre><p>同样,前四个代码单元值 (68, 111, 103, 33) 代表了字符<code>D</code> <code>o</code> <code>g</code><code>!</code>,他们的 UTF-16 代码单元和 UTF-8 完全相同。</p>
</code></pre>
<p>同样,前四个代码单元值 (68, 111, 103, 33) 代表了字符<code>D</code> <code>o</code> <code>g</code><code>!</code>,它们的 UTF-16 代码单元和 UTF-8 完全相同。</p>
<p>第五和第六个代码单元值 (55357 和 56374) 是<code>DOG FACE</code>字符的UTF-16 表示。
第一个值为<code>U+D83D</code>(十进制值为 55357),第二个值为<code>U+DC36</code>(十进制值为 56374)。</p>
<p><a name="unicode_scalars"></a></p>
@ -865,16 +876,17 @@ print(&quot;\n&quot;)
其为<code>UnicodeScalarView</code>类型的属性, <code>UnicodeScalarView</code><code>UnicodeScalar</code>的集合。
<code>UnicodeScalar</code>是21位的 Unicode 代码点。</p>
<p>每一个<code>UnicodeScalar</code>拥有一个值属性可以返回对应的21位数值<code>UInt32</code>来表示。</p>
<pre><code>for scalar in dogString.unicodeScalars {
<pre><code class="lang-swift">for scalar in dogString.unicodeScalars {
print(&quot;\(scalar.value) &quot;)
}
print(&quot;\n&quot;)
// 68 111 103 33 128054
</code></pre><p>同样,前四个代码单元值 (68, 111, 103, 33) 代表了字符<code>D</code> <code>o</code> <code>g</code><code>!</code>
</code></pre>
<p>同样,前四个代码单元值 (68, 111, 103, 33) 代表了字符<code>D</code> <code>o</code> <code>g</code><code>!</code>
第五位数值128054是一个十六进制1F436的十进制表示。
其等同于<code>DOG FACE</code>的Unicode 标量 U+1F436。</p>
<p>作为查询字符值属性的一种替代方法,每个<code>UnicodeScalar</code>值也可以用来构建一个新的字符串值,比如在字符串插值中使用:</p>
<pre><code>for scalar in dogString.unicodeScalars {
<pre><code class="lang-swift">for scalar in dogString.unicodeScalars {
println(&quot;\(scalar) &quot;)
}
// D
@ -883,6 +895,7 @@ print(&quot;\n&quot;)
// !
// 🐶
</code></pre>
</section>

278
chapter2/04_Collection_Types.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.4" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.4" 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,11 @@
<div class="page-inner">
<section class="normal" id="section-gitbook_94">
<section class="normal" id="section-gitbook_41">
<blockquote>
<p>翻译zqp</p>
<p>校对shinyzhu</p>
<p>翻译zqp
校对shinyzhu</p>
</blockquote>
<h1 id="-collection-types-">集合类型 (Collection Types)</h1>
<hr>
@ -604,90 +604,120 @@
<p>Swift 语言提供经典的数组和字典两种集合类型来存储集合数据。数组用来按顺序存储相同类型的数据。字典虽然无序存储相同类型数据值但是需要由独有的标识符引用和寻址(就是键值对)。</p>
<p>Swift 语言里的数组和字典中存储的数据值类型必须明确。 这意味着我们不能把不正确的数据类型插入其中。 同时这也说明我们完全可以对获取出的值类型非常自信。 Swift 对显式类型集合的使用确保了我们的代码对工作所需要的类型非常清楚,也让我们在开发中可以早早地找到任何的类型不匹配错误。</p>
<blockquote>
<p>注意:</p>
<p>Swift 的数组结构在被声明成常量和变量或者被传入函数与方法中时会相对于其他类型展现出不同的特性。 获取更多信息请参见<a href="#mutability_of_collections">集合的可变性</a><a href="09_Classes_and_Structures.html#assignment_and_copy_behavior_for_collection_types">集合在赋值和复制中的行为</a>章节。</p>
<p>注意:
Swift 的数组结构在被声明成常量和变量或者被传入函数与方法中时会相对于其他类型展现出不同的特性。 获取更多信息请参见<a href="#mutability_of_collections">集合的可变性</a><a href="09_Classes_and_Structures.html#assignment_and_copy_behavior_for_collection_types">集合在赋值和复制中的行为</a>章节。</p>
</blockquote>
<p><a name="arrays"></a></p>
<h2 id="-">数组</h2>
<p>数组使用有序列表存储同类型的多重数据。相同的值可以多次出现在一个数组的不同位置中。</p>
<p>Swift 数组对存储数据有具体要求。 不同于 Objective-C 的<code>NSArray</code><code>NSMutableArray</code>类,他们可以存储任类型的实例而且不提供他们返回对象的任何本质信息。 在 Swift 中,数据值在被存储进入某个数组之前类型必须明确,方法是通过显式的类型标注或类型推断,而且不是必须是<code>class</code>类型。例如: 如果我们创建了一个<code>Int</code>值类型的数组,我们不能往其中插入任何不是<code>Int</code>类型的数据。 Swift 中的数组是类型安全的,并且它们中包含的类型必须明确。</p>
<p>数组使用有序列表存储同类型的多个值。相同的值可以多次出现在一个数组的不同位置中。</p>
<p>Swift 数组特定于它所存储元素的类型。这与 Objective-C 的 NSArray 和 NSMutableArray 不同,这两个类可以存储任类型的对象,并且不提供返回对象的任何特别信息。在 Swift 中,数据值在被存储进入某个数组之前类型必须明确,方法是通过显式的类型标注或类型推断,而且不是必须是<code>class</code>类型。例如: 如果我们创建了一个<code>Int</code>值类型的数组,我们不能往其中插入任何不是<code>Int</code>类型的数据。 Swift 中的数组是类型安全的,并且它们中包含的类型必须明确。</p>
<p><a name="array_type_shorthand_syntax"></a></p>
<h3 id="-">数组的简单语法</h3>
<p>写 Swift 数组应该遵循像<code>Array&lt;SomeType&gt;</code>这样的形式,其中<code>SomeType</code>是这个数组中唯一允许存在的数据类型。 我们也可以使用像<code>SomeType[]</code>这样的简单语法。 尽管两种形式在功能上是一样的,但是推荐较短的那种,而且在本文中都会使用这种形式来使用数组。</p>
<p><a name="array_literals"></a></p>
<h3 id="-">数组构造语句</h3>
<p>我们可以使用字面语句来进行数组构造,这是一种用一个或者多个数值构造数组的简单方法。字面语句是一系列由逗号分割并由方括号包含的数值。
<p>我们可以使用字面来进行数组构造,这是一种用一个或者多个数值构造数组的简单方法。字面是一系列由逗号分割并由方括号包含的数值。
<code>[value 1, value 2, value 3]</code></p>
<p>下面这个例子创建了一个叫做<code>shoppingList</code>并且存储字符串的数组:</p>
<pre><code>var shoppingList: String[] = [&quot;Eggs&quot;, &quot;Milk&quot;]
<pre><code class="lang-swift">var shoppingList: String[] = [&quot;Eggs&quot;, &quot;Milk&quot;]
// shoppingList 已经被构造并且拥有两个初始项。
</code></pre><p><code>shoppingList</code>变量被声明为“字符串值类型的数组“,记作<code>String[]</code>。 因为这个数组被规定只有<code>String</code>一种数据结构,所以只有<code>String</code>类型可以在其中被存取。 在这里,<code>shoppinglist</code>数组由两个<code>String</code>值(<code>&quot;Eggs&quot;</code><code>&quot;Milk&quot;</code>)构造,并且由字面语句定义。</p>
</code></pre>
<p><code>shoppingList</code>变量被声明为“字符串值类型的数组“,记作<code>String[]</code>。 因为这个数组被规定只有<code>String</code>一种数据结构,所以只有<code>String</code>类型可以在其中被存取。 在这里,<code>shoppinglist</code>数组由两个<code>String</code>值(<code>&quot;Eggs&quot;</code><code>&quot;Milk&quot;</code>)构造,并且由字面量定义。</p>
<blockquote>
<p>注意:</p>
<p><code>Shoppinglist</code>数组被声明为变量(<code>var</code>关键字创建)而不是常量(<code>let</code>创建)是因为以后可能会有更多的数据项被插入其中。</p>
<p>注意:
<code>Shoppinglist</code>数组被声明为变量(<code>var</code>关键字创建)而不是常量(<code>let</code>创建)是因为以后可能会有更多的数据项被插入其中。</p>
</blockquote>
<p>在这个例子中,字面语句仅仅包含两个<code>String</code>值。匹配了该数组的变量声明(只能包含<code>String</code>的数组),所以这个字面语句的分配过程就是允许用两个初始项来构造<code>shoppinglist</code></p>
<p>由于 Swift 的类型推断机制,当我们用字面语句构造只拥有相同类型值数组的时候,我们不必把数组的类型定义清楚。 <code>shoppinglist</code>的构造也可以这样写:</p>
<pre><code>var shoppingList = [&quot;Eggs&quot;, &quot;Milk&quot;]
</code></pre><p>因为所有字面语句中的值都是相同的类型Swift 可以推断出<code>String[]</code><code>shoppinglist</code>中变量的正确类型。</p>
<p>在这个例子中,字面仅仅包含两个<code>String</code>值。匹配了该数组的变量声明(只能包含<code>String</code>的数组),所以这个字面的分配过程就是允许用两个初始项来构造<code>shoppinglist</code></p>
<p>由于 Swift 的类型推断机制,当我们用字面构造只拥有相同类型值数组的时候,我们不必把数组的类型定义清楚。 <code>shoppinglist</code>的构造也可以这样写:</p>
<pre><code class="lang-swift">var shoppingList = [&quot;Eggs&quot;, &quot;Milk&quot;]
</code></pre>
<p>因为所有字面量中的值都是相同的类型Swift 可以推断出<code>String[]</code><code>shoppinglist</code>中变量的正确类型。</p>
<p><a name="accessing_and_modifying_an_array"></a></p>
<h3 id="-">访问和修改数组</h3>
<p>我们可以通过数组的方法和属性来访问和修改数组,或者下标语法。
还可以使用数组的只读属性<code>count</code>来获取数组中的数据项数量。</p>
<pre><code>println(&quot;The shopping list contains \(shoppingList.count) items.&quot;)
<pre><code class="lang-swift">println(&quot;The shopping list contains \(shoppingList.count) items.&quot;)
// 输出&quot;The shopping list contains 2 items.&quot;这个数组有2个项
</code></pre><p>使用布尔项<code>isEmpty</code>来作为检查<code>count</code>属性的值是否为 0 的捷径。</p>
<pre><code>if shoppingList.isEmpty {
</code></pre>
<p>使用布尔项<code>isEmpty</code>来作为检查<code>count</code>属性的值是否为 0 的捷径。</p>
<pre><code class="lang-swift">if shoppingList.isEmpty {
println(&quot;The shopping list is empty.&quot;)
} else {
println(&quot;The shopping list is not empty.&quot;)
}
// 打印 &quot;The shopping list is not empty.&quot;shoppinglist不是空的
</code></pre><p>也可以使用<code>append</code>方法在数组后面添加新的数据项:</p>
<pre><code>shoppingList.append(&quot;Flour&quot;)
</code></pre>
<p>也可以使用<code>append</code>方法在数组后面添加新的数据项:</p>
<pre><code class="lang-swift">shoppingList.append(&quot;Flour&quot;)
// shoppingList 现在有3个数据项有人在摊煎饼
</code></pre><p>除此之外,使用加法赋值运算符(<code>+=</code>)也可以直接在数组后面添加数据项:</p>
<pre><code>shoppingList += &quot;Baking Powder&quot;
</code></pre>
<p>除此之外,使用加法赋值运算符(<code>+=</code>)也可以直接在数组后面添加数据项:</p>
<pre><code class="lang-swift">shoppingList += &quot;Baking Powder&quot;
// shoppingList 现在有四项了
</code></pre><p>我们也可以使用加法赋值运算符(<code>+=</code>)直接添加拥有相同类型数据的数组。</p>
<pre><code>shoppingList += [&quot;Chocolate Spread&quot;, &quot;Cheese&quot;, &quot;Butter&quot;]
</code></pre>
<p>我们也可以使用加法赋值运算符(<code>+=</code>)直接添加拥有相同类型数据的数组。</p>
<pre><code class="lang-swift">shoppingList += [&quot;Chocolate Spread&quot;, &quot;Cheese&quot;, &quot;Butter&quot;]
// shoppingList 现在有7项了
</code></pre><p>可以直接使用下标语法来获取数组中的数据项,把我们需要的数据项的索引值放在直接放在数组名称的方括号中:</p>
<pre><code>var firstItem = shoppingList[0]
</code></pre>
<p>可以直接使用下标语法来获取数组中的数据项,把我们需要的数据项的索引值放在直接放在数组名称的方括号中:</p>
<pre><code class="lang-swift">var firstItem = shoppingList[0]
// 第一项是 &quot;Eggs&quot;
</code></pre><p>注意第一项在数组中的索引值是<code>0</code>而不是<code>1</code>。 Swift 中的数组索引总是从零开始。</p>
</code></pre>
<p>注意第一项在数组中的索引值是<code>0</code>而不是<code>1</code>。 Swift 中的数组索引总是从零开始。</p>
<p>我们也可以用下标来改变某个已有索引值对应的数据值:</p>
<pre><code>shoppingList[0] = &quot;Six eggs&quot;
<pre><code class="lang-swift">shoppingList[0] = &quot;Six eggs&quot;
// 其中的第一项现在是 &quot;Six eggs&quot; 而不是 &quot;Eggs&quot;
</code></pre><p>还可以利用下标来一次改变一系列数据值,即使新数据和原有数据的数量是不一样的。下面的例子把<code>&quot;Chocolate Spread&quot;</code><code>&quot;Cheese&quot;</code>,和<code>&quot;Butter&quot;</code>替换为<code>&quot;Bananas&quot;</code><code>&quot;Apples&quot;</code></p>
<pre><code>shoppingList[4...6] = [&quot;Bananas&quot;, &quot;Apples&quot;]
</code></pre>
<p>还可以利用下标来一次改变一系列数据值,即使新数据和原有数据的数量是不一样的。下面的例子把<code>&quot;Chocolate Spread&quot;</code><code>&quot;Cheese&quot;</code>,和<code>&quot;Butter&quot;</code>替换为<code>&quot;Bananas&quot;</code><code>&quot;Apples&quot;</code></p>
<pre><code class="lang-swift">shoppingList[4...6] = [&quot;Bananas&quot;, &quot;Apples&quot;]
// shoppingList 现在有六项
</code></pre><blockquote>
</code></pre>
<p>&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD</p>
<blockquote>
<p>注意:</p>
<p>我们不能使用下标语法在数组尾部添加新项。如果我们试着用这种方法对索引越界的数据进行检索或者设置新值的操作,我们会引发一个运行期错误。我们可以使用索引值和数组的<code>count</code>属性进行比较来在使用某个索引之前先检验是否有效。除了当<code>count</code>等于 0 时(说明这是个空数组),最大索引值一直是<code>count - 1</code>,因为数组都是零起索引。</p>
<h1 id="-count-count-0-count-1-">我们不能使用下标语法在数组尾部添加新项。如果我们试着用这种方法对索引越界的数据进行检索或者设置新值的操作,我们会引发一个运行期错误。我们可以使用索引值和数组的<code>count</code>属性进行比较来在使用某个索引之前先检验是否有效。除了当<code>count</code>等于 0 时(说明这是个空数组),最大索引值一直是<code>count - 1</code>,因为数组都是零起索引。</h1>
<p>注意:</p>
<p>我们不能使用下标语法在数组尾部添加新项。如果我们试着用这种方法对索引越界的数据进行检索或者设置新值的操作,我们会引发一个运行时错误。我们可以使用索引值和数组的<code>count</code>属性进行比较来在使用某个索引之前先检验是否有效。除了当<code>count</code>等于 0 时(说明这是个空数组),最大索引值一直是<code>count - 1</code>,因为数组都是零起索引。</p>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>a516af6a531a104ec88da0d236ecf389a5ec72af</p>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
<p>调用数组的<code>insert(atIndex:)</code>方法来在某个具体索引值之前添加数据项:</p>
<pre><code>shoppingList.insert(&quot;Maple Syrup&quot;, atIndex: 0)
<pre><code class="lang-swift">shoppingList.insert(&quot;Maple Syrup&quot;, atIndex: 0)
// shoppingList 现在有7项
// &quot;Maple Syrup&quot; 现在是这个列表中的第一项
</code></pre><p>这次<code>insert</code>函数调用把值为<code>&quot;Maple Syrup&quot;</code>的新数据项插入列表的最开始位置,并且使用<code>0</code>作为索引值。</p>
</code></pre>
<p>这次<code>insert</code>函数调用把值为<code>&quot;Maple Syrup&quot;</code>的新数据项插入列表的最开始位置,并且使用<code>0</code>作为索引值。</p>
<p>类似的我们可以使用<code>removeAtIndex</code>方法来移除数组中的某一项。这个方法把数组在特定索引值中存储的数据项移除并且返回这个被移除的数据项(我们不需要的时候就可以无视它):</p>
<pre><code>let mapleSyrup = shoppingList.removeAtIndex(0)
//索引值为0的数据项被移除
<pre><code class="lang-swift">let mapleSyrup = shoppingList.removeAtIndex(0)
// 索引值为0的数据项被移除
// shoppingList 现在只有6项而且不包括Maple Syrup
// mapleSyrup常量的值等于被移除数据项的值 &quot;Maple Syrup&quot;
</code></pre><p>数据项被移除后数组中的空出项会被自动填补,所以现在索引值为<code>0</code>的数据项的值再次等于<code>&quot;Six eggs&quot;</code>:</p>
<pre><code>firstItem = shoppingList[0]
</code></pre>
<p>数据项被移除后数组中的空出项会被自动填补,所以现在索引值为<code>0</code>的数据项的值再次等于<code>&quot;Six eggs&quot;</code>:</p>
<pre><code class="lang-swift">firstItem = shoppingList[0]
// firstItem 现在等于 &quot;Six eggs&quot;
</code></pre><p>如果我们只想把数组中的最后一项移除,可以使用<code>removeLast</code>方法而不是<code>removeAtIndex</code>方法来避免我们需要获取数组的<code>count</code>属性。就像后者一样,前者也会返回被移除的数据项:</p>
<pre><code>let apples = shoppingList.removeLast()
</code></pre>
<p>如果我们只想把数组中的最后一项移除,可以使用<code>removeLast</code>方法而不是<code>removeAtIndex</code>方法来避免我们需要获取数组的<code>count</code>属性。就像后者一样,前者也会返回被移除的数据项:</p>
<pre><code class="lang-swift">let apples = shoppingList.removeLast()
// 数组的最后一项被移除了
// shoppingList现在只有5项不包括cheese
// apples 常量的值现在等于&quot;Apples&quot; 字符串
</code></pre><p><a name="iterating_over_an_array"></a></p>
// apples 常量的值现在等于&quot;Apples&quot; 字符串
</code></pre>
<p><a name="iterating_over_an_array"></a></p>
<h3 id="-">数组的遍历</h3>
<p>我们可以使用<code>for-in</code>循环来遍历所有数组中的数据项:</p>
<pre><code>for item in shoppingList {
<pre><code class="lang-swift">for item in shoppingList {
println(item)
}
// Six eggs
@ -695,8 +725,9 @@
// Flour
// Baking Powder
// Bananas
</code></pre><p>如果我们同时需要每个数据项的值和索引值,可以使用全局<code>enumerate</code>函数来进行数组遍历。<code>enumerate</code>返回一个由每一个数据项索引值和数据值组成的键值对组。我们可以把这个键值对组分解成临时常量或者变量来进行遍历:</p>
<pre><code>for (index, value) in enumerate(shoppingList) {
</code></pre>
<p>如果我们同时需要每个数据项的值和索引值,可以使用全局<code>enumerate</code>函数来进行数组遍历。<code>enumerate</code>返回一个由每一个数据项索引值和数据值组成的元组。我们可以把这个元组分解成临时常量或者变量来进行遍历:</p>
<pre><code class="lang-swift">for (index, value) in enumerate(shoppingList) {
println(&quot;Item \(index + 1): \(value)&quot;)
}
// Item 1: Six eggs
@ -704,142 +735,163 @@
// Item 3: Flour
// Item 4: Baking Powder
// Item 5: Bananas
</code></pre><p>更多关于<code>for-in</code>循环的介绍请参见<a href="05_Control_Flow.html#for_loops">for 循环</a></p>
</code></pre>
<p>更多关于<code>for-in</code>循环的介绍请参见<a href="05_Control_Flow.html#for_loops">for 循环</a></p>
<p><a name="creating_and_initializing_an_array"></a></p>
<h3 id="-">创建并且构造一个数组</h3>
<p>我们可以使用构造语法来创建一个由特定数据类型构成的空数组:</p>
<pre><code>var someInts = Int[]()
<pre><code class="lang-swift">var someInts = Int[]()
println(&quot;someInts is of type Int[] with \(someInts.count) items。&quot;)
// 打印 &quot;someInts is of type Int[] with 0 items。&quot;someInts是0数据项的Int[]数组)
</code></pre><p>注意<code>someInts</code>被设置为一个<code>Int[]</code>构造函数的输出所以它的变量类型被定义为<code>Int[]</code></p>
</code></pre>
<p>注意<code>someInts</code>被设置为一个<code>Int[]</code>构造函数的输出所以它的变量类型被定义为<code>Int[]</code></p>
<p>除此之外,如果代码上下文中提供了类型信息, 例如一个函数参数或者一个已经定义好类型的常量或者变量,我们可以使用空数组语句创建一个空数组,它的写法很简单:<code>[]</code>(一对空方括号):</p>
<pre><code>someInts.append(3)
<pre><code class="lang-swift">someInts.append(3)
// someInts 现在包含一个INT值
someInts = []
// someInts 现在是空数组但是仍然是Int[]类型的。
</code></pre><p>Swift 中的<code>Array</code>类型还提供一个可以创建特定大小并且所有数据都被默认的构造方法。我们可以把准备加入新数组的数据项数量(<code>count</code>)和适当类型的初始值(<code>repeatedValue</code>)传入数组构造函数:</p>
<pre><code>var threeDoubles = Double[](count: 3, repeatedValue:0.0)
</code></pre>
<p>Swift 中的<code>Array</code>类型还提供一个可以创建特定大小并且所有数据都被默认的构造方法。我们可以把准备加入新数组的数据项数量(<code>count</code>)和适当类型的初始值(<code>repeatedValue</code>)传入数组构造函数:</p>
<pre><code class="lang-swift">var threeDoubles = Double[](count: 3, repeatedValue:0.0)
// threeDoubles 是一种 Double[]数组, 等于 [0.0, 0.0, 0.0]
</code></pre><p>因为类型推断的存在,我们使用这种构造方法的时候不需要特别指定数组中存储的数据类型,因为类型可以从默认值推断出来:</p>
<pre><code>var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5)
</code></pre>
<p>因为类型推断的存在,我们使用这种构造方法的时候不需要特别指定数组中存储的数据类型,因为类型可以从默认值推断出来:</p>
<pre><code class="lang-swift">var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5)
// anotherThreeDoubles is inferred as Double[], and equals [2.5, 2.5, 2.5]
</code></pre><p>最后,我们可以使用加法操作符(<code>+</code>)来组合两种已存在的相同类型数组。新数组的数据类型会被从两个数组的数据类型中推断出来:</p>
<pre><code>var sixDoubles = threeDoubles + anotherThreeDoubles
</code></pre>
<p>最后,我们可以使用加法操作符(<code>+</code>)来组合两种已存在的相同类型数组。新数组的数据类型会被从两个数组的数据类型中推断出来:</p>
<pre><code class="lang-swift">var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles 被推断为 Double[], 等于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
</code></pre><p><a name="dictionaries"></a></p>
</code></pre>
<p><a name="dictionaries"></a></p>
<h2 id="-">字典</h2>
<p>字典是一种存储相同类型多重数据的存储器。每个值value都关联独特的键key键作为字典中的这个值数据的标识符。和数组中的数据项不同字典中的数据项并没有具体顺序。我们在需要通过标识符访问数据的时候使用字典这种方法很大程度上和我们在现实世界中使用字典查字义的方法一样。</p>
<p>字典是一种存储多个相同类型的值的容器。每个值value都关联唯一的键key键作为字典中的这个值数据的标识符。和数组中的数据项不同字典中的数据项并没有具体顺序。我们在需要通过标识符访问数据的时候使用字典这种方法很大程度上和我们在现实世界中使用字典查字义的方法一样。</p>
<p>Swift 的字典使用时需要具体规定可以存储键和值类型。不同于 Objective-C 的<code>NSDictionary</code><code>NSMutableDictionary</code> 类可以使用任何类型的对象来作键和值并且不提供任何关于这些对象的本质信息。在 Swift 中,在某个特定字典中可以存储的键和值必须提前定义清楚,方法是通过显性类型标注或者类型推断。</p>
<p>Swift 的字典使用<code>Dictionary&lt;KeyType, ValueType&gt;</code>定义,其中<code>KeyType</code>是字典中键的数据类型,<code>ValueType</code>是字典中对应于这些键所存储值的数据类型。</p>
<p><code>KeyType</code>的唯一限制就是可哈希的,这样可以保证它是独一无二的,所有的 Swift 基本类型(例如<code>String</code><code>Int</code> <code>Double</code><code>Bool</code>)都是默认可哈希的,并且所有这些类型都可以在字典中当做键使用。未关联值的枚举成员(参见<a href="08_Enumerations.html">枚举</a>)也是默认可哈希的。</p>
<p><a name="dictionary_literals"></a></p>
<h2 id="-">字典字面语句</h2>
<p>我们可以使用字典字面语句来构造字典,们和我们刚才介绍过的数组字面语句拥有相似语法。一个字典字面语句是一个定义拥有一个或者多个键值对的字典集合的简单语句。</p>
<p>一个键值对是一个<code>key</code>和一个<code>value</code>的结合体。在字典字面语句中,每一个键值对的键和值都由冒号分割。这些键值对构成一个列表,其中这些键值对由方括号包含并且由逗号分割:</p>
<pre><code>[key 1: value 1, key 2: value 2, key 3: value 3]
</code></pre><p>下面的例子创建了一个存储国际机场名称的字典。在这个字典中键是三个字母的国际航空运输相关代码,值是机场名称:</p>
<pre><code>var airports: Dictionary&lt;String, String&gt; = [&quot;TYO&quot;: &quot;Tokyo&quot;, &quot;DUB&quot;: &quot;Dublin&quot;]
</code></pre><p><code>airports</code>字典被定义为一种<code>Dictionary&lt;String, String&gt;</code>,它意味着这个字典的键和值都是<code>String</code>类型。</p>
<h2 id="-">字典字面</h2>
<p>我们可以使用字典字面来构造字典,们和我们刚才介绍过的数组字面拥有相似语法。一个字典字面是一个定义拥有一个或者多个键值对的字典集合的简单语句。</p>
<p>一个键值对是一个<code>key</code>和一个<code>value</code>的结合体。在字典字面中,每一个键值对的键和值都由冒号分割。这些键值对构成一个列表,其中这些键值对由方括号包含并且由逗号分割:</p>
<pre><code class="lang-swift">[key 1: value 1, key 2: value 2, key 3: value 3]
</code></pre>
<p>下面的例子创建了一个存储国际机场名称的字典。在这个字典中键是三个字母的国际航空运输相关代码,值是机场名称:</p>
<pre><code class="lang-swift">var airports: Dictionary&lt;String, String&gt; = [&quot;TYO&quot;: &quot;Tokyo&quot;, &quot;DUB&quot;: &quot;Dublin&quot;]
</code></pre>
<p><code>airports</code>字典被定义为一种<code>Dictionary&lt;String, String&gt;</code>,它意味着这个字典的键和值都是<code>String</code>类型。</p>
<blockquote>
<p>注意:</p>
<p><code>airports</code>字典被声明为变量(用<code>var</code>关键字)而不是常量(<code>let</code>关键字)因为后来更多的机场信息会被添加到这个示例字典中。</p>
<p>注意:
<code>airports</code>字典被声明为变量(用<code>var</code>关键字)而不是常量(<code>let</code>关键字)因为后来更多的机场信息会被添加到这个示例字典中。</p>
</blockquote>
<p><code>airports</code>字典使用字典字面语句初始化,包含两个键值对。第一对的键是<code>TYO</code>,值是<code>Tokyo</code>。第二对的键是<code>DUB</code>,值是<code>Dublin</code></p>
<p>这个字典语句包含了两个<code>String: String</code>类型的键值对。们对应<code>airports</code>变量声明的类型(一个只有<code>String</code>键和<code>String</code>值的字典)所以这个字典字面语句是构造两个初始数据项的<code>airport</code>字典。</p>
<p>和数组一样,如果我们使用字面语句构造字典就不用把类型定义清楚。<code>airports</code>的也可以用这种方法简短定义:</p>
<pre><code>var airports = [&quot;TYO&quot;: &quot;Tokyo&quot;, &quot;DUB&quot;: &quot;Dublin&quot;]
</code></pre><p>因为这个语句中所有的键和值都分别是相同的数据类型Swift 可以推断出<code>Dictionary&lt;String, String&gt;</code><code>airports</code>字典的正确类型。</p>
<p><code>airports</code>字典使用字典字面初始化,包含两个键值对。第一对的键是<code>TYO</code>,值是<code>Tokyo</code>。第二对的键是<code>DUB</code>,值是<code>Dublin</code></p>
<p>这个字典语句包含了两个<code>String: String</code>类型的键值对。们对应<code>airports</code>变量声明的类型(一个只有<code>String</code>键和<code>String</code>值的字典)所以这个字典字面是构造两个初始数据项的<code>airport</code>字典。</p>
<p>和数组一样,如果我们使用字面构造字典就不用把类型定义清楚。<code>airports</code>的也可以用这种方法简短定义:</p>
<pre><code class="lang-swift">var airports = [&quot;TYO&quot;: &quot;Tokyo&quot;, &quot;DUB&quot;: &quot;Dublin&quot;]
</code></pre>
<p>因为这个语句中所有的键和值都分别是相同的数据类型Swift 可以推断出<code>Dictionary&lt;String, String&gt;</code><code>airports</code>字典的正确类型。</p>
<p><a name="accessing_and_modifying_a_dictionary"></a></p>
<h3 id="-">读取和修改字典</h3>
<p>我们可以通过字典的方法和属性来读取和修改字典,或者使用下标语法。和数组一样,我们可以通过字典的只读属性<code>count</code>来获取某个字典的数据项数量:</p>
<pre><code>println(&quot;The dictionary of airports contains \(airports.count) items.&quot;)
<pre><code class="lang-swift">println(&quot;The dictionary of airports contains \(airports.count) items.&quot;)
// 打印 &quot;The dictionary of airports contains 2 items.&quot;(这个字典有两个数据项)
</code></pre><p>我们也可以在字典中使用下标语法来添加新的数据项。可以使用一个合适类型的 key 作为下标索引,并且分配新的合适类型的值:</p>
<pre><code>airports[&quot;LHR&quot;] = &quot;London&quot;
</code></pre>
<p>我们也可以在字典中使用下标语法来添加新的数据项。可以使用一个合适类型的 key 作为下标索引,并且分配新的合适类型的值:</p>
<pre><code class="lang-swift">airports[&quot;LHR&quot;] = &quot;London&quot;
// airports 字典现在有三个数据项
</code></pre><p>我们也可以使用下标语法来改变特定键对应的值:</p>
<pre><code>airports[&quot;LHR&quot;] = &quot;London Heathrow&quot;
</code></pre>
<p>我们也可以使用下标语法来改变特定键对应的值:</p>
<pre><code class="lang-swift">airports[&quot;LHR&quot;] = &quot;London Heathrow&quot;
// &quot;LHR&quot;对应的值 被改为 &quot;London Heathrow
</code></pre><p>作为另一种下标方法,字典的<code>updateValue(forKey:)</code>方法可以设置或者更新特定键对应的值。就像上面所示的示例,<code>updateValue(forKey:)</code>方法在这个键不存在对应值的时候设置值或者在存在时更新已存在的值。和上面的下标方法不一样,这个方法返回更新值之前的原值。这样方便我们检查更新是否成功。</p>
</code></pre>
<p>作为另一种下标方法,字典的<code>updateValue(forKey:)</code>方法可以设置或者更新特定键对应的值。就像上面所示的示例,<code>updateValue(forKey:)</code>方法在这个键不存在对应值的时候设置值或者在存在时更新已存在的值。和上面的下标方法不一样,这个方法返回更新值之前的原值。这样方便我们检查更新是否成功。</p>
<p><code>updateValue(forKey:)</code>函数会返回包含一个字典值类型的可选值。举例来说:对于存储<code>String</code>值的字典,这个函数会返回一个<code>String?</code>或者“可选 <code>String</code>”类型的值。如果值存在,则这个可选值值等于被替换的值,否则将会是<code>nil</code></p>
<pre><code>if let oldValue = airports.updateValue(&quot;Dublin Internation&quot;, forKey: &quot;DUB&quot;) {
<pre><code class="lang-swift">if let oldValue = airports.updateValue(&quot;Dublin Internation&quot;, forKey: &quot;DUB&quot;) {
println(&quot;The old value for DUB was \(oldValue).&quot;)
}
// 输出 &quot;The old value for DUB was Dublin.&quot;DUB原值是dublin
</code></pre><p>我们也可以使用下标语法来在字典中检索特定键对应的值。由于使用一个没有值的键这种情况是有可能发生的,可选类型返回这个键存在的相关值,否则就返回<code>nil</code></p>
<pre><code>if let airportName = airports[&quot;DUB&quot;] {
</code></pre>
<p>我们也可以使用下标语法来在字典中检索特定键对应的值。由于使用一个没有值的键这种情况是有可能发生的,可选类型返回这个键存在的相关值,否则就返回<code>nil</code></p>
<pre><code class="lang-swift">if let airportName = airports[&quot;DUB&quot;] {
println(&quot;The name of the airport is \(airportName).&quot;)
} else {
println(&quot;That airport is not in the airports dictionary.&quot;)
}
// 打印 &quot;The name of the airport is Dublin Internation.&quot;(机场的名字是都柏林国际)
</code></pre><p>我们还可以使用下标语法来通过给某个键的对应值赋值为<code>nil</code>来从字典里移除一个键值对:</p>
<pre><code>airports[&quot;APL&quot;] = &quot;Apple Internation&quot;
</code></pre>
<p>我们还可以使用下标语法来通过给某个键的对应值赋值为<code>nil</code>来从字典里移除一个键值对:</p>
<pre><code class="lang-swift">airports[&quot;APL&quot;] = &quot;Apple Internation&quot;
// &quot;Apple Internation&quot;不是真的 APL机场, 删除它
airports[&quot;APL&quot;] = nil
// APL现在被移除了
</code></pre><p>另外,<code>removeValueForKey</code>方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的value或者在没有值的情况下返回<code>nil</code></p>
<pre><code>if let removedValue = airports.removeValueForKey(&quot;DUB&quot;) {
</code></pre>
<p>另外,<code>removeValueForKey</code>方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的value或者在没有值的情况下返回<code>nil</code></p>
<pre><code class="lang-swift">if let removedValue = airports.removeValueForKey(&quot;DUB&quot;) {
println(&quot;The removed airport&#39;s name is \(removedValue).&quot;)
} else {
println(&quot;The airports dictionary does not contain a value for DUB.&quot;)
}
// prints &quot;The removed airport&#39;s name is Dublin International.&quot;
</code></pre><p><a name="iterating_over_a_dictionary"></a></p>
</code></pre>
<p><a name="iterating_over_a_dictionary"></a></p>
<h3 id="-">字典遍历</h3>
<p>我们可以使用<code>for-in</code>循环来遍历某个字典中的键值对。每一个字典中的数据项都由<code>(key, value)</code>元组形式返回,并且我们可以使用暂时性常量或者变量来分解这些元组:</p>
<pre><code>for (airportCode, airportName) in airports {
<p>我们可以使用<code>for-in</code>循环来遍历某个字典中的键值对。每一个字典中的数据项都由<code>(key, value)</code>元组形式返回,并且我们可以使用临时常量或者变量来分解这些元组:</p>
<pre><code class="lang-swift">for (airportCode, airportName) in airports {
println(&quot;\(airportCode): \(airportName)&quot;)
}
// TYO: Tokyo
// LHR: London Heathrow
</code></pre><p><code>for-in</code>循环请参见<a href="05_Control_Flow.html#for_loops">For 循环</a></p>
<p>我们也可以通过访问他的<code>keys</code>或者<code>values</code>属性(都是可遍历集合)检索一个字典的键或者值:</p>
<pre><code>for airportCode in airports.keys {
</code></pre>
<p><code>for-in</code>循环请参见<a href="05_Control_Flow.html#for_loops">For 循环</a></p>
<p>我们也可以通过访问它的<code>keys</code>或者<code>values</code>属性(都是可遍历集合)检索一个字典的键或者值:</p>
<pre><code class="lang-swift">for airportCode in airports.keys {
println(&quot;Airport code: \(airportCode)&quot;)
}
// Airport code: TYO
// Airport code: LHR
for airportName in airports.values {
</code></pre>
<pre><code class="lang-swift">for airportName in airports.values {
println(&quot;Airport name: \(airportName)&quot;)
}
// Airport name: Tokyo
// Airport name: London Heathrow
</code></pre><p>如果我们只是需要使用某个字典的键集合或者值集合来作为某个接受<code>Array</code>实例 API 的参数,可以直接使用<code>keys</code>或者<code>values</code>属性直接构造一个新数组:</p>
<pre><code>let airportCodes = Array(airports.keys)
</code></pre>
<p>如果我们只是需要使用某个字典的键集合或者值集合来作为某个接受<code>Array</code>实例 API 的参数,可以直接使用<code>keys</code>或者<code>values</code>属性直接构造一个新数组:</p>
<pre><code class="lang-swift">let airportCodes = Array(airports.keys)
// airportCodes is [&quot;TYO&quot;, &quot;LHR&quot;]
let airportNames = Array(airports.values)
</code></pre>
<pre><code class="lang-swift">let airportNames = Array(airports.values)
// airportNames is [&quot;Tokyo&quot;, &quot;London Heathrow&quot;]
</code></pre><blockquote>
<p>注意:</p>
<p>Swift 的字典类型是无序集合类型。其中字典键,值,键值对在遍历的时候会重新排列,而且其中顺序是不固定的。</p>
</code></pre>
<blockquote>
<p>注意:
Swift 的字典类型是无序集合类型。其中字典键,值,键值对在遍历的时候会重新排列,而且其中顺序是不固定的。</p>
</blockquote>
<p><a name="creating_an_empty_dictionary"></a></p>
<h3 id="-">创建一个空字典</h3>
<p>我们可以像数组一样使用构造语法创建一个空字典:</p>
<pre><code>var namesOfIntegers = Dictionary&lt;Int, String&gt;()
<pre><code class="lang-swift">var namesOfIntegers = Dictionary&lt;Int, String&gt;()
// namesOfIntegers 是一个空的 Dictionary&lt;Int, String&gt;
</code></pre><p>这个例子创建了一个<code>Int, String</code>类型的空字典来储存英语对整数的命名。它的键是<code>Int</code>型,值是<code>String</code>型。</p>
<p>如果上下文已经提供了信息类型,我们可以使用空字典字面语句来创建一个空字典,记作<code>[:]</code>(中括号中放一个冒号):</p>
<pre><code>namesOfIntegers[16] = &quot;sixteen&quot;
</code></pre>
<p>这个例子创建了一个<code>Int, String</code>类型的空字典来储存英语对整数的命名。它的键是<code>Int</code>型,值是<code>String</code>型。</p>
<p>如果上下文已经提供了信息类型,我们可以使用空字典字面量来创建一个空字典,记作<code>[:]</code>(中括号中放一个冒号):</p>
<pre><code class="lang-swift">namesOfIntegers[16] = &quot;sixteen&quot;
// namesOfIntegers 现在包含一个键值对
namesOfIntegers = [:]
// namesOfIntegers 又成为了一个 Int, String类型的空字典
</code></pre><blockquote>
<p>注意:</p>
<p>在后台Swift 的数组和字典都是由泛型集合来实现的,想了解更多泛型和集合信息请参见<a href="22_Generics.html">泛型</a></p>
</code></pre>
<blockquote>
<p>注意:
在后台Swift 的数组和字典都是由泛型集合来实现的,想了解更多泛型和集合信息请参见<a href="22_Generics.html">泛型</a></p>
</blockquote>
<p><a name="mutability_of_collections"></a></p>
<h2 id="-">集合的可变性</h2>
<p>数组和字典都是在单个集合中存储可变值。如果我们创建一个数组或者字典并且把它分配成一个变量,这个集合将会是可变的。这意味着我们可以在创建之后添加更多或移除已存在的数据项来改变这个集合的大小。与此相反,如果我们把数组或字典分配成常量,那么就是不可变的,它的大小不能被改变。</p>
<p>数组和字典都是在单个集合中存储可变值。如果我们创建一个数组或者字典并且把它分配成一个变量,这个集合将会是可变的。这意味着我们可以在创建之后添加更多或移除已存在的数据项来改变这个集合的大小。与此相反,如果我们把数组或字典分配成常量,那么就是不可变的,它的大小不能被改变。</p>
<p>对字典来说,不可变性也意味着我们不能替换其中任何现有键所对应的值。不可变字典的内容在被首次设定之后不能更改。
不可变性对数组来说有一点不同,当然我们不能试着改变任何不可变数组的大小,但是我们可以重新设定相对现存索引所对应的值。这使得 Swift 数组在大小被固定的时候依然可以做的很棒。</p>
<p>Swift 数组的可变性行为同时影响了数组实例如何被分配和修改,想获取更多信息,请参见<a href="09_Classes_and_Structures.html#assignment_and_copy_behavior_for_collection_types">集合在赋值和复制中的行为</a></p>
<blockquote>
<p>注意:</p>
<p>在我们不需要改变数组大小的时候创建不可变数组是很好的习惯。如此 Swift 编译器可以优化我们创建的集合。</p>
<p>注意:
在我们不需要改变数组大小的时候创建不可变数组是很好的习惯。如此 Swift 编译器可以优化我们创建的集合。</p>
</blockquote>

113
chapter2/05_Control_Flow.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.5" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.5" 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_96">
<section class="normal" id="section-gitbook_43">
<blockquote>
<p>翻译vclwei, coverxit, NicePiao</p>
<p>校对coverxit</p>
<p>翻译vclwei, coverxit, NicePiao<br>校对coverxit, stanzhai</p>
</blockquote>
<h1 id="-">控制流</h1>
<hr>
@ -603,16 +602,14 @@
<li><a href="#control_transfer_statements">控制转移语句Control Transfer Statements</a></li>
</ul>
<p>Swift提供了类似 C 语言的流程控制结构,包括可以多次执行任务的<code>for</code><code>while</code>循环,基于特定条件选择执行不同代码分支的<code>if</code><code>switch</code>语句,还有控制流程跳转到其他代码的<code>break</code><code>continue</code>语句。</p>
<p>除了 C 语言里面传统的for条件递增<code>for-condition-increment</code>循环Swift 还增加了<code>for-in</code>循环用来更简单地遍历数组array字典dictionary区间range字符串string和其他序列类型。</p>
<p>除了 C 语言里面传统的 for 条件递增(<code>for-condition-increment</code>循环Swift 还增加了<code>for-in</code>循环用来更简单地遍历数组array字典dictionary区间range字符串string和其他序列类型。</p>
<p>Swift 的<code>switch</code>语句比 C 语言中更加强大。在 C 语言中,如果某个 case 不小心漏写了<code>break</code>,这个 case 就会贯穿fallthrough至下一个 caseSwift 无需写<code>break</code>所以不会发生这种贯穿fallthrough的情况。case 还可以匹配更多的类型模式包括区间匹配range matching元组tuple和特定类型的描述。<code>switch</code>的 case 语句中匹配的值可以是由 case 体内部临时的常量或者变量决定,也可以由<code>where</code>分句描述更复杂的匹配条件。</p>
<p><a name="for_loops"></a></p>
<h2 id="for-">For 循环</h2>
<p><code>for</code>循环用来按照指定的次数多次执行一系列语句。Swift 提供两种<code>for</code>循环形式:</p>
<ul>
<li><p><code>for-in</code>用来遍历一个区间range序列sequence集合collection系列progression里面所有的元素执行一系列语句。</p>
</li>
<li><p>for条件递增<code>for-condition-increment</code>)语句,用来重复执行一系列语句直到达成特定条件达成,一般通过在每次循环完成后增加计数器的值来实现。</p>
</li>
<li><code>for-in</code>用来遍历一个区间range序列sequence集合collection系列progression里面所有的元素执行一系列语句。</li>
<li>for条件递增<code>for-condition-increment</code>)语句,用来重复执行一系列语句直到达成特定条件达成,一般通过在每次循环完成后增加计数器的值来实现。</li>
</ul>
<p><a name="for_in"></a></p>
<h3 id="for-in">For-In</h3>
@ -630,8 +627,7 @@
<p>例子中用来进行遍历的元素是一组使用闭区间操作符(<code>...</code>)表示的从<code>1</code><code>5</code>的数字。<code>index</code>被赋值为闭区间中的第一个数字(<code>1</code>),然后循环中的语句被执行一次。在本例中,这个循环只包含一个语句,用来输出当前<code>index</code>值所对应的乘 5 乘法表结果。该语句执行后,<code>index</code>的值被更新为闭区间中的第二个数字(<code>2</code>),之后<code>println</code>方法会再执行一次。整个过程会进行到闭区间结尾为止。</p>
<p>上面的例子中,<code>index</code>是一个每次循环遍历开始时被自动赋值的常量。这种情况下,<code>index</code>在使用前不需要声明,只需要将它包含在循环的声明中,就可以对其进行隐式声明,而无需使用<code>let</code>关键字声明。</p>
<blockquote>
<p>注意:</p>
<p><code>index</code>常量只存在于循环的生命周期里。如果你想在循环完成后访问<code>index</code>的值,又或者想让<code>index</code>成为一个变量而不是常量,你必须在循环之前自己进行声明。</p>
<p>注意:<br><code>index</code>常量只存在于循环的生命周期里。如果你想在循环完成后访问<code>index</code>的值,又或者想让<code>index</code>成为一个变量而不是常量,你必须在循环之前自己进行声明。</p>
</blockquote>
<p>如果你不需要知道区间内每一项的值,你可以使用下划线(<code>_</code>)替代变量名来忽略对值的访问:</p>
<pre><code class="lang-swift">let base = 3
@ -685,10 +681,9 @@ for (animalName, legCount) in numberOfLegs {
// index is 2
</code></pre>
<p>下面是一般情况下这种循环方式的格式:</p>
<pre><code class="lang-swift">for `initialization`; `condition`; `increment` {
`statements`
}
</code></pre>
<blockquote>
<p>for <code>initialization</code>; <code>condition</code>; <code>increment</code> {<br> <code>statements</code><br>} </p>
</blockquote>
<p>和 C 语言中一样,分号将循环的定义分为 3 个部分不同的是Swift 不需要使用圆括号将“initialization; condition; increment”包括起来。</p>
<p>这个循环执行流程如下:</p>
<ol>
@ -698,12 +693,9 @@ for (animalName, legCount) in numberOfLegs {
<li>执行所有语句(<em>statements</em>)之后,执行递增表达式(<em>increment expression</em>)。通常会增加或减少计数器的值,或者根据语句(<em>statements</em>)输出来修改某一个初始化的变量。当递增表达式运行完成后,重复执行第 2 步,条件表达式会再次执行。</li>
</ol>
<p>上述描述和循环格式等同于:</p>
<pre><code class="lang-swift">`initialization`
while `condition` {
`statements`
`increment`
}
</code></pre>
<blockquote>
<p><code>initialization</code><br>while <code>condition</code> {<br> <code>statements</code><br> <code>increment</code><br>} </p>
</blockquote>
<p>在初始化表达式中声明的常量和变量(比如<code>var index = 0</code>)只在<code>for</code>循环的生命周期里有效。如果想在循环结束后访问<code>index</code>的值,你必须要在循环生命周期开始前声明<code>index</code></p>
<pre><code class="lang-swift">var index: Int
for index = 0; index &lt; 3; ++index {
@ -720,19 +712,16 @@ println(&quot;The loop statements were executed \(index) times&quot;)
<h2 id="while-">While 循环</h2>
<p><code>while</code>循环运行一系列语句直到条件变成<code>false</code>。这类循环适合使用在第一次迭代前迭代次数未知的情况下。Swift 提供两种<code>while</code>循环形式:</p>
<ul>
<li><p><code>while</code>循环,每次在循环开始时计算条件是否符合;</p>
</li>
<li><p><code>do-while</code>循环,每次在循环结束时计算条件是否符合。</p>
</li>
<li><code>while</code>循环,每次在循环开始时计算条件是否符合;</li>
<li><code>do-while</code>循环,每次在循环结束时计算条件是否符合。</li>
</ul>
<p><a name="while"></a></p>
<h3 id="while">While</h3>
<p><code>while</code>循环从计算单一条件开始。如果条件为<code>true</code>,会重复运行一系列语句,直到条件变为<code>false</code></p>
<p>下面是一般情况下 <code>while</code> 循环格式:</p>
<pre><code class="lang-swift">while `condition` {
`statements`
}
</code></pre>
<blockquote>
<p>while <code>condition</code> {<br> <code>statements</code><br>} </p>
</blockquote>
<p>下面的例子来玩一个叫做<em>蛇和梯子Snakes and Ladders</em>的小游戏,也叫做<em>滑道和梯子Chutes and Ladders</em></p>
<p><img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/snakesAndLadders_2x.png" alt="image"></p>
<p>游戏的规则如下:</p>
@ -775,10 +764,9 @@ println(&quot;Game over!&quot;)
<h3 id="do-while">Do-While</h3>
<p><code>while</code>循环的另外一种形式是<code>do-while</code>,它和<code>while</code>的区别是在判断循环条件之前,先执行一次循环的代码块,然后重复循环直到条件为<code>false</code></p>
<p>下面是一般情况下 <code>do-while</code>循环的格式:</p>
<pre><code class="lang-swift">do {
`statements`
} while `condition`
</code></pre>
<blockquote>
<p>do {<br> <code>statements</code><br>} while <code>condition</code> </p>
</blockquote>
<p>还是蛇和梯子的游戏,使用<code>do-while</code>循环来替代<code>while</code>循环。<code>finalSquare</code><code>board</code><code>square</code><code>diceRoll</code>的值初始化同<code>while</code>循环一样:</p>
<pre><code class="lang-swift">let finalSquare = 25
var board = Int[](count: finalSquare + 1, repeatedValue: 0)
@ -850,16 +838,9 @@ if temperatureInFahrenheit &lt;= 32 {
<h3 id="switch">Switch</h3>
<p><code>switch</code>语句会尝试把某个值与若干个模式pattern进行匹配。根据第一个匹配成功的模式<code>switch</code>语句会执行对应的代码。当有可能的情况较多时,通常用<code>switch</code>语句替换<code>if</code>语句。</p>
<p><code>switch</code>语句最简单的形式就是把某个值与一个或若干个相同类型的值作比较:</p>
<pre><code class="lang-swift">switch `some value to consider` {
case `value 1`:
`respond to value 1`
case `value 2`,
`value 3`:
`respond to value 2 or 3`
default:
`otherwise, do something else`
}
</code></pre>
<blockquote>
<p>switch <code>some value to consider</code> {<br>case <code>value 1</code>:<br> <code>respond to value 1</code><br>case <code>value 2</code>,<br><code>value 3</code>:<br> <code>respond to value 2 or 3</code><br>default:<br> <code>otherwise, do something else</code><br>} </p>
</blockquote>
<p><code>switch</code>语句都由<em>多个 case</em> 构成。为了匹配某些更特定的值Swift 提供了几种更复杂的匹配模式,这些模式将在本节的稍后部分提到。</p>
<p>每一个 case 都是代码执行的一条分支,这与<code>if</code>语句类似。与之不同的是,<code>switch</code>语句会决定哪一条分支应该被执行。</p>
<p><code>switch</code>语句必须是<em>完备的</em>。这就是说,每一个可能的值都必须至少有一个 case 分支与之对应。在某些不可能涵盖所有值的情况下,你可以使用默认(<code>default</code>)分支满足该要求,这个默认分支必须在<code>switch</code>语句的最后面。</p>
@ -882,8 +863,7 @@ default:
<h4 id="-no-implicit-fallthrough-">不存在隐式的贯穿No Implicit Fallthrough</h4>
<p>与 C 语言和 Objective-C 中的<code>switch</code>语句不同,在 Swift 中,当匹配的 case 分支中的代码执行完毕后,程序会终止<code>switch</code>语句,而不会继续执行下一个 case 分支。这也就是说,不需要在 case 分支中显式地使用<code>break</code>语句。这使得<code>switch</code>语句更安全、更易用,也避免了因忘记写<code>break</code>语句而产生的错误。</p>
<blockquote>
<p>注意:</p>
<p>你依然可以在 case 分支中的代码执行完毕前跳出,详情请参考<a href="#break_in_a_switch_statement">Switch 语句中的 break</a></p>
<p>注意:<br>你依然可以在 case 分支中的代码执行完毕前跳出,详情请参考<a href="#break_in_a_switch_statement">Switch 语句中的 break</a></p>
</blockquote>
<p>每一个 case 分支都<em>必须</em>包含至少一条语句。像下面这样书写代码是无效的,因为第一个 case 分支是空的:</p>
<pre><code class="lang-swift">let anotherCharacter: Character = &quot;a&quot;
@ -898,15 +878,9 @@ default:
</code></pre>
<p>不像 C 语言里的<code>switch</code>语句,在 Swift 中,<code>switch</code>语句不会同时匹配<code>&quot;a&quot;</code><code>&quot;A&quot;</code>。相反的,上面的代码会引起编译期错误:<code>case &quot;a&quot;: does not contain any executable statements</code>——这就避免了意外地从一个 case 分支贯穿到另外一个,使得代码更安全、也更直观。</p>
<p>一个 case 也可以包含多个模式,用逗号把它们分开(如果太长了也可以分行写):</p>
<pre><code class="lang-swift">switch `some value to consider` {
case `value 1`,
`value 2`:
`statements`
}
</code></pre>
<blockquote>
<p>注意:
如果想要贯穿至特定的 case 分支中,请使用<code>fallthrough</code>语句,详情请参考<a href="#fallthrough">贯穿Fallthrough</a></p>
<p>switch <code>some value to consider</code> {<br>case <code>value 1</code>,<br><code>value 2</code>:<br> <code>statements</code><br>} </p>
<p>注意:<br>如果想要贯穿至特定的 case 分支中,请使用<code>fallthrough</code>语句,详情请参考<a href="#fallthrough">贯穿Fallthrough</a></p>
</blockquote>
<p><a name="range_matching"></a></p>
<h4 id="-range-matching-">区间匹配Range Matching</h4>
@ -1009,8 +983,7 @@ case let (x, y):
<h3 id="continue">Continue</h3>
<p><code>continue</code>语句告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。就好像在说“本次循环迭代我已经执行完了”,但是并不会离开整个循环体。</p>
<blockquote>
<p>注意:</p>
<p>在一个for条件递增<code>for-condition-increment</code>)循环体中,在调用<code>continue</code>语句后,迭代增量仍然会被计算求值。循环体继续像往常一样工作,仅仅只是循环体中的执行代码会被跳过。</p>
<p>注意:<br>在一个for条件递增<code>for-condition-increment</code>)循环体中,在调用<code>continue</code>语句后,迭代增量仍然会被计算求值。循环体继续像往常一样工作,仅仅只是循环体中的执行代码会被跳过。</p>
</blockquote>
<p>下面的例子把一个小写字符串中的元音字母和空格字符移除,生成了一个含义模糊的短句:</p>
<pre><code class="lang-swift">let puzzleInput = &quot;great minds think alike&quot;
@ -1038,8 +1011,7 @@ println(puzzleOutput)
<p>当在一个<code>switch</code>代码块中使用<code>break</code>时,会立即中断该<code>switch</code>代码块的执行,并且跳转到表示<code>switch</code>代码块结束的大括号(<code>}</code>)后的第一行代码。</p>
<p>这种特性可以被用来匹配或者忽略一个或多个分支。因为 Swift 的<code>switch</code>需要包含所有的分支而且不允许有为空的分支,有时为了使你的意图更明显,需要特意匹配或者忽略某个分支。那么当你想忽略某个分支时,可以在该分支内写上<code>break</code>语句。当那个分支被匹配到时,分支内的<code>break</code>语句立即结束<code>switch</code>代码块。</p>
<blockquote>
<p>注意:</p>
<p>当一个<code>switch</code>分支仅仅包含注释时,会被报编译时错误。注释不是代码语句而且也不能让<code>switch</code>分支达到被忽略的效果。你总是可以使用<code>break</code>来忽略某个分支。</p>
<p>注意:<br>当一个<code>switch</code>分支仅仅包含注释时,会被报编译时错误。注释不是代码语句而且也不能让<code>switch</code>分支达到被忽略的效果。你总是可以使用<code>break</code>来忽略某个分支。</p>
</blockquote>
<p>下面的例子通过<code>switch</code>来判断一个<code>Character</code>值是否代表下面四种语言之一。为了简洁,多个值被包含在了同一个分支情况中。</p>
<pre><code class="lang-swift">let numberSymbol: Character = &quot;&quot; // 简体中文里的数字 3
@ -1086,18 +1058,17 @@ println(description)
<p>如果<code>integerToDescribe</code>的值不属于列表中的任何质数,那么它不会匹配到第一个<code>switch</code>分支。而这里没有其他特别的分支情况,所以<code>integerToDescribe</code>匹配到包含所有的<code>default</code>分支中。</p>
<p><code>switch</code>代码块执行完后,使用<code>println</code>函数打印该数字的描述。在这个例子中,数字<code>5</code>被准确的识别为了一个质数。</p>
<blockquote>
<p>注意:</p>
<p><code>fallthrough</code>关键字不会检查它下一个将会落入执行的 case 中的匹配条件。<code>fallthrough</code>简单地使代码执行继续连接到下一个 case 中的执行代码,这和 C 语言标准中的<code>switch</code>语句特性是一样的。</p>
<p>注意:<br><code>fallthrough</code>关键字不会检查它下一个将会落入执行的 case 中的匹配条件。<code>fallthrough</code>简单地使代码执行继续连接到下一个 case 中的执行代码,这和 C 语言标准中的<code>switch</code>语句特性是一样的。</p>
</blockquote>
<p><a name="labeled_statements"></a></p>
<h3 id="-labeled-statements-">带标签的语句Labeled Statements</h3>
<p>在 Swift 中,你可以在循环体和<code>switch</code>代码块中嵌套循环体和<code>switch</code>代码块来创造复杂的控制流结构。然而,循环体和<code>switch</code>代码块两者都可以使用<code>break</code>语句来提前结束整个方法体。因此,显示地指明<code>break</code>语句想要终止的是哪个循环体或者<code>switch</code>代码块,会很有用。类似地,如果你有许多嵌套的循环体,显示指明<code>continue</code>语句想要影响哪一个循环体也会非常有用。</p>
<p>为了实现这个目的,你可以使用标签来标记一个循环体或者<code>switch</code>代码块,当使用<code>break</code>或者<code>continue</code>时,带上这个标签,可以控制该标签代表对象的中断或者执行。</p>
<p>产生一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签,并且该标签后面还需带着一个冒号。下面是一个<code>while</code>循环体的语法,同样的规则适用于所有的循环体和<code>switch</code>代码块。</p>
<pre><code>`label name`: while `condition` {
`statements`
}
</code></pre><p>下面的例子是在一个带有标签的<code>while</code>循环体中调用<code>break</code><code>continue</code>语句,该循环体是前面章节中<em>蛇和梯子</em>的改编版本。这次,游戏增加了一条额外的规则:</p>
<blockquote>
<p><code>label name</code>: while <code>condition</code> {<br> <code>statements</code><br>} </p>
</blockquote>
<p>下面的例子是在一个带有标签的<code>while</code>循环体中调用<code>break</code><code>continue</code>语句,该循环体是前面章节中<em>蛇和梯子</em>的改编版本。这次,游戏增加了一条额外的规则:</p>
<ul>
<li>为了获胜,你必须<em>刚好</em>落在第 25 个方块中。</li>
</ul>
@ -1133,16 +1104,12 @@ println(&quot;Game over!&quot;)
</code></pre>
<p>每次循环迭代开始时掷骰子。与之前玩家掷完骰子就立即移动不同,这里使用了<code>switch</code>来考虑每次移动可能产生的结果,从而决定玩家本次是否能够移动。</p>
<ul>
<li><p>如果骰子数刚好使玩家移动到最终的方格里,游戏结束。<code>break gameLoop</code>语句跳转控制去执行<code>while</code>循环体后的第一行代码,游戏结束。</p>
</li>
<li><p>如果骰子数将会使玩家的移动超出最后的方格,那么这种移动是不合法的,玩家需要重新掷骰子。<code>continue gameLoop</code>语句结束本次<code>while</code>循环的迭代,开始下一次循环迭代。</p>
</li>
<li><p>在剩余的所有情况中,骰子数产生的都是合法的移动。玩家向前移动骰子数个方格,然后游戏逻辑再处理玩家当前是否处于蛇头或者梯子的底部。本次循环迭代结束,控制跳转到<code>while</code>循环体的条件判断语句处,再决定是否能够继续执行下次循环迭代。</p>
</li>
<li>如果骰子数刚好使玩家移动到最终的方格里,游戏结束。<code>break gameLoop</code>语句跳转控制去执行<code>while</code>循环体后的第一行代码,游戏结束。</li>
<li>如果骰子数将会使玩家的移动超出最后的方格,那么这种移动是不合法的,玩家需要重新掷骰子。<code>continue gameLoop</code>语句结束本次<code>while</code>循环的迭代,开始下一次循环迭代。</li>
<li>在剩余的所有情况中,骰子数产生的都是合法的移动。玩家向前移动骰子数个方格,然后游戏逻辑再处理玩家当前是否处于蛇头或者梯子的底部。本次循环迭代结束,控制跳转到<code>while</code>循环体的条件判断语句处,再决定是否能够继续执行下次循环迭代。</li>
</ul>
<blockquote>
<p>注意:</p>
<p>如果上述的<code>break</code>语句没有使用<code>gameLoop</code>标签,那么它将会中断<code>switch</code>代码块而不是<code>while</code>循环体。使用<code>gameLoop</code>标签清晰的表明了<code>break</code>想要中断的是哪个代码块。
<p>注意:<br>如果上述的<code>break</code>语句没有使用<code>gameLoop</code>标签,那么它将会中断<code>switch</code>代码块而不是<code>while</code>循环体。使用<code>gameLoop</code>标签清晰的表明了<code>break</code>想要中断的是哪个代码块。
同时请注意,当调用<code>continue gameLoop</code>去跳转到下一次循环迭代时,这里使用<code>gameLoop</code>标签并不是严格必须的。因为在这个游戏中,只有一个循环体,所以<code>continue</code>语句会影响到哪个循环体是没有歧义的。然而,<code>continue</code>语句使用<code>gameLoop</code>标签也是没有危害的。这样做符合标签的使用规则,同时参照旁边的<code>break gameLoop</code>,能够使游戏的逻辑更加清晰和易于理解。</p>
</blockquote>

56
chapter2/06_Functions.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.6" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.6" 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_98">
<section class="normal" id="section-gitbook_45">
<blockquote>
<p>翻译honghaoz</p>
<p>校对LunaticM</p>
<p>翻译honghaoz<br>校对LunaticM </p>
</blockquote>
<h1 id="-functions-">函数Functions</h1>
<hr>
@ -605,8 +604,8 @@
</ul>
<p>函数是用来完成特定任务的独立的代码块。你给一个函数起一个合适的名字,用来标示函数做什么,并且当函数需要执行的时候,这个名字会被“调用”。</p>
<p>Swift 统一的函数语法足够灵活,可以用来表示任何函数,包括从最简单的没有参数名字的 C 风格函数,到复杂的带局部和外部参数名的 Objective-C 风格函数。参数可以提供默认值,以简化函数调用。参数也可以即当做传入参数,也当做传出参数,也就是说,一旦函数执行结束,传入的参数值可以被修改。</p>
<p>在 Swift 中,每个函数都有一种类型,包括函数的参数值类型和返回值类型。你可以把函数类型当做任何其他普通变量类型一样处理,这样就可以更简单地把函数当做别的函数的参数,也可以从其他函数中返回函数。函数的定义可以写在在其他函数定义中,这样可以在嵌套函数范围内实现功能封装。
<a name="Defining_and_Calling_Functions"></a></p>
<p>在 Swift 中,每个函数都有一种类型,包括函数的参数值类型和返回值类型。你可以把函数类型当做任何其他普通变量类型一样处理,这样就可以更简单地把函数当做别的函数的参数,也可以从其他函数中返回函数。函数的定义可以写在在其他函数定义中,这样可以在嵌套函数范围内实现功能封装。</p>
<p><a name="Defining_and_Calling_Functions"></a></p>
<h2 id="-defining-and-calling-functions-">函数的定义与调用Defining and Calling Functions</h2>
<p>当你定义一个函数时你可以定义一个或多个有名字和类型的值作为函数的输入称为参数parameters也可以定义某种类型的值作为函数执行结束的输出称为返回类型</p>
<p>每个函数有个函数名用来描述函数执行的任务。要使用一个函数时你用函数名“调用”并传给它匹配的输入值称作实参arguments。一个函数的实参必须与函数参数表里参数的顺序一致。</p>
@ -664,8 +663,7 @@ sayGoodbye(&quot;Dave&quot;)
</code></pre>
<p>因为这个函数不需要返回值,所以这个函数的定义中没有返回箭头(-&gt;)和返回类型。</p>
<blockquote>
<p>注意:</p>
<p>严格上来说,虽然没有返回值被定义,<code>sayGoodbye</code> 函数依然返回了值。没有定义返回类型的函数会返回特殊的值,叫 <code>Void</code>。它其实是一个空的元组tuple没有任何元素可以写成<code>()</code></p>
<p>注意:<br>严格上来说,虽然没有返回值被定义,<code>sayGoodbye</code> 函数依然返回了值。没有定义返回类型的函数会返回特殊的值,叫 <code>Void</code>。它其实是一个空的元组tuple没有任何元素可以写成<code>()</code></p>
</blockquote>
<p>被调用时,一个函数的返回值可以被忽略:</p>
<pre><code class="lang-swift">func printAndCount(stringToPrint: String) -&gt; Int {
@ -682,8 +680,7 @@ printWithoutCounting(&quot;hello, world&quot;)
</code></pre>
<p>第一个函数 <code>printAndCount</code>,输出一个字符串并返回 <code>Int</code> 类型的字符数。第二个函数 <code>printWithoutCounting</code>调用了第一个函数,但是忽略了它的返回值。当第二个函数被调用时,消息依然会由第一个函数输出,但是返回值不会被用到。</p>
<blockquote>
<p>注意:</p>
<p>返回值可以被忽略但定义了有返回值的函数必须返回一个值如果在函数定义底部没有返回任何值这叫导致编译错误compile-time error</p>
<p>注意:<br>返回值可以被忽略但定义了有返回值的函数必须返回一个值如果在函数定义底部没有返回任何值这叫导致编译错误compile-time error</p>
</blockquote>
<h3 id="-functions-with-multiple-return-values-">多重返回值函数Functions with Multiple Return Values</h3>
<p>你可以用元组tuple类型让多个值作为一个复合值从函数中返回。</p>
@ -705,11 +702,12 @@ printWithoutCounting(&quot;hello, world&quot;)
}
</code></pre>
<p>你可以用 <code>count</code> 函数来处理任何一个字符串,返回的值将是一个包含三个 <code>Int</code> 型值的元组tuple</p>
<pre><code>let total = count(&quot;some arbitrary string!&quot;)
<pre><code class="lang-swift">let total = count(&quot;some arbitrary string!&quot;)
println(&quot;\(total.vowels) vowels and \(total.consonants) consonants&quot;)
// prints &quot;6 vowels and 13 consonants
</code></pre><p>需要注意的是,元组的成员不需要在函数中返回时命名,因为它们的名字已经在函数返回类型有有了定义。
<a name="Function_Parameter_Names"></a></p>
</code></pre>
<p>需要注意的是,元组的成员不需要在函数中返回时命名,因为它们的名字已经在函数返回类型有有了定义。</p>
<p><a name="Function_Parameter_Names"></a></p>
<h2 id="-function-parameter-names-">函数参数名称Function Parameter Names</h2>
<p>以上所有的函数都给它们的参数定义了<code>参数名parameter name</code></p>
<pre><code class="lang-swift">func someFunction(parameterName: Int) {
@ -727,8 +725,7 @@ println(&quot;\(total.vowels) vowels and \(total.consonants) consonants&quot;)
}
</code></pre>
<blockquote>
<p>注意:</p>
<p>如果你提供了外部参数名,那么函数在被调用时,必须使用外部参数名。</p>
<p>注意:<br>如果你提供了外部参数名,那么函数在被调用时,必须使用外部参数名。 </p>
</blockquote>
<p>以下是个例子,这个函数使用一个<code>结合者joiner</code>把两个字符串联在一起:</p>
<pre><code class="lang-swift">func join(s1: String, s2: String, joiner: String) -&gt; String {
@ -751,8 +748,7 @@ println(&quot;\(total.vowels) vowels and \(total.consonants) consonants&quot;)
</code></pre>
<p>使用外部参数名让第二个版本的 <code>join</code> 函数的调用更为有表现力,更为通顺,同时还保持了函数体是可读的和有明确意图的。</p>
<blockquote>
<p>注意:</p>
<p>当其他人在第一次读你的代码,函数参数的意图显得不明显时,考虑使用外部参数名。如果函数参数名的意图是很明显的,那就不需要定义外部参数名了。</p>
<p>注意:<br>当其他人在第一次读你的代码,函数参数的意图显得不明显时,考虑使用外部参数名。如果函数参数名的意图是很明显的,那就不需要定义外部参数名了。 </p>
</blockquote>
<h3 id="-shorthand-external-parameter-names-">简写外部参数名Shorthand External Parameter Names</h3>
<p>如果你需要提供外部参数名,但是局部参数名已经定义好了,那么你不需要写两次这些参数名。相反,只写一次参数名,并用<code>井号(#</code>作为前缀就可以了。这告诉 Swift 使用这个参数名作为局部和外部参数名。</p>
@ -773,8 +769,7 @@ println(&quot;\(total.vowels) vowels and \(total.consonants) consonants&quot;)
<h3 id="-default-parameter-values-">默认参数值Default Parameter Values</h3>
<p>你可以在函数体中为每个参数定义<code>默认值</code>。当默认值被定义后,调用这个函数时可以略去这个参数。</p>
<blockquote>
<p>注意:</p>
<p>将带有默认值的参数放在函数参数表的最后。这样可以保证在函数调用时,非默认参数的顺序是一致的,同时使得相同的函数在不同情况下调用时显得更为清晰。</p>
<p>注意:<br>将带有默认值的参数放在函数参数表的最后。这样可以保证在函数调用时,非默认参数的顺序是一致的,同时使得相同的函数在不同情况下调用时显得更为清晰。 </p>
</blockquote>
<p>以下是另一个版本的<code>join</code>函数,其中<code>joiner</code>有了默认参数值:</p>
<pre><code class="lang-swift">func join(string s1: String, toString s2: String, withJoiner joiner: String = &quot; &quot;) -&gt; String {
@ -802,8 +797,7 @@ println(&quot;\(total.vowels) vowels and \(total.consonants) consonants&quot;)
// returns &quot;hello-world&quot;
</code></pre>
<blockquote>
<p>注意:</p>
<p>你可以使用<code>下划线_</code>作为默认值参数的外部参数名,这样可以在调用时不用提供外部参数名。但是给带默认值的参数命名总是更加合适的。</p>
<p>注意:<br>你可以使用<code>下划线_</code>作为默认值参数的外部参数名,这样可以在调用时不用提供外部参数名。但是给带默认值的参数命名总是更加合适的。 </p>
</blockquote>
<h3 id="-variadic-parameters-">可变参数Variadic Parameters</h3>
<p>一个<code>可变参数variadic parameter</code>可以接受一个或多个值。函数调用时,你可以用可变参数来传入不确定数量的输入参数。通过在变量类型名后面加入<code>...</code>的方式来定义可变参数。</p>
@ -812,7 +806,7 @@ println(&quot;\(total.vowels) vowels and \(total.consonants) consonants&quot;)
<pre><code class="lang-swift">func arithmeticMean(numbers: Double...) -&gt; Double {
var total: Double = 0
for number in numbers {
total += number
total += number
}
return total / Double(numbers.count)
}
@ -822,8 +816,7 @@ arithmeticMean(3, 8, 19)
// returns 10.0, which is the arithmetic mean of these three numbers
</code></pre>
<blockquote>
<p>注意:</p>
<p>一个函数至多能有一个可变参数,而且它必须是参数表中最后的一个。这样做是为了避免函数调用时出现歧义。</p>
<p>注意:<br>一个函数至多能有一个可变参数,而且它必须是参数表中最后的一个。这样做是为了避免函数调用时出现歧义。 </p>
</blockquote>
<p>如果函数有一个或多个带默认值的参数,而且还有一个可变参数,那么把可变参数放在参数表的最后。</p>
<h3 id="-constant-and-variable-parameters-">常量参数和变量参数Constant and Variable Parameters</h3>
@ -846,16 +839,14 @@ let paddedString = alignRight(originalString, 10, &quot;-&quot;)
<p><code>alignRight</code> 函数将参数 <code>string</code> 定义为变量参数。这意味着 <code>string</code> 现在可以作为一个局部变量,用传入的字符串值初始化,并且可以在函数体中进行操作。</p>
<p>该函数首先计算出多少个字符需要被添加到 <code>string</code> 的左边,以右对齐到总的字符串中。这个值存在局部常量 <code>amountToPad</code> 中。这个函数然后将 <code>amountToPad</code> 多的填充pad字符填充到 <code>string</code> 左边,并返回结果。它使用了 <code>string</code> 这个变量参数来进行所有字符串操作。</p>
<blockquote>
<p>注意:</p>
<p>对变量参数所进行的修改在函数调用结束后便消失了,并且对于函数体外是不可见的。变量参数仅仅存在于函数调用的生命周期中。</p>
<p>注意:<br>对变量参数所进行的修改在函数调用结束后便消失了,并且对于函数体外是不可见的。变量参数仅仅存在于函数调用的生命周期中。 </p>
</blockquote>
<h3 id="-in-out-parameters-">输入输出参数In-Out Parameters</h3>
<p>变量参数正如上面所述仅仅能在函数体内被更改。如果你想要一个函数可以修改参数的值并且想要在这些修改在函数调用结束后仍然存在那么就应该把这个参数定义为输入输出参数In-Out Parameters</p>
<p>定义一个输入输出参数时,在参数定义前加 <code>inout</code> 关键字。一个输入输出参数有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。</p>
<p>你只能传入一个变量作为输入输出参数。你不能传入常量或者字面量literal value因为这些量是不能被修改的。当传入的参数作为输入输出参数时需要在参数前加<code>&amp;</code>符,表示这个值可以被函数修改。</p>
<blockquote>
<p>注意:</p>
<p>输入输出参数不能有默认值,而且可变参数不能用 <code>inout</code> 标记。如果你用 <code>inout</code> 标记一个参数,这个参数不能被 <code>var</code> 或者 <code>let</code> 标记。</p>
<p>注意:<br>输入输出参数不能有默认值,而且可变参数不能用 <code>inout</code> 标记。如果你用 <code>inout</code> 标记一个参数,这个参数不能被 <code>var</code> 或者 <code>let</code> 标记。 </p>
</blockquote>
<p>下面是例子,<code>swapTwoInts</code> 函数,有两个分别叫做 <code>a</code><code>b</code> 的输出输出参数:</p>
<pre><code class="lang-swift">func swapTwoInts(inout a: Int, inout b: Int) {
@ -874,8 +865,7 @@ println(&quot;someInt is now \(someInt), and anotherInt is now \(anotherInt)&quo
</code></pre>
<p>从上面这个例子中,我们可以看到 <code>someInt</code><code>anotherInt</code> 的原始值在 <code>swapTwoInts</code> 函数中被修改,尽管它们的定义在函数体外。</p>
<blockquote>
<p>注意:</p>
<p>输出输出参数和返回值是不一样的。上面的 <code>swapTwoInts</code> 函数并没有定义任何返回值,但仍然修改了 <code>someInt</code><code>anotherInt</code> 的值。输入输出参数是函数对函数体外产生影响的另一种方式。</p>
<p>注意:<br>输出输出参数和返回值是不一样的。上面的 <code>swapTwoInts</code> 函数并没有定义任何返回值,但仍然修改了 <code>someInt</code><code>anotherInt</code> 的值。输入输出参数是函数对函数体外产生影响的另一种方式。 </p>
</blockquote>
<p><a name="Function_Types"></a></p>
<h2 id="-function-types-">函数类型Function Types</h2>
@ -912,7 +902,7 @@ func multiplyTwoInts(a: Int, b: Int) -&gt; Int {
println(&quot;Result: \(mathFunction(2, 3))&quot;)
// prints &quot;Result: 6&quot;
</code></pre>
<p>就像其他类型一样,当赋值一个函数给常量或变量时,你可以让 Swift 来推其函数类型:</p>
<p>就像其他类型一样,当赋值一个函数给常量或变量时,你可以让 Swift 来推其函数类型:</p>
<pre><code class="lang-swift">let anotherMathFunction = addTwoInts
// anotherMathFunction is inferred to be of type (Int, Int) -&gt; Int
</code></pre>

132
chapter2/07_Closures.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.7" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.7" 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_100">
<section class="normal" id="section-gitbook_47">
<blockquote>
<p>翻译wh1100717</p>
<p>校对lyuka</p>
<p>翻译wh1100717<br>校对lyuka </p>
</blockquote>
<h1 id="-closures-">闭包Closures</h1>
<hr>
@ -607,8 +606,7 @@ Swift 中的闭包与 C 和 Objective-C 中的代码块blocks以及其他
<p>闭包可以捕获和存储其所在上下文中任意常量和变量的引用。
这就是所谓的闭合并包裹着这些常量和变量俗称闭包。Swift 会为您管理在捕获过程中涉及到的所有内存操作。</p>
<blockquote>
<p>注意:</p>
<p>如果您不熟悉捕获capturing这个概念也不用担心您可以在 <a href="#capturing_values">值捕获</a> 章节对其进行详细了解。</p>
<p>注意:<br>如果您不熟悉捕获capturing这个概念也不用担心您可以在 <a href="#capturing_values">值捕获</a> 章节对其进行详细了解。 </p>
</blockquote>
<p><a href="../chapter2/06_Functions.html">函数</a> 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:</p>
<ul>
@ -635,20 +633,22 @@ Swift 中的闭包与 C 和 Objective-C 中的代码块blocks以及其他
<p>Swift 标准库提供了<code>sort</code>函数,会根据您提供的基于输出类型排序的闭包函数将已知类型数组中的值进行排序。
一旦排序完成,函数会返回一个与原数组大小相同的新数组,该数组中包含已经正确排序的同类型元素。</p>
<p>下面的闭包表达式示例使用<code>sort</code>函数对一个<code>String</code>类型的数组进行字母逆序排序,以下是初始数组值:</p>
<pre><code>let names = [&quot;Chris&quot;, &quot;Alex&quot;, &quot;Ewa&quot;, &quot;Barry&quot;, &quot;Daniella&quot;]
</code></pre><p><code>sort</code>函数需要传入两个参数:</p>
<pre><code class="lang-swift">let names = [&quot;Chris&quot;, &quot;Alex&quot;, &quot;Ewa&quot;, &quot;Barry&quot;, &quot;Daniella&quot;]
</code></pre>
<p><code>sort</code>函数需要传入两个参数:</p>
<ul>
<li>已知类型的数组</li>
<li>闭包函数,该闭包函数需要传入与数组类型相同的两个值,并返回一个布尔类型值来告诉<code>sort</code>函数当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回<code>true</code>,反之返回<code>false</code></li>
</ul>
<p>该例子对一个<code>String</code>类型的数组进行排序,因此排序闭包函数类型需为<code>(String, String) -&gt; Bool</code></p>
<p>提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为<code>sort</code>函数的第二个参数传入:</p>
<pre><code>func backwards(s1: String, s2: String) -&gt; Bool {
<pre><code class="lang-swift">func backwards(s1: String, s2: String) -&gt; Bool {
return s1 &gt; s2
}
var reversed = sort(names, backwards)
// reversed 为 [&quot;Ewa&quot;, &quot;Daniella&quot;, &quot;Chris&quot;, &quot;Barry&quot;, &quot;Alex&quot;]
</code></pre><p>如果第一个字符串 (<code>s1</code>) 大于第二个字符串 (<code>s2</code>)<code>backwards</code>函数返回<code>true</code>,表示在新的数组中<code>s1</code>应该出现在<code>s2</code>前。
</code></pre>
<p>如果第一个字符串 (<code>s1</code>) 大于第二个字符串 (<code>s2</code>)<code>backwards</code>函数返回<code>true</code>,表示在新的数组中<code>s1</code>应该出现在<code>s2</code>前。
对于字符串中的字符来说,“大于” 表示 “按照字母顺序较晚出现”。
这意味着字母<code>&quot;B&quot;</code>大于字母<code>&quot;A&quot;</code>,字符串<code>&quot;Tom&quot;</code>大于字符串<code>&quot;Tim&quot;</code>
其将进行字母逆序排序,<code>&quot;Barry&quot;</code>将会排在<code>&quot;Alex&quot;</code>之后。</p>
@ -657,93 +657,100 @@ var reversed = sort(names, backwards)
<p><a name="closure_expression_syntax"></a></p>
<h3 id="-closure-expression-syntax-">闭包表达式语法Closure Expression Syntax</h3>
<p>闭包表达式语法有如下一般形式:</p>
<pre><code>{ (parameters) -&gt; returnType in
<pre><code class="lang-swift">{ (parameters) -&gt; returnType in
statements
}
</code></pre><p>闭包表达式语法可以使用常量、变量和<code>inout</code>类型作为参数,不提供默认值。
</code></pre>
<p>闭包表达式语法可以使用常量、变量和<code>inout</code>类型作为参数,不提供默认值。
也可以在参数列表的最后使用可变参数。
元组也可以作为参数和返回值。</p>
<p>下面的例子展示了之前<code>backwards</code>函数对应的闭包表达式版本的代码:</p>
<pre><code>reversed = sort(names, { (s1: String, s2: String) -&gt; Bool in
<pre><code class="lang-swift">reversed = sort(names, { (s1: String, s2: String) -&gt; Bool in
return s1 &gt; s2
})
</code></pre><p>需要注意的是内联闭包参数和返回值类型声明与<code>backwards</code>函数类型声明相同。
})
</code></pre>
<p>需要注意的是内联闭包参数和返回值类型声明与<code>backwards</code>函数类型声明相同。
在这两种方式中,都写成了<code>(s1: String, s2: String) -&gt; Bool</code>
然而在内联闭包表达式中,函数和返回值类型都写在大括号内,而不是大括号外。</p>
<p>闭包的函数体部分由关键字<code>in</code>引入。
该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。</p>
<p>因为这个闭包的函数体部分如此短以至于可以将其改写成一行代码:</p>
<pre><code>reversed = sort(names, { (s1: String, s2: String) -&gt; Bool in return s1 &gt; s2 } )
</code></pre><p>这说明<code>sort</code>函数的整体调用保持不变,一对圆括号仍然包裹住了函数中整个参数集合。而其中一个参数现在变成了内联闭包(相比于<code>backwards</code>版本的代码)。</p>
<pre><code class="lang-swift">reversed = sort(names, { (s1: String, s2: String) -&gt; Bool in return s1 &gt; s2 } )
</code></pre>
<p>这说明<code>sort</code>函数的整体调用保持不变,一对圆括号仍然包裹住了函数中整个参数集合。而其中一个参数现在变成了内联闭包(相比于<code>backwards</code>版本的代码)。</p>
<p><a name="inferring_type_from_context"></a></p>
<h3 id="-inferring-type-from-context-">根据上下文推断类型Inferring Type From Context</h3>
<p>因为排序闭包函数是作为<code>sort</code>函数的参数进行传入的Swift可以推断其参数和返回值的类型。
<code>sort</code>期望第二个参数是类型为<code>(String, String) -&gt; Bool</code>的函数,因此实际上<code>String</code>,<code>String</code><code>Bool</code>类型并不需要作为闭包表达式定义中的一部分。
因为所有的类型都可以被正确推断,返回箭头 (<code>-&gt;</code>) 和围绕在参数周围的括号也可以被省略:</p>
<pre><code>reversed = sort(names, { s1, s2 in return s1 &gt; s2 } )
</code></pre><p>实际上任何情况下,通过内联闭包表达式构造的闭包作为参数传递给函数时,都可以推断出闭包的参数和返回值类型,这意味着您几乎不需要利用完整格式构造任何内联闭包。</p>
<pre><code class="lang-swift">reversed = sort(names, { s1, s2 in return s1 &gt; s2 } )
</code></pre>
<p>实际上任何情况下,通过内联闭包表达式构造的闭包作为参数传递给函数时,都可以推断出闭包的参数和返回值类型,这意味着您几乎不需要利用完整格式构造任何内联闭包。</p>
<p><a name="implicit_returns_from_single_expression_closures"></a></p>
<h3 id="-implicit-return-from-single-expression-clossures-">单表达式闭包隐式返回Implicit Return From Single-Expression Clossures</h3>
<p>单行表达式闭包可以通过隐藏<code>return</code>关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:</p>
<pre><code>reversed = sort(names, { s1, s2 in s1 &gt; s2 } )
</code></pre><p>在这个例子中,<code>sort</code>函数的第二个参数函数类型明确了闭包必须返回一个<code>Bool</code>类型值。
<pre><code class="lang-swift">reversed = sort(names, { s1, s2 in s1 &gt; s2 } )
</code></pre>
<p>在这个例子中,<code>sort</code>函数的第二个参数函数类型明确了闭包必须返回一个<code>Bool</code>类型值。
因为闭包函数体只包含了一个单一表达式 (<code>s1 &gt; s2</code>),该表达式返回<code>Bool</code>类型值,因此这里没有歧义,<code>return</code>关键字可以省略。</p>
<p><a name="shorthand_argument_names"></a></p>
<h3 id="-shorthand-argument-names-">参数名称缩写Shorthand Argument Names</h3>
<p>Swift 自动为内联函数提供了参数名称缩写功能,您可以直接通过<code>$0</code>,<code>$1</code>,<code>$2</code>来顺序调用闭包的参数。</p>
<p>如果您在闭包表达式中使用参数名称缩写,您可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。
<code>in</code>关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:</p>
<pre><code>reversed = sort(names, { $0 &gt; $1 } )
</code></pre><p>在这个例子中,<code>$0</code><code>$1</code>表示闭包中第一个和第二个<code>String</code>类型的参数。</p>
<pre><code class="lang-swift">reversed = sort(names, { $0 &gt; $1 } )
</code></pre>
<p>在这个例子中,<code>$0</code><code>$1</code>表示闭包中第一个和第二个<code>String</code>类型的参数。</p>
<p><a name="operator_functions"></a></p>
<h3 id="-operator-functions-">运算符函数Operator Functions</h3>
<p>实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。
Swift 的<code>String</code>类型定义了关于大于号 (<code>&gt;</code>) 的字符串实现,其作为一个函数接受两个<code>String</code>类型的参数并返回<code>Bool</code>类型的值。
而这正好与<code>sort</code>函数的第二个参数需要的函数类型相符合。
因此您可以简单地传递一个大于号Swift可以自动推断出您想使用大于号的字符串函数实现</p>
<pre><code>reversed = sort(names, &gt;)
</code></pre><p>更多关于运算符表达式的内容请查看 <a href="../chapter2/23_Advanced_Operators.html#operator_functions">运算符函数</a></p>
<pre><code class="lang-swift">reversed = sort(names, &gt;)
</code></pre>
<p>更多关于运算符表达式的内容请查看 <a href="../chapter2/23_Advanced_Operators.html#operator_functions">运算符函数</a></p>
<p><a name="trailing_closures"></a></p>
<h2 id="-trailing-closures-">尾随闭包Trailing Closures</h2>
<p>如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。
尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。</p>
<pre><code>func someFunctionThatTakesAClosure(closure: () -&gt; ()) {
<pre><code class="lang-swift">func someFunctionThatTakesAClosure(closure: () -&gt; ()) {
// 函数体部分
}
// 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure({
// 闭包主体部分
})
})
// 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() {
// 闭包主体部分
// 闭包主体部分
}
</code></pre><blockquote>
<p>注意:</p>
<p>如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把<code>()</code>省略掉。</p>
</code></pre>
<blockquote>
<p>注意:<br>如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把<code>()</code>省略掉。 </p>
</blockquote>
<p>在上例中作为<code>sort</code>函数参数的字符串排序闭包可以改写为:</p>
<pre><code>reversed = sort(names) { $0 &gt; $1 }
</code></pre><p>当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。
<pre><code class="lang-swift">reversed = sort(names) { $0 &gt; $1 }
</code></pre>
<p>当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。
举例来说Swift 的<code>Array</code>类型有一个<code>map</code>方法,其获取一个闭包表达式作为其唯一参数。
数组中的每一个元素调用一次该闭包函数,并返回该元素所映射的值(也可以是不同类型的值)。
具体的映射方式和返回值类型由闭包来指定。</p>
<p>当提供给数组闭包函数后,<code>map</code>方法将返回一个新的数组,数组中包含了与原数组一一对应的映射后的值。</p>
<p>下例介绍了如何在<code>map</code>方法中使用尾随闭包将<code>Int</code>类型数组<code>[16,58,510]</code>转换为包含对应<code>String</code>类型的数组<code>[&quot;OneSix&quot;, &quot;FiveEight&quot;, &quot;FiveOneZero&quot;]</code>:</p>
<pre><code>let digitNames = [
<pre><code class="lang-swift">let digitNames = [
0: &quot;Zero&quot;, 1: &quot;One&quot;, 2: &quot;Two&quot;, 3: &quot;Three&quot;, 4: &quot;Four&quot;,
5: &quot;Five&quot;, 6: &quot;Six&quot;, 7: &quot;Seven&quot;, 8: &quot;Eight&quot;, 9: &quot;Nine&quot;
]
let numbers = [16, 58, 510]
</code></pre><p>如上代码创建了一个数字位和他们名字映射的英文版本字典。
</code></pre>
<p>如上代码创建了一个数字位和它们名字映射的英文版本字典。
同时定义了一个准备转换为字符串的整型数组。</p>
<p>您现在可以通过传递一个尾随闭包给<code>numbers</code><code>map</code>方法来创建对应的字符串版本数组。
需要注意的时调用<code>numbers.map</code>不需要在<code>map</code>后面包含任何括号,因为其只需要传递闭包表达式这一个参数,并且该闭包表达式参数通过尾随方式进行撰写:</p>
<pre><code>let strings = numbers.map {
<pre><code class="lang-swift">let strings = numbers.map {
(var number) -&gt; String in
var output = &quot;&quot;
while number &gt; 0 {
@ -754,16 +761,14 @@ let numbers = [16, 58, 510]
}
// strings 常量被推断为字符串类型数组,即 String[]
// 其值为 [&quot;OneSix&quot;, &quot;FiveEight&quot;, &quot;FiveOneZero&quot;]
</code></pre><p><code>map</code>在数组中为每一个元素调用了闭包表达式。
</code></pre>
<p><code>map</code>在数组中为每一个元素调用了闭包表达式。
您不需要指定闭包的输入参数<code>number</code>的类型,因为可以通过要映射的数组类型进行推断。</p>
<p>闭包<code>number</code>参数被声明为一个变量参数(变量的具体描述请参看<a href="../chapter2/06_Functions.html#constant_and_variable_parameters">常量参数和变量参数</a>),因此可以在闭包函数体内对其进行修改。闭包表达式制定了返回类型为<code>String</code>,以表明存储映射值的新数组类型为<code>String</code></p>
<p>闭包表达式在每次被调用的时候创建了一个字符串并返回。
其使用求余运算符 (number % 10) 计算最后一位数字并利用<code>digitNames</code>字典获取所映射的字符串。</p>
<blockquote>
<p>注意:</p>
<p>字典<code>digitNames</code>下标后跟着一个叹号 (!),因为字典下标返回一个可选值 (optional value),表明即使该 key 不存在也不会查找失败。
在上例中,它保证了<code>number % 10</code>可以总是作为一个<code>digitNames</code>字典的有效下标 key。
因此叹号可以用于强制解析 (force-unwrap) 存储在可选下标项中的<code>String</code>类型值。</p>
<p>注意:<br>字典<code>digitNames</code>下标后跟着一个叹号 (!),因为字典下标返回一个可选值 (optional value),表明即使该 key 不存在也不会查找失败。<br>在上例中,它保证了<code>number % 10</code>可以总是作为一个<code>digitNames</code>字典的有效下标 key。<br>因此叹号可以用于强制解析 (force-unwrap) 存储在可选下标项中的<code>String</code>类型值。 </p>
</blockquote>
<p><code>digitNames</code>字典中获取的字符串被添加到输出的前部,逆序建立了一个字符串版本的数字。
(在表达式<code>number % 10</code>如果number为16则返回658返回8510返回0</p>
@ -782,7 +787,7 @@ let numbers = [16, 58, 510]
嵌套函数<code>incrementor</code>从上下文中捕获了两个值,<code>runningTotal</code><code>amount</code>
之后<code>makeIncrementor</code><code>incrementor</code>作为闭包返回。
每次调用<code>incrementor</code>时,其会以<code>amount</code>作为增量增加<code>runningTotal</code>的值。</p>
<pre><code>func makeIncrementor(forIncrement amount: Int) -&gt; () -&gt; Int {
<pre><code class="lang-swift">func makeIncrementor(forIncrement amount: Int) -&gt; () -&gt; Int {
var runningTotal = 0
func incrementor() -&gt; Int {
runningTotal += amount
@ -790,7 +795,8 @@ let numbers = [16, 58, 510]
}
return incrementor
}
</code></pre><p><code>makeIncrementor</code>返回类型为<code>() -&gt; Int</code>
</code></pre>
<p><code>makeIncrementor</code>返回类型为<code>() -&gt; Int</code>
这意味着其返回的是一个函数,而不是一个简单类型值。
该函数在每次调用时不接受参数只返回一个<code>Int</code>类型的值。
关于函数返回其他函数的内容,请查看<a href="../chapter2/06_Functions.html#function_types_as_return_types">函数类型作为返回类型</a></p>
@ -800,40 +806,39 @@ let numbers = [16, 58, 510]
<p><code>incrementor</code>函数用来执行实际的增加操作。
该函数简单地使<code>runningTotal</code>增加<code>amount</code>,并将其返回。</p>
<p>如果我们单独看这个函数,会发现看上去不同寻常:</p>
<pre><code>func incrementor() -&gt; Int {
<pre><code class="lang-swift">func incrementor() -&gt; Int {
runningTotal += amount
return runningTotal
}
</code></pre><p><code>incrementor</code>函数并没有获取任何参数,但是在函数体内访问了<code>runningTotal</code><code>amount</code>变量。这是因为其通过捕获在包含它的函数体内已经存在的<code>runningTotal</code><code>amount</code>变量而实现。</p>
</code></pre>
<p><code>incrementor</code>函数并没有获取任何参数,但是在函数体内访问了<code>runningTotal</code><code>amount</code>变量。这是因为其通过捕获在包含它的函数体内已经存在的<code>runningTotal</code><code>amount</code>变量而实现。</p>
<p>由于没有修改<code>amount</code>变量,<code>incrementor</code>实际上捕获并存储了该变量的一个副本,而该副本随着<code>incrementor</code>一同被存储。</p>
<p>然而,因为每次调用该函数的时候都会修改<code>runningTotal</code>的值,<code>incrementor</code>捕获了当前<code>runningTotal</code>变量的引用,而不是仅仅复制该变量的初始值。捕获一个引用保证了当<code>makeIncrementor</code>结束时候并不会消失,也保证了当下一次执行<code>incrementor</code>函数时,<code>runningTotal</code>可以继续增加。</p>
<blockquote>
<p>注意:</p>
<p>Swift 会决定捕获引用还是拷贝值。
您不需要标注<code>amount</code>或者<code>runningTotal</code>来声明在嵌入的<code>incrementor</code>函数中的使用方式。
Swift 同时也处理<code>runingTotal</code>变量的内存管理操作,如果不再被<code>incrementor</code>函数使用,则会被清除。</p>
<p>注意:<br>Swift 会决定捕获引用还是拷贝值。<br>您不需要标注<code>amount</code>或者<code>runningTotal</code>来声明在嵌入的<code>incrementor</code>函数中的使用方式。<br>Swift 同时也处理<code>runingTotal</code>变量的内存管理操作,如果不再被<code>incrementor</code>函数使用,则会被清除。 </p>
</blockquote>
<p>下面代码为一个使用<code>makeIncrementor</code>的例子:</p>
<pre><code>let incrementByTen = makeIncrementor(forIncrement: 10)
</code></pre><p>该例子定义了一个叫做<code>incrementByTen</code>的常量该常量指向一个每次调用会加10的<code>incrementor</code>函数。
<pre><code class="lang-swift">let incrementByTen = makeIncrementor(forIncrement: 10)
</code></pre>
<p>该例子定义了一个叫做<code>incrementByTen</code>的常量该常量指向一个每次调用会加10的<code>incrementor</code>函数。
调用这个函数多次可以得到以下结果:</p>
<pre><code>incrementByTen()
<pre><code class="lang-swift">incrementByTen()
// 返回的值为10
incrementByTen()
// 返回的值为20
incrementByTen()
// 返回的值为30
</code></pre><p>如果您创建了另一个<code>incrementor</code>,其会有一个属于自己的独立的<code>runningTotal</code>变量的引用。
</code></pre>
<p>如果您创建了另一个<code>incrementor</code>,其会有一个属于自己的独立的<code>runningTotal</code>变量的引用。
下面的例子中,<code>incrementBySevne</code>捕获了一个新的<code>runningTotal</code>变量,该变量和<code>incrementByTen</code>中捕获的变量没有任何联系:</p>
<pre><code>let incrementBySeven = makeIncrementor(forIncrement: 7)
<pre><code class="lang-swift">let incrementBySeven = makeIncrementor(forIncrement: 7)
incrementBySeven()
// 返回的值为7
incrementByTen()
// 返回的值为40
</code></pre><blockquote>
<p>注意:</p>
<p>如果您闭包分配给一个类实例的属性,并且该闭包通过指向该实例或其成员来捕获了该实例,您将创建一个在闭包和实例间的强引用环。
Swift 使用捕获列表来打破这种强引用环。更多信息,请参考 <a href="../chapter2/16_Automatic_Reference_Counting.html#strong_reference_cycles_for_closures">闭包引起的循环强引用</a></p>
</code></pre>
<blockquote>
<p>注意:<br>如果您闭包赋值给一个类实例的属性,并且该闭包通过指向该实例或其成员来捕获了该实例,您将创建一个在闭包和实例间的强引用环。<br>Swift 使用捕获列表来打破这种强引用环。更多信息,请参考 <a href="../chapter2/16_Automatic_Reference_Counting.html#strong_reference_cycles_for_closures">闭包引起的循环强引用</a></p>
</blockquote>
<p><a name="closures_are_reference_types"></a></p>
<h2 id="-closures-are-reference-types-">闭包是引用类型Closures Are Reference Types</h2>
@ -842,10 +847,11 @@ Swift 使用捕获列表来打破这种强引用环。更多信息,请参考 <
<p>无论您将函数/闭包赋值给一个常量还是变量,您实际上都是将常量/变量的值设置为对应函数/闭包的引用。
上面的例子中,<code>incrementByTen</code>指向闭包的引用是一个常量,而并非闭包内容本身。</p>
<p>这也意味着如果您将闭包赋值给了两个不同的常量/变量,两个值都会指向同一个闭包:</p>
<pre><code>let alsoIncrementByTen = incrementByTen
<pre><code class="lang-swift">let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()
// 返回的值为50
</code></pre>
</section>

115
chapter2/08_Enumerations.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.8" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.8" 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_102">
<section class="normal" id="section-gitbook_49">
<blockquote>
<p>翻译yankuangshi</p>
<p>校对shinyzhu</p>
<p>翻译yankuangshi<br>校对shinyzhu </p>
</blockquote>
<h1 id="-enumerations-">枚举Enumerations</h1>
<hr>
@ -610,34 +609,38 @@
<p><a name="enumeration_syntax"></a></p>
<h2 id="-">枚举语法</h2>
<p>使用<code>enum</code>关键词并且把它们的整个定义放在一对大括号内:</p>
<pre><code>enum SomeEumeration {
// enumeration definition goes here
<pre><code class="lang-swift">enum SomeEumeration {
// enumeration definition goes here
}
</code></pre><p>以下是指南针四个方向的一个例子:</p>
<pre><code>enum CompassPoint {
case North
case South
case East
case West
</code></pre>
<p>以下是指南针四个方向的一个例子:</p>
<pre><code class="lang-swift">enum CompassPoint {
case North
case South
case East
case West
}
</code></pre><p>一个枚举中被定义的值(例如 <code>North</code><code>South</code><code>East</code><code>West</code>)是枚举的<strong><em>成员值</em></strong>(或者<strong><em>成员</em></strong>)。<code>case</code>关键词表明新的一行成员值将被定义。</p>
</code></pre>
<p>一个枚举中被定义的值(例如 <code>North</code><code>South</code><code>East</code><code>West</code>)是枚举的<strong><em>成员值</em></strong>(或者<strong><em>成员</em></strong>)。<code>case</code>关键词表明新的一行成员值将被定义。</p>
<blockquote>
<p>注意:</p>
<p>不像 C 和 Objective-C 一样Swift 的枚举成员在被创建时不会被赋予一个默认的整数值。在上面的<code>CompassPoints</code>例子中,<code>North</code><code>South</code><code>East</code><code>West</code>不是隐式的等于<code>0</code><code>1</code><code>2</code><code>3</code>。相反的,这些不同的枚举成员在<code>CompassPoint</code>的一种显示定义中拥有各自不同的值。</p>
<p>注意:<br>不像 C 和 Objective-C 一样Swift 的枚举成员在被创建时不会被赋予一个默认的整数值。在上面的<code>CompassPoints</code>例子中,<code>North</code><code>South</code><code>East</code><code>West</code>不是隐式的等于<code>0</code><code>1</code><code>2</code><code>3</code>。相反的,这些不同的枚举成员在<code>CompassPoint</code>的一种显示定义中拥有各自不同的值。 </p>
</blockquote>
<p>多个成员值可以出现在同一行上,用逗号隔开:</p>
<pre><code>enum Planet {
case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Nepturn
<pre><code class="lang-swift">enum Planet {
case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Nepturn
}
</code></pre><p>每个枚举定义了一个全新的类型。像 Swift 中其他类型一样,它们的名字(例如<code>CompassPoint</code><code>Planet</code>)必须以一个大写字母开头。给枚举类型起一个单数名字而不是复数名字,以便于读起来更加容易理解:</p>
<pre><code>var directionToHead = CompassPoint.West
</code></pre><p><code>directionToHead</code>的类型被推断当它被<code>CompassPoint</code>的一个可能值初始化。一旦<code>directionToHead</code>被声明为一个<code>CompassPoint</code>,你可以使用更短的点(.)语法将其设置为另一个<code>CompassPoint</code>的值:</p>
<pre><code>directionToHead = .East
</code></pre><p><code>directionToHead</code>的类型已知时,当设定它的值时,你可以不再写类型名。使用显示类型的枚举值可以让代码具有更好的可读性。</p>
</code></pre>
<p>每个枚举定义了一个全新的类型。像 Swift 中其他类型一样,它们的名字(例如<code>CompassPoint</code><code>Planet</code>)必须以一个大写字母开头。给枚举类型起一个单数名字而不是复数名字,以便于读起来更加容易理解:</p>
<pre><code class="lang-swift">var directionToHead = CompassPoint.West
</code></pre>
<p><code>directionToHead</code>的类型被推断当它被<code>CompassPoint</code>的一个可能值初始化。一旦<code>directionToHead</code>被声明为一个<code>CompassPoint</code>,你可以使用更短的点(.)语法将其设置为另一个<code>CompassPoint</code>的值:</p>
<pre><code class="lang-swift">directionToHead = .East
</code></pre>
<p><code>directionToHead</code>的类型已知时,当设定它的值时,你可以不再写类型名。使用显示类型的枚举值可以让代码具有更好的可读性。</p>
<p><a name="matching_enumeration_values_with_a_switch_statement"></a></p>
<h2 id="-switch-">匹配枚举值和<code>Switch</code>语句</h2>
<p>你可以匹配单个枚举值和<code>switch</code>语句:</p>
<pre><code>directionToHead = .South
<pre><code class="lang-swift">directionToHead = .South
switch directionToHead {
case .North:
println(&quot;Lots of planets have a north&quot;)
@ -649,12 +652,13 @@ case .West:
println(&quot;Where the skies are blue&quot;)
}
// 输出 &quot;Watch out for penguins”
</code></pre><p>你可以如此理解这段代码:</p>
</code></pre>
<p>你可以如此理解这段代码:</p>
<p>“考虑<code>directionToHead</code>的值。当它等于<code>.North</code>,打印<code>“Lots of planets have a north”</code>。当它等于<code>.South</code>,打印<code>“Watch out for penguins”</code>。”</p>
<p>等等依次类推。</p>
<p>正如在<a href="05_Control_Flow.html">控制流Control Flow</a>中介绍,当考虑一个枚举的成员们时,一个<code>switch</code>语句必须全面。如果忽略了<code>.West</code>这种情况,上面那段代码将无法通过编译,因为它没有考虑到<code>CompassPoint</code>的全部成员。全面性的要求确保了枚举成员不会被意外遗漏。</p>
<p>当不需要匹配每个枚举成员的时候,你可以提供一个默认<code>default</code>分支来涵盖所有未明确被提出的任何成员:</p>
<pre><code>let somePlanet = Planet.Earth
<pre><code class="lang-swift">let somePlanet = Planet.Earth
switch somePlanet {
case .Earth:
println(&quot;Mostly harmless&quot;)
@ -662,7 +666,8 @@ default:
println(&quot;Not a safe place for humans&quot;)
}
// 输出 &quot;Mostly harmless”
</code></pre><p><a name="associated_values"></a></p>
</code></pre>
<p><a name="associated_values"></a></p>
<h2 id="-associated-values-">实例值Associated Values</h2>
<p>上一小节的例子演示了一个枚举的成员是如何被定义(分类)的。你可以为<code>Planet.Earth</code>设置一个常量或则变量,并且在之后查看这个值。然而,有时候会很有用如果能够把其他类型的实例值和成员值一起存储起来。这能让你随着成员值存储额外的自定义信息,并且当每次你在代码中利用该成员时允许这个信息产生变化。</p>
<p>你可以定义 Swift 的枚举存储任何类型的实例值如果需要的话每个成员的数据类型可以是各不相同的。枚举的这种特性跟其他语言中的可辨识联合discriminated unions标签联合tagged unions或者变体variants相似。</p>
@ -672,61 +677,70 @@ default:
<p><img width="169" height="169" alt="" src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/barcode_QR_2x.png"></p>
<p>对于库存跟踪系统来说,能够把 UPC-A 码作为三个整型值的元组,和把 QR 码作为一个任何长度的字符串存储起来是方便的。</p>
<p>在 Swift 中,用来定义两种商品条码的枚举是这样子的:</p>
<pre><code>enum Barcode {
case UPCA(Int, Int, Int)
case QRCode(String)
<pre><code class="lang-swift">enum Barcode {
case UPCA(Int, Int, Int)
case QRCode(String)
}
</code></pre><p>以上代码可以这么理解:</p>
</code></pre>
<p>以上代码可以这么理解:</p>
<p>“定义一个名为<code>Barcode</code>的枚举类型,它可以是<code>UPCA</code>的一个实例值(<code>Int</code><code>Int</code><code>Int</code>),或者<code>QRCode</code>的一个字符串类型(<code>String</code>)实例值。”</p>
<p>这个定义不提供任何<code>Int</code><code>String</code>的实际值,它只是定义了,当<code>Barcode</code>常量和变量等于<code>Barcode.UPCA</code><code>Barcode.QRCode</code>时,实例值的类型。</p>
<p>然后可以使用任何一种条码类型创建新的条码,如:</p>
<pre><code>var productBarcode = Barcode.UPCA(8, 85909_51226, 3)
</code></pre><p>以上例子创建了一个名为<code>productBarcode</code>的新变量,并且赋给它一个<code>Barcode.UPCA</code>的实例元组值<code>(8, 8590951226, 3)</code>。提供的“标识符”值在整数字中有一个下划线,使其便于阅读条形码。</p>
<pre><code class="lang-swift">var productBarcode = Barcode.UPCA(8, 85909_51226, 3)
</code></pre>
<p>以上例子创建了一个名为<code>productBarcode</code>的新变量,并且赋给它一个<code>Barcode.UPCA</code>的实例元组值<code>(8, 8590951226, 3)</code>。提供的“标识符”值在整数字中有一个下划线,使其便于阅读条形码。</p>
<p>同一个商品可以被分配给一个不同类型的条形码,如:</p>
<pre><code>productBarcode = .QRCode(&quot;ABCDEFGHIJKLMNOP&quot;)
</code></pre><p>这时,原始的<code>Barcode.UPCA</code>和其整数值被新的<code>Barcode.QRCode</code>和其字符串值所替代。条形码的常量和变量可以存储一个<code>.UPCA</code>或者一个<code>.QRCode</code>(连同它的实例值),但是在任何指定时间只能存储其中之一。</p>
<pre><code class="lang-swift">productBarcode = .QRCode(&quot;ABCDEFGHIJKLMNOP&quot;)
</code></pre>
<p>这时,原始的<code>Barcode.UPCA</code>和其整数值被新的<code>Barcode.QRCode</code>和其字符串值所替代。条形码的常量和变量可以存储一个<code>.UPCA</code>或者一个<code>.QRCode</code>(连同它的实例值),但是在任何指定时间只能存储其中之一。</p>
<p>像以前那样,不同的条形码类型可以使用一个 switch 语句来检查,然而这次实例值可以被提取作为 switch 语句的一部分。你可以在<code>switch</code>的 case 分支代码中提取每个实例值作为一个常量(用<code>let</code>前缀)或者作为一个变量(用<code>var</code>前缀)来使用:</p>
<pre><code>switch productBarcode {
<pre><code class="lang-swift">switch productBarcode {
case .UPCA(let numberSystem, let identifier, let check):
println(&quot;UPC-A with value of \(numberSystem), \(identifier), \(check).&quot;)
case .QRCode(let productCode):
println(&quot;QR code with value of \(productCode).&quot;)
}
// 输出 &quot;QR code with value of ABCDEFGHIJKLMNOP.”
</code></pre><p>如果一个枚举成员的所有实例值被提取为常量,或者它们全部被提取为变量,为了简洁,你可以只放置一个<code>var</code>或者<code>let</code>标注在成员名称前:</p>
<pre><code>switch productBarcode {
</code></pre>
<p>如果一个枚举成员的所有实例值被提取为常量,或者它们全部被提取为变量,为了简洁,你可以只放置一个<code>var</code>或者<code>let</code>标注在成员名称前:</p>
<pre><code class="lang-swift">switch productBarcode {
case let .UPCA(numberSystem, identifier, check):
println(&quot;UPC-A with value of \(numberSystem), \(identifier), \(check).&quot;)
case let .QRCode(productCode):
println(&quot;QR code with value of \(productCode).&quot;)
}
// 输出 &quot;QR code with value of ABCDEFGHIJKLMNOP.&quot;
</code></pre><p><a name="raw_values"></a></p>
</code></pre>
<p><a name="raw_values"></a></p>
<h2 id="-raw-values-">原始值Raw Values</h2>
<p>在实例值小节的条形码例子中演示了一个枚举的成员如何声明它们存储不同类型的实例值。作为实例值的替代,枚举成员可以被默认值(称为原始值)预先填充,其中这些原始值具有相同的类型。</p>
<p>这里是一个枚举成员存储原始 ASCII 值的例子:</p>
<pre><code>enum ASCIIControlCharacter: Character {
<pre><code class="lang-swift">enum ASCIIControlCharacter: Character {
case Tab = &quot;\t&quot;
case LineFeed = &quot;\n&quot;
case CarriageReturn = &quot;\r&quot;
}
</code></pre><p>在这里,称为<code>ASCIIControlCharacter</code>的枚举的原始值类型被定义为字符型<code>Character</code>,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见字符串和字符<code>Strings and Characters</code>部分。</p>
</code></pre>
<p>在这里,称为<code>ASCIIControlCharacter</code>的枚举的原始值类型被定义为字符型<code>Character</code>,并被设置了一些比较常见的 ASCII 控制字符。字符值的描述请详见字符串和字符<code>Strings and Characters</code>部分。</p>
<p>注意,原始值和实例值是不相同的。当你开始在你的代码中定义枚举的时候原始值是被预先填充的值,像上述三个 ASCII 码。对于一个特定的枚举成员,它的原始值始终是相同的。实例值是当你在创建一个基于枚举成员的新常量或变量时才会被设置,并且每次当你这么做得时候,它的值可以是不同的。</p>
<p>原始值可以是字符串,字符,或者任何整型值或浮点型值。每个原始值在它的枚举声明中必须是唯一的。当整型值被用于原始值,如果其他枚举成员没有值时,它们会自动递增。</p>
<p>下面的枚举是对之前<code>Planet</code>这个枚举的一个细化,利用原始整型值来表示每个 planet 在太阳系中的顺序:</p>
<pre><code>enum Planet: Int {
<pre><code class="lang-swift">enum Planet: Int {
case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}
</code></pre><p>自动递增意味着<code>Planet.Venus</code>的原始值是<code>2</code>,依次类推。</p>
</code></pre>
<p>自动递增意味着<code>Planet.Venus</code>的原始值是<code>2</code>,依次类推。</p>
<p>使用枚举成员的<code>toRaw</code>方法可以访问该枚举成员的原始值:</p>
<pre><code>let earthsOrder = Planet.Earth.toRaw()
<pre><code class="lang-swift">let earthsOrder = Planet.Earth.toRaw()
// earthsOrder is 3
</code></pre><p>使用枚举的<code>fromRaw</code>方法来试图找到具有特定原始值的枚举成员。这个例子通过原始值<code>7</code>识别<code>Uranus</code></p>
<pre><code>let possiblePlanet = Planet.fromRaw(7)
</code></pre>
<p>使用枚举的<code>fromRaw</code>方法来试图找到具有特定原始值的枚举成员。这个例子通过原始值<code>7</code>识别<code>Uranus</code></p>
<pre><code class="lang-swift">let possiblePlanet = Planet.fromRaw(7)
// possiblePlanet is of type Planet? and equals Planet.Uranus
</code></pre><p>然而,并非所有可能的<code>Int</code>值都可以找到一个匹配的行星。正因为如此,<code>fromRaw</code>方法可以返回一个<strong><em>可选</em></strong>的枚举成员。在上面的例子中,<code>possiblePlanet</code><code>Planet?</code>类型,或“可选的<code>Planet</code>”。</p>
</code></pre>
<p>然而,并非所有可能的<code>Int</code>值都可以找到一个匹配的行星。正因为如此,<code>fromRaw</code>方法可以返回一个<strong><em>可选</em></strong>的枚举成员。在上面的例子中,<code>possiblePlanet</code><code>Planet?</code>类型,或“可选的<code>Planet</code>”。</p>
<p>如果你试图寻找一个位置为9的行星通过<code>fromRaw</code>返回的可选<code>Planet</code>值将是<code>nil</code></p>
<pre><code>let positionToFind = 9
<pre><code class="lang-swift">let positionToFind = 9
if let somePlanet = Planet.fromRaw(positionToFind) {
switch somePlanet {
case .Earth:
@ -738,7 +752,8 @@ if let somePlanet = Planet.fromRaw(positionToFind) {
println(&quot;There isn&#39;t a planet at position \(positionToFind)&quot;)
}
// 输出 &quot;There isn&#39;t a planet at position 9
</code></pre><p>这个范例使用可选绑定optional binding通过原始值<code>9</code>试图访问一个行星。<code>if let somePlanet = Planet.fromRaw(9)</code>语句获得一个可选<code>Planet</code>,如果可选<code>Planet</code>可以被获得,把<code>somePlanet</code>设置成该可选<code>Planet</code>的内容。在这个范例中,无法检索到位置为<code>9</code>的行星,所以<code>else</code>分支被执行。</p>
</code></pre>
<p>这个范例使用可选绑定optional binding通过原始值<code>9</code>试图访问一个行星。<code>if let somePlanet = Planet.fromRaw(9)</code>语句获得一个可选<code>Planet</code>,如果可选<code>Planet</code>可以被获得,把<code>somePlanet</code>设置成该可选<code>Planet</code>的内容。在这个范例中,无法检索到位置为<code>9</code>的行星,所以<code>else</code>分支被执行。</p>
</section>

203
chapter2/09_Classes_and_Structures.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.9" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.9" 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_104">
<section class="normal" id="section-gitbook_59">
<blockquote>
<p>翻译JaySurplus</p>
<p>校对sg552</p>
<p>翻译JaySurplus<br>校对sg552 </p>
</blockquote>
<h1 id="-">类和结构体</h1>
<p>本页包含内容:</p>
@ -605,21 +604,43 @@
<p>类和结构体是人们构建代码所用的一种通用且灵活的构造体。为了在类和结构体中实现各种功能,我们必须要严格按照对于常量,变量以及函数所规定的语法规则来定义属性和添加方法。</p>
<p>与其他编程语言所不同的是Swift 并不要求你为自定义类和结构去创建独立的接口和实现文件。你所要做的是在一个单一文件中定义一个类或者结构体,系统将会自动生成面向其它代码的外部接口。</p>
<blockquote>
<p> 注意:</p>
<p>通常一个<code></code>的实例被称为<code>对象</code>。然而在Swift 中,类和结构体的关系要比在其他语言中更加的密切,本章中所讨论的大部分功能都可以用在类和结构体上。因此,我们会主要使用<code>实例</code>而不是<code>对象</code></p>
<p> 注意:<br>通常一个<code></code>的实例被称为<code>对象</code>。然而在Swift 中,类和结构体的关系要比在其他语言中更加的密切,本章中所讨论的大部分功能都可以用在类和结构体上。因此,我们会主要使用<code>实例</code>而不是<code>对象</code></p>
</blockquote>
<p><a name="comparing_classes_and_structures"></a></p>
<h3 id="-">类和结构体对比</h3>
<p>Swift 中类和结构体有很多共同点。共同处在于:</p>
<p>&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD</p>
<ul>
<li>定义属性用于储存值</li>
<li>定义方法用于提供功能</li>
<li>定义附属脚本用于访问值</li>
<li>定义构造器用于生成初始化值</li>
<li>通过扩展以增加默认实现的功能</li>
<li>符合协议以对某类提供标准功能</li>
<li><h1 id="-">符合协议以对某类提供标准功能</h1>
</li>
<li>定义属性用于储存值</li>
<li>定义方法用于提供功能</li>
<li>定义下标脚本用于访问值</li>
<li>定义构造器用于生成初始化值</li>
<li>通过扩展以增加默认实现的功能</li>
<li>符合协议以对某类提供标准功能<blockquote>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>a516af6a531a104ec88da0d236ecf389a5ec72af</p>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</li>
</ul>
<p>更多信息请参见 <a href="10_Properties.html">属性</a><a href="11_Methods.html">方法</a><a href="12_Subscripts.html">附属脚本</a><a href="14_Initialization.html">初始过程</a><a href="20_Extensions.html">扩展</a>,和<a href="21_Protocols.html">协议</a></p>
<p>更多信息请参见 <a href="10_Properties.html">属性</a><a href="11_Methods.html">方法</a><a href="12_Subscripts.html">下标脚本</a><a href="14_Initialization.html">初始过程</a><a href="20_Extensions.html">扩展</a>,和<a href="21_Protocols.html">协议</a></p>
<p>与结构体相比,类还有如下的附加功能:</p>
<ul>
<li>继承允许一个类继承另一个类的特征</li>
@ -629,23 +650,22 @@
</ul>
<p>更多信息请参见<a href="http://" target="_blank">继承</a><a href="http://" target="_blank">类型转换</a><a href="http://" target="_blank">初始化</a>,和<a href="http://" target="_blank">自动引用计数</a></p>
<blockquote>
<p>注意:</p>
<p>结构体总是通过被复制的方式在代码中传递,因此请不要使用引用计数。</p>
<p>注意:<br>结构体总是通过被复制的方式在代码中传递,因此请不要使用引用计数。</p>
</blockquote>
<h3 id="-">定义</h3>
<p>类和结构体有着类似的定义方式。我们通过关键字<code>class</code><code>struct</code>来分别表示类和结构体,并在一对大括号中定义它们的具体内容:</p>
<pre><code>class SomeClass {
<pre><code class="lang-swift">class SomeClass {
// class definition goes here
}
struct SomeStructure {
// structure definition goes here
}
</code></pre><blockquote>
<p> 注意:</p>
<p>在你每次定义一个新类或者结构体的时候,实际上你是有效地定义了一个新的 Swift 类型。因此请使用 <code>UpperCamelCase</code> 这种方式来命名(如 <code>SomeClass</code><code>SomeStructure</code>以便符合标准Swift 类型的大写命名风格(如<code>String</code><code>Int</code><code>Bool</code>)。相反的,请使用<code>lowerCamelCase</code>这种方式为属性和方法命名(如<code>framerate</code><code>incrementCount</code>),以便和类区分。</p>
</code></pre>
<blockquote>
<p> 注意:<br>在你每次定义一个新类或者结构体的时候,实际上你是有效地定义了一个新的 Swift 类型。因此请使用 <code>UpperCamelCase</code> 这种方式来命名(如 <code>SomeClass</code><code>SomeStructure</code>以便符合标准Swift 类型的大写命名风格(如<code>String</code><code>Int</code><code>Bool</code>)。相反的,请使用<code>lowerCamelCase</code>这种方式为属性和方法命名(如<code>framerate</code><code>incrementCount</code>),以便和类区分。</p>
</blockquote>
<p>以下是定义结构体和定义类的示例:</p>
<pre><code>struct Resolution {
<pre><code class="lang-swift">struct Resolution {
var width = 0
var heigth = 0
}
@ -655,56 +675,64 @@ class VideoMode {
var frameRate = 0.0
var name: String?
}
</code></pre><p>在上面的示例中我们定义了一个名为<code>Resolution</code>的结构体,用来描述一个显示器的像素分辨率。这个结构体包含了两个名为<code>width</code><code>height</code>的储存属性。储存属性是捆绑和储存在类或结构体中的常量或变量。当这两个属性被初始化为整数<code>0</code>的时候,它们会被推断为<code>Int</code>类型。</p>
</code></pre>
<p>在上面的示例中我们定义了一个名为<code>Resolution</code>的结构体,用来描述一个显示器的像素分辨率。这个结构体包含了两个名为<code>width</code><code>height</code>的储存属性。储存属性是捆绑和储存在类或结构体中的常量或变量。当这两个属性被初始化为整数<code>0</code>的时候,它们会被推断为<code>Int</code>类型。</p>
<p>在上面的示例中我们还定义了一个名为<code>VideoMode</code>的类,用来描述一个视频显示器的特定模式。这个类包含了四个储存属性变量。第一个是<code>分辨率</code>,它被初始化为一个新的<code>Resolution</code>结构体的实例,具有<code>Resolution</code>的属性类型。新<code>VideoMode</code>实例同时还会初始化其它三个属性,它们分别是,初始值为<code>false</code>(意为“non-interlaced video”)的<code>inteflaced</code>,回放帧率初始值为<code>0.0</code><code>frameRate</code>和值为可选<code>String</code><code>name</code><code>name</code>属性会被自动赋予一个默认值<code>nil</code>,意为“没有<code>name</code>值”,因它是一个可选类型。</p>
<h3 id="-">类和结构体实例</h3>
<p><code>Resolution</code>结构体和<code>VideoMode</code>类的定义仅描述了什么是<code>Resolution</code><code>VideoMode</code>。它们并没有描述一个特定的分辨率resolution或者视频模式video mode。为了描述一个特定的分辨率或者视频模式我们需要生成一个它们的实例。</p>
<p>生成结构体和类实例的语法非常相似:</p>
<pre><code>let someResolution = Resolution()
<pre><code class="lang-swift">let someResolution = Resolution()
let someVideoMode = VideoMode()
</code></pre><p>结构体和类都使用构造器语法来生成新的实例。构造器语法的最简单形式是在结构体或者类的类型名称后跟随一个空括弧,如<code>Resolution()</code><code>VideoMode()</code>。通过这种方式所创建的类或者结构体实例,其属均会被初始化为默认值。<a href="14_Initialization.html">构造过程</a>章节会对类和结构体的初始化进行更详细的讨论。</p>
</code></pre>
<p>结构体和类都使用构造器语法来生成新的实例。构造器语法的最简单形式是在结构体或者类的类型名称后跟随一个空括弧,如<code>Resolution()</code><code>VideoMode()</code>。通过这种方式所创建的类或者结构体实例,其属均会被初始化为默认值。<a href="14_Initialization.html">构造过程</a>章节会对类和结构体的初始化进行更详细的讨论。</p>
<h3 id="-">属性访问</h3>
<p>通过使用<em>点语法</em><em>dot syntax</em>,你可以访问实例中所含有的属性。其语法规则是,实例名后面紧跟属性名,两者通过点号(.)连接:</p>
<pre><code>println(&quot;The width of someResolution is \(someResolution.width)&quot;)
<pre><code class="lang-swift">println(&quot;The width of someResolution is \(someResolution.width)&quot;)
// 输出 &quot;The width of someResolution is 0&quot;
</code></pre><p>在上面的例子中,<code>someResolution.width</code>引用<code>someResolution</code><code>width</code>属性,返回<code>width</code>的初始值<code>0</code></p>
</code></pre>
<p>在上面的例子中,<code>someResolution.width</code>引用<code>someResolution</code><code>width</code>属性,返回<code>width</code>的初始值<code>0</code></p>
<p>你也可以访问子属性,如何<code>VideoMode</code><code>Resolution</code>属性的<code>width</code>属性:</p>
<pre><code>println(&quot;The width of someVideoMode is \(someVideoMode.resolution.width)&quot;)
<pre><code class="lang-swift">println(&quot;The width of someVideoMode is \(someVideoMode.resolution.width)&quot;)
// 输出 &quot;The width of someVideoMode is 0&quot;
</code></pre><p>你也可以使用点语法为属性变量赋值:</p>
<pre><code>someVideoMode.resolution.width = 12880
</code></pre>
<p>你也可以使用点语法为属性变量赋值:</p>
<pre><code class="lang-swift">someVideoMode.resolution.width = 12880
println(&quot;The width of someVideoMode is now \(someVideoMode.resolution.width)&quot;)
// 输出 &quot;The width of someVideoMode is now 1280&quot;
</code></pre><blockquote>
<p> 注意:</p>
<p>与 Objective-C 语言不同的是Swift 允许直接设置结构体属性的子属性。上面的最后一个例子,就是直接设置了<code>someVideoMode</code><code>resolution</code>属性的<code>width</code>这个子属性,以上操作并不需要从新设置<code>resolution</code>属性。</p>
</code></pre>
<blockquote>
<p> 注意:<br>与 Objective-C 语言不同的是Swift 允许直接设置结构体属性的子属性。上面的最后一个例子,就是直接设置了<code>someVideoMode</code><code>resolution</code>属性的<code>width</code>这个子属性,以上操作并不需要从新设置<code>resolution</code>属性。</p>
</blockquote>
<h3 id="-">结构体类型的成员逐一构造器</h3>
<p>//Memberwise Initializers for structure Types</p>
<h3 id="-memberwise-initializers-for-structure-types-">结构体类型的成员逐一构造器(Memberwise Initializers for structure Types)</h3>
<p>所有结构体都有一个自动生成的成员逐一构造器,用于初始化新结构体实例中成员的属性。新实例中各个属性的初始值可以通过属性的名称传递到成员逐一构造器之中:</p>
<pre><code>let vga = resolution(width:640, heigth: 480)
</code></pre><p>与结构体不同,类实例没有默认的成员逐一构造器。<a href="14_Initialization.html">构造过程</a>章节会对构造器进行更详细的讨论。</p>
<pre><code class="lang-swift">let vga = resolution(width:640, heigth: 480)
</code></pre>
<p>与结构体不同,类实例没有默认的成员逐一构造器。<a href="14_Initialization.html">构造过程</a>章节会对构造器进行更详细的讨论。</p>
<p><a name="structures_and_enumerations_are_value_types"></a></p>
<h2 id="-">结构体和枚举是值类型</h2>
<p>值类型被赋予给一个变量,常数或者本身被传递给一个函数的时候,实际上操作的是其的拷贝。</p>
<p>在之前的章节中,我们已经大量使用了值类型。实际上,在 Swift 中所有的基本类型整数Integer、浮点数floating-point、布尔值Booleans、字符串string)、数组array和字典dictionaries都是值类型并且都是以结构体的形式在后台所实现。</p>
<p>在 Swift 中,所有的结构体和枚举都是值类型。这意味着它们的实例,以及实例中所包含的任何值类型属性,在代码中传递的时候都会被复制。</p>
<p>请看下面这个示例,其使用了前一个示例中<code>Resolution</code>结构体:</p>
<pre><code>let hd = Resolution(width: 1920, height: 1080)
<pre><code class="lang-swift">let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
</code></pre><p>在以上示例中,声明了一个名为<code>hd</code>的常量其值为一个初始化为全高清视频分辨率1920 像素宽1080 像素高)的<code>Resolution</code>实例。</p>
</code></pre>
<p>在以上示例中,声明了一个名为<code>hd</code>的常量其值为一个初始化为全高清视频分辨率1920 像素宽1080 像素高)的<code>Resolution</code>实例。</p>
<p>然后示例中又声明了一个名为<code>cinema</code>的变量,其值为之前声明的<code>hd</code>。因为<code>Resolution</code>是一个结构体,所以<code>cinema</code>的值其实是<code>hd</code>的一个拷贝副本,而不是<code>hd</code>本身。尽管<code>hd</code><code>cinema</code>有着相同的宽width和高height属性但是在后台中它们是两个完全不同的实例。</p>
<p>下面为了符合数码影院放映的需求2048 像素宽1080 像素高),<code>cinema</code><code>width</code>属性需要作如下修改:</p>
<pre><code>cinema.width = 2048
</code></pre><p>这里,将会显示<code>cinema</code><code>width</code>属性确已改为了<code>2048</code></p>
<pre><code>println(&quot;cinema is now \(cinema.width) pixels wide&quot;)
<pre><code class="lang-swift">cinema.width = 2048
</code></pre>
<p>这里,将会显示<code>cinema</code><code>width</code>属性确已改为了<code>2048</code></p>
<pre><code class="lang-swift">println(&quot;cinema is now \(cinema.width) pixels wide&quot;)
// 输出 &quot;cinema is now 2048 pixels wide&quot;
</code></pre><p>然而,初始的<code>hd</code>实例中<code>width</code>属性还是<code>1920</code></p>
<pre><code>println(&quot;hd is still \(hd.width ) pixels wide&quot;)
</code></pre>
<p>然而,初始的<code>hd</code>实例中<code>width</code>属性还是<code>1920</code></p>
<pre><code class="lang-swift">println(&quot;hd is still \(hd.width ) pixels wide&quot;)
// 输出 &quot;hd is still 1920 pixels wide&quot;
</code></pre><p>在将<code>hd</code>赋予给<code>cinema</code>的时候,实际上是将<code>hd</code>中所储存的<code>values</code>进行拷贝,然后将拷贝的数据储存到新的<code>cinema</code>实例中。结果就是两个完全独立的实例碰巧包含有相同的数值。由于两者相互独立,因此将<code>cinema</code><code>width</code>修改为<code>2048</code>并不会影响<code>hd</code>中的宽width</p>
</code></pre>
<p>在将<code>hd</code>赋予给<code>cinema</code>的时候,实际上是将<code>hd</code>中所储存的<code>values</code>进行拷贝,然后将拷贝的数据储存到新的<code>cinema</code>实例中。结果就是两个完全独立的实例碰巧包含有相同的数值。由于两者相互独立,因此将<code>cinema</code><code>width</code>修改为<code>2048</code>并不会影响<code>hd</code>中的宽width</p>
<p>枚举也遵循相同的行为准则:</p>
<pre><code>enum CompassPoint {
<pre><code class="lang-swift">enum CompassPoint {
case North, South, East, West
}
var currentDirection = CompassPoint.West
@ -714,25 +742,29 @@ if rememberDirection == .West {
println(&quot;The remembered direction is still .West&quot;)
}
// 输出 &quot;The remembered direction is still .West&quot;
</code></pre><p>上例中<code>rememberedDirection</code>被赋予了<code>currentDirection</code>的值value实际上它被赋予的是值value的一个拷贝。赋值过程结束后再修改<code>currentDirection</code>的值并不影响<code>rememberedDirection</code>所储存的原始值value的拷贝。</p>
</code></pre>
<p>上例中<code>rememberedDirection</code>被赋予了<code>currentDirection</code>的值value实际上它被赋予的是值value的一个拷贝。赋值过程结束后再修改<code>currentDirection</code>的值并不影响<code>rememberedDirection</code>所储存的原始值value的拷贝。</p>
<p><a name="classes_are_reference_types"></a></p>
<h2 id="-">类是引用类型</h2>
<p>与值类型不同,引用类型在被赋予到一个变量,常量或者被传递到一个函数时,操作的并不是其拷贝。因此,引用的是已存在的实例本身而不是其拷贝。</p>
<p>请看下面这个示例,其使用了之前定义的<code>VideoMode</code>类:</p>
<pre><code>let tenEighty = VideoMode()
<pre><code class="lang-swift">let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = &quot;1080i&quot;
tenEighty.frameRate = 25.0
</code></pre><p>以上示例中,声明了一个名为<code>tenEighty</code>的常量,其引用了一个<code>VideoMode</code>类的新实例。在之前的示例中这个视频模式video mode被赋予了HD分辨率1920*1080的一个拷贝<code>hd</code>。同时设置为交错interlaced,命名为<code>“1080i”</code>。最后,其帧率是<code>25.0</code>帧每秒。</p>
</code></pre>
<p>以上示例中,声明了一个名为<code>tenEighty</code>的常量,其引用了一个<code>VideoMode</code>类的新实例。在之前的示例中这个视频模式video mode被赋予了HD分辨率1920*1080的一个拷贝<code>hd</code>。同时设置为交错interlaced,命名为<code>“1080i”</code>。最后,其帧率是<code>25.0</code>帧每秒。</p>
<p>然后,<code>tenEighty</code> 被赋予名为<code>alsoTenEighty</code>的新常量,同时对<code>alsoTenEighty</code>的帧率进行修改:</p>
<pre><code>let alsoTenEighty = tenEighty
<pre><code class="lang-swift">let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0
</code></pre><p>因为类是引用类型,所以<code>tenEight</code><code>alsoTenEight</code>实际上引用的是相同的<code>VideoMode</code>实例。换句话说,它们只是同一个实例的两种叫法。</p>
</code></pre>
<p>因为类是引用类型,所以<code>tenEight</code><code>alsoTenEight</code>实际上引用的是相同的<code>VideoMode</code>实例。换句话说,它们只是同一个实例的两种叫法。</p>
<p>下面,通过查看<code>tenEighty</code><code>frameRate</code>属性,我们会发现它正确的显示了基本<code>VideoMode</code>实例的新帧率,其值为<code>30.0</code></p>
<pre><code>println(&quot;The frameRate property of tenEighty is now \(tenEighty.frameRate)&quot;)
<pre><code class="lang-swift">println(&quot;The frameRate property of tenEighty is now \(tenEighty.frameRate)&quot;)
// 输出 &quot;The frameRate property of theEighty is now 30.0&quot;
</code></pre><p>需要注意的是<code>tenEighty</code><code>alsoTenEighty</code>被声明为<em>常量constants</em>而不是变量。然而你依然可以改变<code>tenEighty.frameRate</code><code>alsoTenEighty.frameRate</code>,因为这两个常量本身不会改变。它们并不<code>储存</code>这个<code>VideoMode</code>实例,在后台仅仅是对<code>VideoMode</code>实例的引用。所以,改变的是被引用的基础<code>VideoMode</code><code>frameRate</code>参数,而不改变常量的值。</p>
</code></pre>
<p>需要注意的是<code>tenEighty</code><code>alsoTenEighty</code>被声明为<em>常量constants</em>而不是变量。然而你依然可以改变<code>tenEighty.frameRate</code><code>alsoTenEighty.frameRate</code>,因为这两个常量本身不会改变。它们并不<code>储存</code>这个<code>VideoMode</code>实例,在后台仅仅是对<code>VideoMode</code>实例的引用。所以,改变的是被引用的基础<code>VideoMode</code><code>frameRate</code>参数,而不改变常量的值。</p>
<h3 id="-">恒等运算符</h3>
<p>因为类是引用类型,有可能有多个常量和变量在后台同时引用某一个类实例。(对于结构体和枚举来说,这并不成立。因为它们作值类型,在被赋予到常量,变量或者传递到函数时,总是会被拷贝。)</p>
<p>如果能够判定两个常量或者变量是否引用同一个类实例将会很有帮助。为了达到这个目的Swift 内建了两个恒等运算符:</p>
@ -741,11 +773,12 @@ alsoTenEighty.frameRate = 30.0
<li>不等价于 !== </li>
</ul>
<p>以下是运用这两个运算符检测两个常量或者变量是否引用同一个实例:</p>
<pre><code>if tenEighty === alsoTenTighty {
<pre><code class="lang-swift">if tenEighty === alsoTenTighty {
println(&quot;tenTighty and alsoTenEighty refer to the same Resolution instance.&quot;)
}
//输出 &quot;tenEighty and alsoTenEighty refer to the same Resolution instance.&quot;
</code></pre><p>请注意“等价于”(用三个等号表示,=== 与“等于”(用两个等号表示,==)的不同:</p>
</code></pre>
<p>请注意“等价于”(用三个等号表示,=== 与“等于”(用两个等号表示,==)的不同:</p>
<ul>
<li>“等价于”表示两个类类型class type的常量或者变量引用同一个类实例。</li>
<li>“等于”表示两个实例的值“相等”或“相同”,判定时要遵照类设计者定义定义的评判标准,因此相比于“相等”,这是一种更加合适的叫法。</li>
@ -776,48 +809,52 @@ alsoTenEighty.frameRate = 30.0
<p>Swift 中<code>数组Array</code><code>字典Dictionary</code>类型均以结构体的形式实现。然而当数组被赋予一个常量或变量,或被传递给一个函数或方法时,其拷贝行为与字典和其它结构体有些许不同。</p>
<p>以下对<code>数组</code><code>结构体</code>的行为描述与对<code>NSArray</code><code>NSDictionary</code>的行为描述在本质上不同,后者是以类的形式实现,前者是以结构体的形式实现。<code>NSArray</code><code>NSDictionary</code>实例总是以对已有实例引用,而不是拷贝的方式被赋值和传递。</p>
<blockquote>
<p>注意:</p>
<p>以下是对于数组,字典,字符串和其它值的<code>拷贝</code>的描述。
<p>注意:<br>以下是对于数组,字典,字符串和其它值的<code>拷贝</code>的描述。
在你的代码中,拷贝好像是确实是在有拷贝行为的地方产生过。然而,在 Swift 的后台中,只有确有必要,<code>实际actual</code>拷贝才会被执行。Swift 管理所有的值拷贝以确保性能最优化的性能,所以你也没有必要去避免赋值以保证最优性能。(实际赋值由系统管理优化)</p>
</blockquote>
<h3 id="-">字典类型的赋值和拷贝行为</h3>
<p>无论何时将一个<code>字典</code>实例赋给一个常量或变量,或者传递给一个函数或方法,这个字典会即会在赋值或调用发生时被拷贝。在章节<a href="#structures_and_enumerations_are_value_types">结构体和枚举是值类型</a>中将会对此过程进行详细介绍。</p>
<p>如果<code>字典</code>实例中所储存的键keys和/或值values是值类型结构体或枚举当赋值或调用发生时它们都会被拷贝。相反如果键keys和/或值values是引用类型被拷贝的将会是引用而不是被它们引用的类实例或函数。<code>字典</code>的键和值的拷贝行为与结构体所储存的属性的拷贝行为相同。</p>
<p>下面的示例定义了一个名为<code>ages</code>的字典,其中储存了四个人的名字和年龄。<code>ages</code>字典被赋予了一个名为<code>copiedAges</code>的新变量,同时<code>ages</code>在赋值的过程中被拷贝。赋值结束后,<code>ages</code><code>copiedAges</code>成为两个相互独立的字典。</p>
<pre><code>var ages = [&quot;Peter&quot;: 23, &quot;Wei&quot;: 35, &quot;Anish&quot;: 65, &quot;Katya&quot;: 19]
<pre><code class="lang-swift">var ages = [&quot;Peter&quot;: 23, &quot;Wei&quot;: 35, &quot;Anish&quot;: 65, &quot;Katya&quot;: 19]
var copiedAges = ages
</code></pre><p>这个字典的键keys<code>字符串String</code>类型values<code>Int</code>类型。这两种类型在Swift 中都是值类型value types所以当字典被拷贝时两者都会被拷贝。</p>
</code></pre>
<p>这个字典的键keys<code>字符串String</code>类型values<code>Int</code>类型。这两种类型在Swift 中都是值类型value types所以当字典被拷贝时两者都会被拷贝。</p>
<p>我们可以通过改变一个字典中的年龄值age value检查另一个字典中所对应的值来证明<code>ages</code>字典确实是被拷贝了。如果在<code>copiedAges</code>字典中将<code>Peter</code>的值设为<code>24</code>,那么<code>ages</code>字典仍然会返回修改前的值<code>23</code></p>
<pre><code>copiedAges[&quot;Peter&quot;] = 24
<pre><code class="lang-swift">copiedAges[&quot;Peter&quot;] = 24
println(ages[&quot;Peter&quot;])
// 输出 &quot;23&quot;
</code></pre><h3 id="-">数组的赋值和拷贝行为</h3>
</code></pre>
<h3 id="-">数组的赋值和拷贝行为</h3>
<p>在Swift 中,<code>数组Arrays</code>类型的赋值和拷贝行为要比<code>字典Dictionary</code>类型的复杂的多。当操作数组内容时,<code>数组Array</code>能提供接近C语言的的性能并且拷贝行为只有在必要时才会发生。</p>
<p>如果你将一个<code>数组Array</code>实例赋给一个变量或常量,或者将其作为参数传递给函数或方法调用,在事件发生时数组的内容<code></code>会被拷贝。相反,数组公用相同的元素序列。当你在一个数组内修改某一元素,修改结果也会在另一数组显示。</p>
<p>对数组来说,拷贝行为仅仅当操作有可能修改数组<code>长度</code>时才会发生。这种行为包括了附加appending,插入inserting,删除removing或者使用范围下标ranged subscript去替换这一范围内的元素。只有当数组拷贝确要发生时数组内容的行为规则与字典中键值的相同参见章节[集合collection类型的赋值与复制行为](#assignment_and_copy_behavior_for_collection_types。</p>
<p>下面的示例将一个<code>整数Int</code>数组赋给了一个名为<code>a</code>的变量,继而又被赋给了变量<code>b</code><code>c</code></p>
<pre><code>var a = [1, 2, 3]
<pre><code class="lang-swift">var a = [1, 2, 3]
var b = a
var c = a
</code></pre><p>我们可以在<code>a</code>,<code>b</code>,<code>c</code>上使用下标语法以得到数组的第一个元素:</p>
<pre><code>println(a[0])
</code></pre>
<p>我们可以在<code>a</code>,<code>b</code>,<code>c</code>上使用下标语法以得到数组的第一个元素:</p>
<pre><code class="lang-swift">println(a[0])
// 1
println(b[0])
// 1
println(c[0])
// 1
</code></pre><p>如果通过下标语法修改数组中某一元素的值,那么<code>a</code>,<code>b</code>,<code>c</code>中的相应值都会发生改变。请注意当你用下标语法修改某一值时,并没有拷贝行为伴随发生,因为下表语法修改值时没有改变数组长度的可能:</p>
<pre><code>a[0] = 42
</code></pre>
<p>如果通过下标语法修改数组中某一元素的值,那么<code>a</code>,<code>b</code>,<code>c</code>中的相应值都会发生改变。请注意当你用下标语法修改某一值时,并没有拷贝行为伴随发生,因为下表语法修改值时没有改变数组长度的可能:</p>
<pre><code class="lang-swift">a[0] = 42
println(a[0])
// 42
println(b[0])
// 42
println(c[0])
// 42
</code></pre><p>然而,当你给<code>a</code>附加新元素时,数组的长度<code></code>改变。
</code></pre>
<p>然而,当你给<code>a</code>附加新元素时,数组的长度<code></code>改变。
当附加元素这一事件发生时Swift 会创建这个数组的一个拷贝。从此以后,<code>a</code>将会是原数组的一个独立拷贝。</p>
<p>拷贝发生后,如果再修改<code>a</code>中元素值的话,<code>a</code>将会返回与<code>b</code><code>c</code>不同的结果,因为后两者引用的是原来的数组:</p>
<pre><code>a.append(4)
<pre><code class="lang-swift">a.append(4)
a[0] = 777
println(a[0])
// 777
@ -825,48 +862,54 @@ println(b[0])
// 42
println(c[0])
// 42
</code></pre><h3 id="-">确保数组的唯一性</h3>
</code></pre>
<h3 id="-">确保数组的唯一性</h3>
<p>在操作一个数组,或将其传递给函数以及方法调用之前是很有必要先确定这个数组是有一个唯一拷贝的。通过在数组变量上调用<code>unshare</code>方法来确定数组引用的唯一性。(当数组赋给常量时,不能调用<code>unshare</code>方法)</p>
<p>如果一个数组被多个变量引用,在其中的一个变量上调用<code>unshare</code>方法,则会拷贝此数组,此时这个变量将会有属于它自己的独立数组拷贝。当数组仅被一个变量引用时,则不会有拷贝发生。</p>
<p>在上一个示例的最后,<code>b</code><code>c</code>都引用了同一个数组。此时在<code>b</code>上调用<code>unshare</code>方法则会将<code>b</code>变成一个唯一个拷贝:</p>
<pre><code>b.unshare()
</code></pre><p><code>unshare</code>方法调用后再修改<code>b</code>中第一个元素的值,这三个数组(<code>a</code>,<code>b</code>,<code>c</code>)会返回不同的三个值:</p>
<pre><code>b[0] = -105
<pre><code class="lang-swift">b.unshare()
</code></pre>
<p><code>unshare</code>方法调用后再修改<code>b</code>中第一个元素的值,这三个数组(<code>a</code>,<code>b</code>,<code>c</code>)会返回不同的三个值:</p>
<pre><code class="lang-swift">b[0] = -105
println(a[0])
// 77
println(b[0])
// -105
println(c[0])
// 42
</code></pre><h3 id="-">判定两个数组是否共用相同元素</h3>
</code></pre>
<h3 id="-">判定两个数组是否共用相同元素</h3>
<p>我们通过使用恒等运算符identity operators === 和 !==)来判定两个数组或子数组共用相同的储存空间或元素。</p>
<p>下面这个示例使用了“等同identical to” 运算符(=== 来判定<code>b</code><code>c</code>是否共用相同的数组元素:</p>
<pre><code>if b === c {
<pre><code class="lang-swift">if b === c {
println(&quot;b and c still share the same array elements.&quot;)
} else {
println(&quot;b and c now refer to two independent sets of array elements.&quot;)
}
// 输出 &quot;b and c now refer totwo independent sets of array elements.&quot;
</code></pre><p>此外,我们还可以使用恒等运算符来判定两个子数组是否共用相同的元素。下面这个示例中,比较了<code>b</code>的两个相等的子数组,并且确定了这两个子数组都引用相同的元素:</p>
<pre><code>if b[0...1] === b[0...1] {
</code></pre>
<pre><code class="lang-swift">// 输出 &quot;b and c now refer totwo independent sets of array elements.&quot;
</code></pre>
<p>此外,我们还可以使用恒等运算符来判定两个子数组是否共用相同的元素。下面这个示例中,比较了<code>b</code>的两个相等的子数组,并且确定了这两个子数组都引用相同的元素:</p>
<pre><code class="lang-swift">if b[0...1] === b[0...1] {
println(&quot;These two subarrays share the same elements.&quot;)
} else {
println(&quot;These two subarrays do not share the same elements.&quot;)
}
// 输出 &quot;These two subarrays share the same elements.&quot;
</code></pre><h3 id="-">强制复制数组</h3>
</code></pre>
<h3 id="-">强制复制数组</h3>
<p>我们通过调用数组的<code>copy</code>方法进行强制显性复制。这个方法对数组进行了浅拷贝shallow copy,并且返回一个包含此拷贝的新数组。</p>
<p>下面这个示例中定义了一个<code>names</code>数组,其包含了七个人名。还定义了一个<code>copiedNames</code>变量,用以储存在<code>names</code>上调用<code>copy</code>方法所返回的结果:</p>
<pre><code>var names = [&quot;Mohsen&quot;, &quot;Hilary&quot;, &quot;Justyn&quot;, &quot;Amy&quot;, &quot;Rich&quot;, &quot;Graham&quot;, &quot;Vic&quot;]
<pre><code class="lang-swift">var names = [&quot;Mohsen&quot;, &quot;Hilary&quot;, &quot;Justyn&quot;, &quot;Amy&quot;, &quot;Rich&quot;, &quot;Graham&quot;, &quot;Vic&quot;]
var copiedNames = names.copy()
</code></pre><p>我们可以通过修改一个数组中某元素,并且检查另一个数组中对应元素的方法来判定<code>names</code>数组确已被复制。如果你将<code>copiedNames</code>中第一个元素从&quot;<code>Mohsen</code>&quot;修改为&quot;<code>Mo</code>&quot;,则<code>names</code>数组返回的仍是拷贝发生前的&quot;<code>Mohsen</code>&quot;</p>
<pre><code>copiedName[0] = &quot;Mo&quot;
</code></pre>
<p>我们可以通过修改一个数组中某元素,并且检查另一个数组中对应元素的方法来判定<code>names</code>数组确已被复制。如果你将<code>copiedNames</code>中第一个元素从&quot;<code>Mohsen</code>&quot;修改为&quot;<code>Mo</code>&quot;,则<code>names</code>数组返回的仍是拷贝发生前的&quot;<code>Mohsen</code>&quot;</p>
<pre><code class="lang-swift">copiedName[0] = &quot;Mo&quot;
println(name[0])
// 输出 &quot;Mohsen&quot;
</code></pre><blockquote>
<p>注意:</p>
<p>如果你仅需要确保你对数组的引用是唯一引用,请调用<code>unshare</code>方法,而不是<code>copy</code>方法。<code>unshare</code>方法仅会在确有必要时才会创建数组拷贝。<code>copy</code>方法会在任何时候都创建一个新的拷贝,即使引用已经是唯一引用。</p>
</code></pre>
<blockquote>
<p>注意:<br>如果你仅需要确保你对数组的引用是唯一引用,请调用<code>unshare</code>方法,而不是<code>copy</code>方法。<code>unshare</code>方法仅会在确有必要时才会创建数组拷贝。<code>copy</code>方法会在任何时候都创建一个新的拷贝,即使引用已经是唯一引用。</p>
</blockquote>

139
chapter2/10_Properties.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.10" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.10" 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 " data-progress="2.11" style="left: 18.42105263157895%;"></a>
<a href="../chapter2/12_Subscripts.html" title="附属脚本" class="chapter " data-progress="2.12" style="left: 21.05263157894737%;"></a>
<a href="../chapter2/12_Subscripts.html" title="下标脚本" class="chapter " data-progress="2.12" style="left: 21.05263157894737%;"></a>
<a href="../chapter2/13_Inheritance.html" title="继承" class="chapter " data-progress="2.13" style="left: 23.68421052631579%;"></a>
@ -587,11 +587,10 @@
<div class="page-inner">
<section class="normal" id="section-gitbook_105">
<section class="normal" id="section-gitbook_57">
<blockquote>
<p>翻译shinyzhu</p>
<p>校对pp-prog</p>
<p>翻译shinyzhu<br>校对pp-prog </p>
</blockquote>
<h1 id="-properties-">属性 (Properties)</h1>
<hr>
@ -610,8 +609,8 @@
<h2 id="-">存储属性</h2>
<p>简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量,存储属性可以是<em>变量存储属性</em>(用关键字<code>var</code>定义),也可以是<em>常量存储属性</em>(用关键字<code>let</code>定义)。</p>
<p>可以在定义存储属性的时候指定默认值,请参考<a href="../chapter2/14_Initialization.html">构造过程</a>一章的<a href="../chapter2/14_Initialization.html#default_property_values">默认属性值</a>一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考<a href="../chapter2/14_Initialization.html">构造过程</a>一章的<a href="../chapter2/14_Initialization.html#modifying_constant_properties_during_initialization">在初始化阶段修改常量存储属性</a>一节。</p>
<p>下面的例子定义了一个名为<code>FixedLengthRange</code>的结构体,描述了一个在创建后无法修改值域宽度的区间:</p>
<pre><code>struct FixedLengthRange {
<p>下面的例子定义了一个名为<code>FixedLengthRange</code>的结构体,描述了一个在创建后无法修改值域宽度的区间:</p>
<pre><code class="lang-swift">struct FixedLengthRange {
var firstValue: Int
let length: Int
}
@ -619,27 +618,28 @@ var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
// 该区间表示整数012
rangeOfThreeItems.firstValue = 6
// 该区间现在表示整数678
</code></pre><p><code>FixedLengthRange</code>的实例包含一个名为<code>firstValue</code>的变量存储属性和一个名为<code>length</code>的常量存储属性。在上面的例子中,<code>length</code>在创建实例的时候被赋值,因为它是一个常量存储属性,所以之后无法修改它的值。</p>
</code></pre>
<p><code>FixedLengthRange</code>的实例包含一个名为<code>firstValue</code>的变量存储属性和一个名为<code>length</code>的常量存储属性。在上面的例子中,<code>length</code>在创建实例的时候被赋值,因为它是一个常量存储属性,所以之后无法修改它的值。</p>
<p><a name="stored_properties_of_constant_structure_instances"></a></p>
<h3 id="-">常量和存储属性</h3>
<p>如果创建了一个结构体的实例并赋值给一个常量,则无法修改实例的任何属性,即使定义了变量存储属性:</p>
<pre><code>let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
<pre><code class="lang-swift">let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// 该区间表示整数0123
rangeOfFourItems.firstValue = 6
// 尽管 firstValue 是个变量属性,这里还是会报错
</code></pre><p>因为<code>rangeOfFourItems</code>声明成了常量(用<code>let</code>关键字),即使<code>firstValue</code>是一个变量属性,也无法再修改它了。</p>
</code></pre>
<p>因为<code>rangeOfFourItems</code>声明成了常量(用<code>let</code>关键字),即使<code>firstValue</code>是一个变量属性,也无法再修改它了。</p>
<p>这种行为是由于结构体struct属于<em>值类型</em>。当值类型的实例被声明为常量的时候,它的所有属性也就成了常量。</p>
<p>属于<em>引用类型</em>的类class则不一样把一个引用类型的实例赋给一个常量后仍然可以修改实例的变量属性。</p>
<p><a name="lazy_stored_properties"></a></p>
<h3 id="-">延迟存储属性</h3>
<p>延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用<code>@lazy</code>来标示一个延迟存储属性。</p>
<blockquote>
<p>注意:</p>
<p>必须将延迟存储属性声明成变量(使用<code>var</code>关键字),因为属性的值在实例构造完成之前可能无法得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。</p>
<p>注意:<br>必须将延迟存储属性声明成变量(使用<code>var</code>关键字),因为属性的值在实例构造完成之前可能无法得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。 </p>
</blockquote>
<p>延迟属性很有用,当属性的值依赖于在实例的构造过程结束前无法知道具体值的外部因素时,或者当属性的值需要复杂或大量计算时,可以只在需要的时候来计算它。</p>
<p>下面的例子使用了延迟存储属性来避免复杂类的不必要的初始化。例子中定义了<code>DataImporter</code><code>DataManager</code>两个类,下面是部分代码:</p>
<pre><code>class DataImporter {
<pre><code class="lang-swift">class DataImporter {
/*
DataImporter 是一个将外部文件中的数据导入的类。
这个类的初始化会消耗不少时间。
@ -658,14 +658,16 @@ let manager = DataManager()
manager.data += &quot;Some data&quot;
manager.data += &quot;Some more data&quot;
// DataImporter 实例的 importer 属性还没有被创建
</code></pre><p><code>DataManager</code>类包含一个名为<code>data</code>的存储属性,初始值是一个空的字符串(<code>String</code>)数组。虽然没有写出全部代码,<code>DataManager</code>类的目的是管理和提供对这个字符串数组的访问。</p>
</code></pre>
<p><code>DataManager</code>类包含一个名为<code>data</code>的存储属性,初始值是一个空的字符串(<code>String</code>)数组。虽然没有写出全部代码,<code>DataManager</code>类的目的是管理和提供对这个字符串数组的访问。</p>
<p><code>DataManager</code>的一个功能是从文件导入数据,该功能由<code>DataImporter</code>类提供,<code>DataImporter</code>需要消耗不少时间完成初始化:因为它的实例在初始化时可能要打开文件,还要读取文件内容到内存。</p>
<p><code>DataManager</code>也可能不从文件中导入数据。所以当<code>DataManager</code>的实例被创建时,没必要创建一个<code>DataImporter</code>的实例,更明智的是当用到<code>DataImporter</code>的时候才去创建它。</p>
<p>由于使用了<code>@lazy</code><code>importer</code>属性只有在第一次被访问的时候才被创建。比如访问它的属性<code>fileName</code>时:</p>
<pre><code>println(manager.importer.fileName)
<pre><code class="lang-swift">println(manager.importer.fileName)
// DataImporter 实例的 importer 属性现在被创建了
// 输出 &quot;data.txt”
</code></pre><p><a name="stored_properties_and_instance_variables"></a></p>
</code></pre>
<p><a name="stored_properties_and_instance_variables"></a></p>
<h3 id="-">存储属性和实例变量</h3>
<p>如果您有过 Objective-C 经验,应该知道有两种方式在类实例存储值和引用。对于属性来说,也可以使用实例变量作为属性值的后端存储。</p>
<p>Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的后端存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。
@ -673,7 +675,7 @@ manager.data += &quot;Some more data&quot;
<p><a name="computed_properties"></a></p>
<h2 id="-">计算属性</h2>
<p>除存储属性外,类、结构体和枚举可以定义<em>计算属性</em>,计算属性不直接存储值,而是提供一个 getter 来获取值,一个可选的 setter 来间接设置其他属性或变量的值。</p>
<pre><code>struct Point {
<pre><code class="lang-swift">struct Point {
var x = 0.0, y = 0.0
}
struct Size {
@ -700,7 +702,8 @@ let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
println(&quot;square.origin is now at (\(square.origin.x), \(square.origin.y))&quot;)
// 输出 &quot;square.origin is now at (10.0, 10.0)”
</code></pre><p>这个例子定义了 3 个几何形状的结构体:</p>
</code></pre>
<p>这个例子定义了 3 个几何形状的结构体:</p>
<ul>
<li><code>Point</code>封装了一个<code>(x, y)</code>的坐标</li>
<li><code>Size</code>封装了一个<code>width</code><code>height</code></li>
@ -714,7 +717,7 @@ println(&quot;square.origin is now at (\(square.origin.x), \(square.origin.y))&q
<p><a name="shorthand_setter_declaration"></a></p>
<h3 id="-setter-">便捷 setter 声明</h3>
<p>如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称<code>newValue</code>。下面是使用了便捷 setter 声明的<code>Rect</code>结构体代码:</p>
<pre><code>struct AlternativeRect {
<pre><code class="lang-swift">struct AlternativeRect {
var origin = Point()
var size = Size()
var center: Point {
@ -729,15 +732,32 @@ println(&quot;square.origin is now at (\(square.origin.x), \(square.origin.y))&q
}
}
}
</code></pre><p><a name="readonly_computed_properties"></a></p>
</code></pre>
<p><a name="readonly_computed_properties"></a></p>
<h3 id="-">只读计算属性</h3>
<p>只有 getter 没有 setter 的计算属性就是<em>只读计算属性</em>。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。</p>
<p>&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD</p>
<blockquote>
<p>注意: </p>
<h1 id="-var-let-">必须使用<code>var</code>关键字定义计算属性,包括只读计算属性,因为他们的值不是固定的。<code>let</code>关键字只用来声明常量属性,表示初始化后再也无法修改的值。 </h1>
<p>注意:</p>
<p>必须使用<code>var</code>关键字定义计算属性,包括只读计算属性,因为们的值不是固定的。<code>let</code>关键字只用来声明常量属性,表示初始化后再也无法修改的值。</p>
<p>必须使用<code>var</code>关键字定义计算属性,包括只读计算属性,因为们的值不是固定的。<code>let</code>关键字只用来声明常量属性,表示初始化后再也无法修改的值。</p>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>a516af6a531a104ec88da0d236ecf389a5ec72af</p>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
<p>只读计算属性的声明可以去掉<code>get</code>关键字和花括号:</p>
<pre><code>struct Cuboid {
<pre><code class="lang-swift">struct Cuboid {
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double {
return width * height * depth
@ -746,14 +766,14 @@ println(&quot;square.origin is now at (\(square.origin.x), \(square.origin.y))&q
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
println(&quot;the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)&quot;)
// 输出 &quot;the volume of fourByFiveByTwo is 40.0&quot;
</code></pre><p>这个例子定义了一个名为<code>Cuboid</code>的结构体,表示三维空间的立方体,包含<code>width</code><code>height</code><code>depth</code>属性,还有一个名为<code>volume</code>的只读计算属性用来返回立方体的体积。设置<code>volume</code>的值毫无意义,因为通过<code>width</code><code>height</code><code>depth</code>就能算出<code>volume</code>。然而,<code>Cuboid</code>提供一个只读计算属性来让外部用户直接获取体积是很有用的。</p>
</code></pre>
<p>这个例子定义了一个名为<code>Cuboid</code>的结构体,表示三维空间的立方体,包含<code>width</code><code>height</code><code>depth</code>属性,还有一个名为<code>volume</code>的只读计算属性用来返回立方体的体积。设置<code>volume</code>的值毫无意义,因为通过<code>width</code><code>height</code><code>depth</code>就能算出<code>volume</code>。然而,<code>Cuboid</code>提供一个只读计算属性来让外部用户直接获取体积是很有用的。</p>
<p><a name="property_observers"></a></p>
<h2 id="-">属性监视器</h2>
<p><em>属性监视器</em>监控和响应属性值的变化,每次属性被设置值的时候都会调用属性监视器,甚至新的值和现在的值相同的时候也不例外。</p>
<p>可以为除了延迟存储属性之外的其他存储属性添加属性监视器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性监视器。属性重载请参考<a href="chapter/13_Inheritance.html">继承</a>一章的<a href="chapter/13_Inheritance.html#overriding">重载</a></p>
<blockquote>
<p>注意:</p>
<p>不需要为无法重载的计算属性添加属性监视器,因为可以通过 setter 直接监控和响应值的变化。</p>
<p>注意:<br>不需要为无法重载的计算属性添加属性监视器,因为可以通过 setter 直接监控和响应值的变化。 </p>
</blockquote>
<p>可以为属性添加如下的一个或全部监视器:</p>
<ul>
@ -762,12 +782,28 @@ println(&quot;the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)&quot;)
</ul>
<p><code>willSet</code>监视器会将新的属性值作为固定参数传入,在<code>willSet</code>的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称<code>newValue</code>表示。</p>
<p>类似地,<code>didSet</code>监视器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名<code>oldValue</code></p>
<p>&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD</p>
<blockquote>
<p>注意: </p>
<h1 id="-willset-didset-"><code>willSet</code><code>didSet</code>监视器在属性初始化过程中不会被调用,他们只会当属性的值在初始化之外的地方被设置时被调用。 </h1>
<p>注意:</p>
<p><code>willSet</code><code>didSet</code>监视器在属性初始化过程中不会被调用,们只会当属性的值在初始化之外的地方被设置时被调用。</p>
<p><code>willSet</code><code>didSet</code>监视器在属性初始化过程中不会被调用,们只会当属性的值在初始化之外的地方被设置时被调用。</p>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>a516af6a531a104ec88da0d236ecf389a5ec72af</p>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
<p>这里是一个<code>willSet</code><code>didSet</code>的实际例子,其中定义了一个名为<code>StepCounter</code>的类,用来统计当人步行时的总步数,可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。</p>
<pre><code>class StepCounter {
<pre><code class="lang-swift">class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
println(&quot;About to set totalSteps to \(newTotalSteps)&quot;)
@ -789,13 +825,13 @@ stepCounter.totalSteps = 360
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps
</code></pre><p><code>StepCounter</code>类定义了一个<code>Int</code>类型的属性<code>totalSteps</code>,它是一个存储属性,包含<code>willSet</code><code>didSet</code>监视器。</p>
</code></pre>
<p><code>StepCounter</code>类定义了一个<code>Int</code>类型的属性<code>totalSteps</code>,它是一个存储属性,包含<code>willSet</code><code>didSet</code>监视器。</p>
<p><code>totalSteps</code>设置新值的时候,它的<code>willSet</code><code>didSet</code>监视器都会被调用,甚至当新的值和现在的值完全相同也会调用。</p>
<p>例子中的<code>willSet</code>监视器将表示新值的参数自定义为<code>newTotalSteps</code>,这个监视器只是简单的将新的值输出。</p>
<p><code>didSet</code>监视器在<code>totalSteps</code>的值改变后被调用,它把新的值和旧的值进行对比,如果总的步数增加了,就输出一个消息表示增加了多少步。<code>didSet</code>没有提供自定义名称,所以默认值<code>oldValue</code>表示旧值的参数名。</p>
<blockquote>
<p>注意:</p>
<p>如果在<code>didSet</code>监视器里为属性赋值,这个值会替换监视器之前设置的值。</p>
<p>注意:<br>如果在<code>didSet</code>监视器里为属性赋值,这个值会替换监视器之前设置的值。 </p>
</blockquote>
<p><a name="global_and_local_variables"></a></p>
<h2 id="-">全局变量和局部变量</h2>
@ -803,9 +839,7 @@ stepCounter.totalSteps = 896
<p>前面章节提到的全局或局部变量都属于存储型变量,跟存储属性类似,它提供特定类型的存储空间,并允许读取和写入。</p>
<p>另外,在全局或局部范围都可以定义计算型变量和为存储型变量定义监视器,计算型变量跟计算属性一样,返回一个计算的值而不是存储值,声明格式也完全一样。</p>
<blockquote>
<p>注意:</p>
<p>全局的常量或变量都是延迟计算的,跟<a href="#lazy_stored_properties">延迟存储属性</a>相似,不同的地方在于,全局的常量或变量不需要标记<code>@lazy</code>特性。</p>
<p>局部范围的常量或变量不会延迟计算。</p>
<p>注意:<br>全局的常量或变量都是延迟计算的,跟<a href="#lazy_stored_properties">延迟存储属性</a>相似,不同的地方在于,全局的常量或变量不需要标记<code>@lazy</code>特性。<br>局部范围的常量或变量不会延迟计算。 </p>
</blockquote>
<p><a name="type_properties"></a></p>
<h2 id="-">类型属性</h2>
@ -815,14 +849,13 @@ stepCounter.totalSteps = 896
<p>对于值类型指结构体和枚举可以定义存储型和计算型类型属性对于类class则只能定义计算型类型属性。</p>
<p>值类型的存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算属性一样定义成变量属性。</p>
<blockquote>
<p>注意:</p>
<p>跟实例的存储属性不同,必须给存储型类型属性指定默认值,因为类型本身无法在初始化过程中使用构造器给类型属性赋值。</p>
<p>注意:<br>跟实例的存储属性不同,必须给存储型类型属性指定默认值,因为类型本身无法在初始化过程中使用构造器给类型属性赋值。 </p>
</blockquote>
<p><a name="type_property_syntax"></a></p>
<h3 id="-">类型属性语法</h3>
<p>在 C 或 Objective-C 中,静态常量和静态变量的定义是通过特定类型加上<code>global</code>关键字。在 Swift 编程语言中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。</p>
<p>使用关键字<code>static</code>来定义值类型的类型属性,关键字<code>class</code>来为类class定义类型属性。下面的例子演示了存储型和计算型类型属性的语法</p>
<pre><code>struct SomeStructure {
<pre><code class="lang-swift">struct SomeStructure {
static var storedTypeProperty = &quot;Some value.&quot;
static var computedTypeProperty: Int {
// 这里返回一个 Int 值
@ -839,14 +872,14 @@ class SomeClass {
// 这里返回一个 Int 值
}
}
</code></pre><blockquote>
<p>注意:</p>
<p>例子中的计算型类型属性是只读的,但也可以定义可读可写的计算型类型属性,跟实例计算属性的语法类似。</p>
</code></pre>
<blockquote>
<p>注意:<br>例子中的计算型类型属性是只读的,但也可以定义可读可写的计算型类型属性,跟实例计算属性的语法类似。 </p>
</blockquote>
<p><a name="querying_and_setting_type_properties"></a></p>
<h3 id="-">获取和设置类型属性的值</h3>
<p>跟实例的属性一样,类型属性的访问也是通过点运算符来进行,但是,类型属性是通过类型本身来获取和设置,而不是通过实例。比如:</p>
<pre><code>println(SomeClass.computedTypeProperty)
<pre><code class="lang-swift">println(SomeClass.computedTypeProperty)
// 输出 &quot;42&quot;
println(SomeStructure.storedTypeProperty)
@ -854,11 +887,12 @@ println(SomeStructure.storedTypeProperty)
SomeStructure.storedTypeProperty = &quot;Another value.&quot;
println(SomeStructure.storedTypeProperty)
// 输出 &quot;Another value.”
</code></pre><p>下面的例子定义了一个结构体,使用两个存储型类型属性来表示多个声道的声音电平值,每个声道有一个 0 到 10 之间的整数表示声音电平值。</p>
</code></pre>
<p>下面的例子定义了一个结构体,使用两个存储型类型属性来表示多个声道的声音电平值,每个声道有一个 0 到 10 之间的整数表示声音电平值。</p>
<p>后面的图表展示了如何联合使用两个声道来表示一个立体声的声音电平值。当声道的电平值是 0没有一个灯会亮当声道的电平值是 10所有灯点亮。本图中左声道的电平是 9右声道的电平是 7。</p>
<p><img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/staticPropertiesVUMeter_2x.png" alt="Static Properties VUMeter" width="243" height="357" /></p>
<p>上面所描述的声道模型使用<code>AudioChannel</code>结构体来表示:</p>
<pre><code>struct AudioChannel {
<pre><code class="lang-swift">struct AudioChannel {
static let thresholdLevel = 10
static var maxInputLevelForAllChannels = 0
var currentLevel: Int = 0 {
@ -874,7 +908,8 @@ println(SomeStructure.storedTypeProperty)
}
}
}
</code></pre><p>结构<code>AudioChannel</code>定义了 2 个存储型类型属性来实现上述功能。第一个是<code>thresholdLevel</code>,表示声音电平的最大上限阈值,它是一个取值为 10 的常量,对所有实例都可见,如果声音电平高于 10则取最大上限值 10见后面描述</p>
</code></pre>
<p>结构<code>AudioChannel</code>定义了 2 个存储型类型属性来实现上述功能。第一个是<code>thresholdLevel</code>,表示声音电平的最大上限阈值,它是一个取值为 10 的常量,对所有实例都可见,如果声音电平高于 10则取最大上限值 10见后面描述</p>
<p>第二个类型属性是变量存储型属性<code>maxInputLevelForAllChannels</code>,它用来表示所有<code>AudioChannel</code>实例的电平值的最大值,初始值是 0。</p>
<p><code>AudioChannel</code>也定义了一个名为<code>currentLevel</code>的实例存储属性,表示当前声道现在的电平值,取值为 0 到 10。</p>
<p>属性<code>currentLevel</code>包含<code>didSet</code>属性监视器来检查每次新设置后的属性值,有如下两个检查:</p>
@ -883,25 +918,27 @@ println(SomeStructure.storedTypeProperty)
<li>如果修正后的<code>currentLevel</code>值大于任何之前任意<code>AudioChannel</code>实例中的值,属性监视器将新值保存在静态属性<code>maxInputLevelForAllChannels</code>中。</li>
</ul>
<blockquote>
<p>注意:</p>
<p>在第一个检查过程中,<code>didSet</code>属性监视器将<code>currentLevel</code>设置成了不同的值,但这时不会再次调用属性监视器。</p>
<p>注意:<br>在第一个检查过程中,<code>didSet</code>属性监视器将<code>currentLevel</code>设置成了不同的值,但这时不会再次调用属性监视器。 </p>
</blockquote>
<p>可以使用结构体<code>AudioChannel</code>来创建表示立体声系统的两个声道<code>leftChannel</code><code>rightChannel</code></p>
<pre><code>var leftChannel = AudioChannel()
<pre><code class="lang-swift">var leftChannel = AudioChannel()
var rightChannel = AudioChannel()
</code></pre><p>如果将左声道的电平设置成 7类型属性<code>maxInputLevelForAllChannels</code>也会更新成 7</p>
<pre><code>leftChannel.currentLevel = 7
</code></pre>
<p>如果将左声道的电平设置成 7类型属性<code>maxInputLevelForAllChannels</code>也会更新成 7</p>
<pre><code class="lang-swift">leftChannel.currentLevel = 7
println(leftChannel.currentLevel)
// 输出 &quot;7&quot;
println(AudioChannel.maxInputLevelForAllChannels)
// 输出 &quot;7&quot;
</code></pre><p>如果试图将右声道的电平设置成 11则会将右声道的<code>currentLevel</code>修正到最大值 10同时<code>maxInputLevelForAllChannels</code>的值也会更新到 10</p>
<pre><code>rightChannel.currentLevel = 11
</code></pre>
<p>如果试图将右声道的电平设置成 11则会将右声道的<code>currentLevel</code>修正到最大值 10同时<code>maxInputLevelForAllChannels</code>的值也会更新到 10</p>
<pre><code class="lang-swift">rightChannel.currentLevel = 11
println(rightChannel.currentLevel)
// 输出 &quot;10&quot;
println(AudioChannel.maxInputLevelForAllChannels)
// 输出 &quot;10&quot;
</code></pre>
</section>

103
chapter2/11_Methods.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.11" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.11" 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 " data-progress="2.12" style="left: 21.05263157894737%;"></a>
<a href="../chapter2/12_Subscripts.html" title="下标脚本" class="chapter " data-progress="2.12" style="left: 21.05263157894737%;"></a>
<a href="../chapter2/13_Inheritance.html" title="继承" class="chapter " data-progress="2.13" style="left: 23.68421052631579%;"></a>
@ -587,11 +587,10 @@
<div class="page-inner">
<section class="normal" id="section-gitbook_107">
<section class="normal" id="section-gitbook_51">
<blockquote>
<p>翻译pp-prog</p>
<p>校对zqp</p>
<p>翻译pp-prog<br>校对zqp </p>
</blockquote>
<h1 id="-methods-">方法Methods</h1>
<hr>
@ -607,7 +606,7 @@
<p><strong>实例方法</strong>是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见<a href="../charpter2/06_Functions.html">函数</a></p>
<p>实例方法要写在它所属的类型的前后大括号之间。实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的某个特定实例调用。实例方法不能脱离于现存的实例而被调用。</p>
<p>下面的例子,定义一个很简单的类<code>Counter</code><code>Counter</code>能被用来对一个动作发生的次数进行计数:</p>
<pre><code>class Counter {
<pre><code class="lang-swift">class Counter {
var count = 0
func increment() {
count++
@ -619,7 +618,8 @@
count = 0
}
}
</code></pre><p><code>Counter</code>类定义了三个实例方法:</p>
</code></pre>
<p><code>Counter</code>类定义了三个实例方法:</p>
<ul>
<li><code>increment</code>让计数器按一递增;</li>
<li><code>incrementBy(amount: Int)</code>让计数器按一个指定的整数值递增;</li>
@ -627,7 +627,7 @@
</ul>
<p><code>Counter</code>这个类还声明了一个可变属性<code>count</code>,用它来保持对当前计数器值的追踪。</p>
<p>和调用属性一样用点语法dot syntax调用实例方法</p>
<pre><code> let counter = Counter()
<pre><code class="lang-swift"> let counter = Counter()
// 初始计数值是0
counter.increment()
// 计数值现在是1
@ -635,28 +635,32 @@
// 计数值现在是6
counter.reset()
// 计数值现在是0
</code></pre><p><a name="local_and_external_parameter"></a></p>
</code></pre>
<p><a name="local_and_external_parameter"></a></p>
<h3 id="-local-and-external-parameter-names-for-methods-">方法的局部参数名称和外部参数名称(Local and External Parameter Names for Methods)</h3>
<p>函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见<a href="06_Functions.html">函数的外部参数名</a>。方法参数也一样(因为方法就是函数,只是这个函数与某个类型相关联了)。但是,方法和函数的局部名称和外部名称的默认行为是不一样的。</p>
<p>Swift 中的方法和 Objective-C 中的方法极其相似。像在 Objective-C 中一样Swift 中方法的名称通常用一个介词指向方法的第一个参数,比如:<code>with</code><code>for</code><code>by</code>等等。前面的<code>Counter</code>类的例子中<code>incrementBy</code>方法就是这样的。介词的使用让方法在被调用时能像一个句子一样被解读。和函数参数不同对于方法的参数Swift 使用不同的默认处理方式,这可以让方法命名规范更容易写。</p>
<p>具体来说Swift 默认仅给方法的第一个参数名称一个局部参数名称;默认同时给第二个和后续的参数名称局部参数名称和外部参数名称。这个约定与典型的命名和调用约定相适应,与你在写 Objective-C 的方法时很相似。这个约定还让表达式方法在调用时不需要再限定参数名称。</p>
<p>看看下面这个<code>Counter</code>的另一个版本(它定义了一个更复杂的<code>incrementBy</code>方法):</p>
<pre><code>class Counter {
<pre><code class="lang-swift">class Counter {
var count: Int = 0
func incrementBy(amount: Int, numberOfTimes: Int) {
count += amount * numberOfTimes
}
}
</code></pre><p><code>incrementBy</code>方法有两个参数: <code>amount</code><code>numberOfTimes</code>。默认情况下Swift 只把<code>amount</code>当作一个局部名称,但是把<code>numberOfTimes</code>即看作局部名称又看作外部名称。下面调用这个方法:</p>
<pre><code>let counter = Counter()
</code></pre>
<p><code>incrementBy</code>方法有两个参数: <code>amount</code><code>numberOfTimes</code>。默认情况下Swift 只把<code>amount</code>当作一个局部名称,但是把<code>numberOfTimes</code>即看作局部名称又看作外部名称。下面调用这个方法:</p>
<pre><code class="lang-swift">let counter = Counter()
counter.incrementBy(5, numberOfTimes: 3)
// counter value is now 15
</code></pre><p>你不必为第一个参数值再定义一个外部变量名:因为从函数名<code>incrementBy</code>已经能很清楚地看出它的作用。但是第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时明确它的作用。</p>
</code></pre>
<p>你不必为第一个参数值再定义一个外部变量名:因为从函数名<code>incrementBy</code>已经能很清楚地看出它的作用。但是第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时明确它的作用。</p>
<p>这种默认的行为能够有效的处理方法method,类似于在参数<code>numberOfTimes</code>前写一个井号(<code>#</code></p>
<pre><code>func incrementBy(amount: Int, #numberOfTimes: Int) {
count += amount * numberOfTimes
<pre><code class="lang-swift">func incrementBy(amount: Int, #numberOfTimes: Int) {
count += amount * numberOfTimes
}
</code></pre><p>这种默认行为使上面代码意味着:在 Swift 中定义方法使用了与 Objective-C 同样的语法风格,并且方法将以自然表达式的方式被调用。</p>
</code></pre>
<p>这种默认行为使上面代码意味着:在 Swift 中定义方法使用了与 Objective-C 同样的语法风格,并且方法将以自然表达式的方式被调用。</p>
<p><a name="modifying_external_parameter"></a></p>
<h3 id="-modifying-external-parameter-name-behavior-for-methods-">修改方法的外部参数名称(Modifying External Parameter Name Behavior for Methods)</h3>
<p>有时为方法的第一个参数提供一个外部参数名称是非常有用的,尽管这不是默认的行为。你可以自己添加一个显式的外部名称或者用一个井号(<code>#</code>)作为第一个参数的前缀来把这个局部名称当作外部名称使用。</p>
@ -665,13 +669,14 @@ counter.incrementBy(5, numberOfTimes: 3)
<h2 id="-self-the-self-property-"><code>self</code>属性(The self Property)</h2>
<p>类型的每一个实例都有一个隐含属性叫做<code>self</code><code>self</code>完全等同于该实例本身。你可以在一个实例的实例方法中使用这个隐含的<code>self</code>属性来引用当前实例。</p>
<p>上面例子中的<code>increment</code>方法还可以这样写:</p>
<pre><code>func increment() {
<pre><code class="lang-swift">func increment() {
self.count++
}
</code></pre><p>实际上,你不必在你的代码里面经常写<code>self</code>。不论何时,只要在一个方法中使用一个已知的属性或者方法名称,如果你没有明确的写<code>self</code>Swift 假定你是指当前实例的属性或者方法。这种假定在上面的<code>Counter</code>中已经示范了:<code>Counter</code>中的三个实例方法中都使用的是<code>count</code>(而不是<code>self.count</code>)。</p>
</code></pre>
<p>实际上,你不必在你的代码里面经常写<code>self</code>。不论何时,只要在一个方法中使用一个已知的属性或者方法名称,如果你没有明确的写<code>self</code>Swift 假定你是指当前实例的属性或者方法。这种假定在上面的<code>Counter</code>中已经示范了:<code>Counter</code>中的三个实例方法中都使用的是<code>count</code>(而不是<code>self.count</code>)。</p>
<p>使用这条规则的主要场景是实例方法的某个参数名称与实例的某个属性名称相同的时候。在这种情况下,参数名称享有优先权,并且在引用属性时必须使用一种更严格的方式。这时你可以使用<code>self</code>属性来区分参数名称和属性名称。</p>
<p>下面的例子中,<code>self</code>消除方法参数<code>x</code>和实例属性<code>x</code>之间的歧义:</p>
<pre><code>struct Point {
<pre><code class="lang-swift">struct Point {
var x = 0.0, y = 0.0
func isToTheRightOfX(x: Double) -&gt; Bool {
return self.x &gt; x
@ -682,13 +687,14 @@ if somePoint.isToTheRightOfX(1.0) {
println(&quot;This point is to the right of the line where x == 1.0&quot;)
}
// 输出 &quot;This point is to the right of the line where x == 1.0&quot;这个点在x等于1.0这条线的右边)
</code></pre><p>如果不使用<code>self</code>前缀Swift 就认为两次使用的<code>x</code>都指的是名称为<code>x</code>的函数参数。</p>
</code></pre>
<p>如果不使用<code>self</code>前缀Swift 就认为两次使用的<code>x</code>都指的是名称为<code>x</code>的函数参数。</p>
<p><a name="modifying_value_types"></a></p>
<h3 id="-modifying-value-types-from-within-instance-methods-">在实例方法中修改值类型(Modifying Value Types from Within Instance Methods)</h3>
<p>结构体和枚举是<strong>值类型</strong>。一般情况下,值类型的属性不能在它的实例方法中被修改。</p>
<p>但是,如果你确实需要在某个具体的方法中修改结构体或者枚举的属性,你可以选择<code>变异(mutating)</code>这个方法,然后方法就可以从方法内部改变它的属性;并且它做的任何改变在方法结束时还会保留在原始结构中。方法还可以给它隐含的<code>self</code>属性赋值一个全新的实例,这个新实例在方法结束后将替换原来的实例。</p>
<p>要使用<code>变异</code>方法, 将关键字<code>mutating</code> 放到方法的<code>func</code>关键字之前就可以了:</p>
<pre><code>struct Point {
<pre><code class="lang-swift">struct Point {
var x = 0.0, y = 0.0
mutating func moveByX(deltaX: Double, y deltaY: Double) {
x += deltaX
@ -699,23 +705,26 @@ var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveByX(2.0, y: 3.0)
println(&quot;The point is now at (\(somePoint.x), \(somePoint.y))&quot;)
// 输出 &quot;The point is now at (3.0, 4.0)&quot;
</code></pre><p>上面的<code>Point</code>结构体定义了一个变异方法mutating method<code>moveByX</code><code>moveByX</code>用来移动点。<code>moveByX</code>方法在被调用时修改了这个点,而不是返回一个新的点。方法定义时加上<code>mutating</code>关键字,这才让方法可以修改值类型的属性。</p>
</code></pre>
<p>上面的<code>Point</code>结构体定义了一个变异方法mutating method<code>moveByX</code><code>moveByX</code>用来移动点。<code>moveByX</code>方法在被调用时修改了这个点,而不是返回一个新的点。方法定义时加上<code>mutating</code>关键字,这才让方法可以修改值类型的属性。</p>
<p>注意:不能在结构体类型常量上调用变异方法,因为常量的属性不能被改变,即使想改变的是常量的变量属性也不行,详情参见<a href="&quot;10_Properties.html&quot;">存储属性和实例变量</a></p>
<pre><code>let fixedPoint = Point(x: 3.0, y: 3.0)
<pre><code class="lang-swift">let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveByX(2.0, y: 3.0)
// this will report an error
</code></pre><p><a name="mutating_method_self"></a></p>
</code></pre>
<p><a name="mutating_method_self"></a></p>
<h3 id="-self-assigning-to-self-within-a-mutating-method-">在变异方法中给self赋值(Assigning to self Within a Mutating Method)</h3>
<p>变异方法能够赋给隐含属性<code>self</code>一个全新的实例。上面<code>Point</code>的例子可以用下面的方式改写:</p>
<pre><code>struct Point {
<pre><code class="lang-swift">struct Point {
var x = 0.0, y = 0.0
mutating func moveByX(deltaX: Double, y deltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY)
}
}
</code></pre><p>新版的变异方法<code>moveByX</code>创建了一个新的结构(它的 x 和 y 的值都被设定为目标值)。调用这个版本的方法和调用上个版本的最终结果是一样的。</p>
</code></pre>
<p>新版的变异方法<code>moveByX</code>创建了一个新的结构(它的 x 和 y 的值都被设定为目标值)。调用这个版本的方法和调用上个版本的最终结果是一样的。</p>
<p>枚举的变异方法可以把<code>self</code>设置为相同的枚举类型中不同的成员:</p>
<pre><code>enum TriStateSwitch {
<pre><code class="lang-swift">enum TriStateSwitch {
case Off, Low, High
mutating func next() {
switch self {
@ -733,26 +742,27 @@ ovenLight.next()
// ovenLight 现在等于 .High
ovenLight.next()
// ovenLight 现在等于 .Off
</code></pre><p>上面的例子中定义了一个三态开关的枚举。每次调用<code>next</code>方法时,开关在不同的电源状态(<code>Off</code><code>Low</code><code>High</code>)之前循环切换。</p>
</code></pre>
<p>上面的例子中定义了一个三态开关的枚举。每次调用<code>next</code>方法时,开关在不同的电源状态(<code>Off</code><code>Low</code><code>High</code>)之前循环切换。</p>
<p><a name="type_methods"></a></p>
<h2 id="-type-methods-">类型方法(Type Methods)</h2>
<p>实例方法是被类型的某个实例调用的方法。你也可以定义类型本身调用的方法,这种方法就叫做<strong>类型方法</strong>。声明类的类型方法,在方法的<code>func</code>关键字之前加上关键字<code>class</code>;声明结构体和枚举的类型方法,在方法的<code>func</code>关键字之前加上关键字<code>static</code></p>
<blockquote>
<p>注意:</p>
<p>在 Objective-C 里面,你只能为 Objective-C 的类定义类型方法type-level methods。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法:每一个类型方法都被它所支持的类型显式包含。</p>
<p>注意:<br>在 Objective-C 里面,你只能为 Objective-C 的类定义类型方法type-level methods。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法:每一个类型方法都被它所支持的类型显式包含。 </p>
</blockquote>
<p>类型方法和实例方法一样用点语法调用。但是,你是在类型层面上调用这个方法,而不是在实例层面上调用。下面是如何在<code>SomeClass</code>类上调用类型方法的例子:</p>
<pre><code>class SomeClass {
<pre><code class="lang-swift">class SomeClass {
class func someTypeMethod() {
// type method implementation goes here
}
}
SomeClass.someTypeMethod()
</code></pre><p>在类型方法的方法体body<code>self</code>指向这个类型本身,而不是类型的某个实例。对于结构体和枚举来说,这意味着你可以用<code>self</code>来消除静态属性和静态方法参数之间的歧义(类似于我们在前面处理实例属性和实例方法参数时做的那样)。</p>
</code></pre>
<p>在类型方法的方法体body<code>self</code>指向这个类型本身,而不是类型的某个实例。对于结构体和枚举来说,这意味着你可以用<code>self</code>来消除静态属性和静态方法参数之间的歧义(类似于我们在前面处理实例属性和实例方法参数时做的那样)。</p>
<p>一般来说,任何未限定的方法和属性名称,将会来自于本类中另外的类型级别的方法和属性。一个类型方法可以调用本类中另一个类型方法的名称,而无需在方法名称前面加上类型名称的前缀。同样,结构体和枚举的类型方法也能够直接通过静态属性的名称访问静态属性,而不需要类型名称前缀。</p>
<p>下面的例子定义了一个名为<code>LevelTracker</code>结构体。它监测玩家的游戏发展情况(游戏的不同层次或阶段)。这是一个单人游戏,但也可以存储多个玩家在同一设备上的游戏信息。</p>
<p>游戏初始时,所有的游戏等级(除了等级 1都被锁定。每次有玩家完成一个等级这个等级就对这个设备上的所有玩家解锁。<code>LevelTracker</code>结构体用静态属性和方法监测游戏的哪个等级已经被解锁。它还监测每个玩家的当前等级。</p>
<pre><code>struct LevelTracker {
<pre><code class="lang-swift">struct LevelTracker {
static var highestUnlockedLevel = 1
static func unlockLevel(level: Int) {
if level &gt; highestUnlockedLevel { highestUnlockedLevel = level }
@ -770,12 +780,13 @@ SomeClass.someTypeMethod()
}
}
}
</code></pre><p><code>LevelTracker</code>监测玩家的已解锁的最高等级。这个值被存储在静态属性<code>highestUnlockedLevel</code>中。</p>
</code></pre>
<p><code>LevelTracker</code>监测玩家的已解锁的最高等级。这个值被存储在静态属性<code>highestUnlockedLevel</code>中。</p>
<p><code>LevelTracker</code>还定义了两个类型方法与<code>highestUnlockedLevel</code>配合工作。第一个类型方法是<code>unlockLevel</code>:一旦新等级被解锁,它会更新<code>highestUnlockedLevel</code>的值。第二个类型方法是<code>levelIsUnlocked</code>:如果某个给定的等级已经被解锁,它将返回<code>true</code>。(注意:尽管我们没有使用类似<code>LevelTracker.highestUnlockedLevel</code>的写法,这个类型方法还是能够访问静态属性<code>highestUnlockedLevel</code></p>
<p>除了静态属性和类型方法,<code>LevelTracker</code>还监测每个玩家的进度。它用实例属性<code>currentLevel</code>来监测玩家当前的等级。</p>
<p>为了便于管理<code>currentLevel</code>属性,<code>LevelTracker</code>定义了实例方法<code>advanceToLevel</code>。这个方法会在更新<code>currentLevel</code>之前检查所请求的新等级是否已经解锁。<code>advanceToLevel</code>方法返回布尔值以指示是否能够设置<code>currentLevel</code></p>
<p>下面,<code>Player</code>类使用<code>LevelTracker</code>来监测和更新每个玩家的发展进度:</p>
<pre><code>class Player {
<pre><code class="lang-swift">class Player {
var tracker = LevelTracker()
let playerName: String
func completedLevel(level: Int) {
@ -786,22 +797,24 @@ SomeClass.someTypeMethod()
playerName = name
}
}
</code></pre><p><code>Player</code>类创建一个新的<code>LevelTracker</code>实例来监测这个用户的发展进度。他提供了<code>completedLevel</code>方法:一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了<code>advanceToLevel</code>返回的布尔值,因为之前调用<code>LevelTracker.unlockLevel</code>时就知道了这个等级已经被解锁了)。</p>
</code></pre>
<p><code>Player</code>类创建一个新的<code>LevelTracker</code>实例来监测这个用户的发展进度。它提供了<code>completedLevel</code>方法:一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了<code>advanceToLevel</code>返回的布尔值,因为之前调用<code>LevelTracker.unlockLevel</code>时就知道了这个等级已经被解锁了)。</p>
<p>你还可以为一个新的玩家创建一个<code>Player</code>的实例,然后看这个玩家完成等级一时发生了什么:</p>
<pre><code>var player = Player(name: &quot;Argyrios&quot;)
<pre><code class="lang-swift">var player = Player(name: &quot;Argyrios&quot;)
player.completedLevel(1)
println(&quot;highest unlocked level is now \(LevelTracker.highestUnlockedLevel)&quot;)
// 输出 &quot;highest unlocked level is now 2&quot;最高等级现在是2
</code></pre><p>如果你创建了第二个玩家,并尝试让开始一个没有被任何玩家解锁的等级,那么这次设置玩家当前等级的尝试将会失败:</p>
<pre><code>player = Player(name: &quot;Beto&quot;)
// 输出 &quot;highest unlocked level is now 2&quot;最高等级现在是2
</code></pre>
<p>如果你创建了第二个玩家,并尝试让开始一个没有被任何玩家解锁的等级,那么这次设置玩家当前等级的尝试将会失败:</p>
<pre><code class="lang-swift">player = Player(name: &quot;Beto&quot;)
if player.tracker.advanceToLevel(6) {
println(&quot;player is now on level 6&quot;)
println(&quot;player is now on level 6&quot;)
} else {
println(&quot;level 6 has not yet been unlocked&quot;)
println(&quot;level 6 has not yet been unlocked&quot;)
}
// 输出 &quot;level 6 has not yet been unlocked&quot;等级6还没被解锁
</code></pre>
</section>
@ -813,7 +826,7 @@ println(&quot;level 6 has not yet been unlocked&quot;)
<a href="../chapter2/10_Properties.html" class="navigation navigation-prev " aria-label="Previous page: 属性"><i class="fa fa-angle-left"></i></a>
<a href="../chapter2/12_Subscripts.html" class="navigation navigation-next " aria-label="Next page: 附属脚本"><i class="fa fa-angle-right"></i></a>
<a href="../chapter2/12_Subscripts.html" class="navigation navigation-next " aria-label="Next page: 下标脚本"><i class="fa fa-angle-right"></i></a>
</div>
</div>

169
chapter2/12_Subscripts.html Executable file → Normal file
View File

@ -21,7 +21,7 @@
<link rel="prev" href="../chapter2/11_Methods.html" />
<meta property="og:title" content="附属脚本 | The Swift Programming Language 中文版">
<meta property="og:title" content="下标脚本 | The Swift Programming Language 中文版">
<meta property="og:site_name" content="The Swift Programming Language 中文版">
<meta property="og:type" content="book">
<meta property="og:locale" content="en_US">
@ -46,7 +46,7 @@
<div class="book" data-level="2.12" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.12" 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 " data-progress="2.13" style="left: 23.68421052631579%;"></a>
@ -587,82 +587,132 @@
<div class="page-inner">
<section class="normal" id="section-gitbook_109">
<section class="normal" id="section-gitbook_53">
<blockquote>
<p>翻译siemenliu</p>
<p>校对zq54zquan</p>
<p>翻译siemenliu<br>校对zq54zquan </p>
</blockquote>
<h1 id="-subscripts-">附属脚本Subscripts</h1>
<h1 id="-subscripts-">下标脚本Subscripts</h1>
<hr>
<p>本页包含内容:</p>
<ul>
<li><a href="#subscript_syntax">附属脚本语法</a></li>
<li><a href="#subscript_usage">附属脚本用法</a></li>
<li><a href="#subscript_options">附属脚本选项</a></li>
<li><a href="#subscript_syntax">下标脚本语法</a></li>
<li><a href="#subscript_usage">下标脚本用法</a></li>
<li><a href="#subscript_options">下标脚本选项</a></li>
</ul>
<p><em>附属脚本</em> 可以定义在类Class、结构体structure和枚举enumeration这些目标中可以认为是访问对象、集合或序列的快捷方式不需要再调用实例的特定的赋值和访问方法。举例来说附属脚本访问一个数组(Array)实例中的元素可以这样写 <code>someArray[index]</code> ,访问字典(Dictionary)实例中的元素可以这样写 <code>someDictionary[key]</code></p>
<p>对于同一个目标可以定义多个附属脚本,通过索引值类型的不同来进行重载,而且索引值的个数可以是多个。</p>
<p><em>下标脚本</em> 可以定义在类Class、结构体structure和枚举enumeration这些目标中可以认为是访问对象、集合或序列的快捷方式不需要再调用实例的特定的赋值和访问方法。举例来说下标脚本访问一个数组(Array)实例中的元素可以这样写 <code>someArray[index]</code> ,访问字典(Dictionary)实例中的元素可以这样写 <code>someDictionary[key]</code></p>
<p>对于同一个目标可以定义多个下标脚本,通过索引值类型的不同来进行重载,而且索引值的个数可以是多个。</p>
<p>&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD</p>
<h1 id="-">&gt; 译者:这里附属脚本重载在本小节中原文并没有任何演示 </h1>
<blockquote>
<p>译者:这里附属脚本重载在本小节中原文并没有任何演示</p>
<p>译者:这里下标脚本重载在本小节中原文并没有任何演示</p>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>a516af6a531a104ec88da0d236ecf389a5ec72af</p>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
<p><a name="subscript_syntax"></a></p>
<h2 id="-">附属脚本语法</h2>
<p>附属脚本允许你通过在实例后面的方括号中传入一个或者多个的索引值来对实例进行访问和赋值。语法类似于实例方法和计算型属性的混合。与定义实例方法类似,定义附属脚本使用<code>subscript</code>关键字,显式声明入参(一个或多个)和返回类型。与实例方法不同的是附属脚本可以设定为读写或只读。这种方式又有点像计算型属性的getter和setter</p>
<pre><code>subscript(index: Int) -&gt; Int {
<h2 id="-">下标脚本语法</h2>
<p>下标脚本允许你通过在实例后面的方括号中传入一个或者多个的索引值来对实例进行访问和赋值。语法类似于实例方法和计算型属性的混合。与定义实例方法类似,定义下标脚本使用<code>subscript</code>关键字,显式声明入参(一个或多个)和返回类型。与实例方法不同的是下标脚本可以设定为读写或只读。这种方式又有点像计算型属性的getter和setter</p>
<pre><code class="lang-swift">subscript(index: Int) -&gt; Int {
get {
// 返回与入参匹配的Int类型的值
// 返回与入参匹配的Int类型的值
}
set(newValue) {
// 执行赋值操作
// 执行赋值操作
}
}
</code></pre><p><code>newValue</code>的类型必须和附属脚本定义的返回类型相同。与计算型属性相同的是set的入参声明<code>newValue</code>就算不写在set代码块中依然可以使用默认的<code>newValue</code>这个变量来访问新赋的值。</p>
</code></pre>
<p><code>newValue</code>的类型必须和下标脚本定义的返回类型相同。与计算型属性相同的是set的入参声明<code>newValue</code>就算不写在set代码块中依然可以使用默认的<code>newValue</code>这个变量来访问新赋的值。</p>
<p>与只读计算型属性一样,可以直接将原本应该写在<code>get</code>代码块中的代码写在<code>subscript</code>中:</p>
<pre><code>subscript(index: Int) -&gt; Int {
<pre><code class="lang-swift">subscript(index: Int) -&gt; Int {
// 返回与入参匹配的Int类型的值
}
</code></pre><p>下面代码演示了一个在<code>TimesTable</code>结构体中使用只读附属脚本的用法,该结构体用来展示传入整数的<em>n</em>倍。</p>
<pre><code>struct TimesTable {
</code></pre>
<p>下面代码演示了一个在<code>TimesTable</code>结构体中使用只读下标脚本的用法,该结构体用来展示传入整数的<em>n</em>倍。</p>
<pre><code class="lang-swift">struct TimesTable {
let multiplier: Int
subscript(index: Int) -&gt; Int {
return multiplier * index
return multiplier * index
}
}
let threeTimesTable = TimesTable(multiplier: 3)
println(&quot;3的6倍是\(threeTimesTable[6])&quot;)
// 输出 &quot;3的6倍是18&quot;
</code></pre><p>在上例中,通过<code>TimesTable</code>结构体创建了一个用来表示索引值三倍的实例。数值<code>3</code>作为结构体<code>构造函数</code>入参初始化实例成员<code>multiplier</code></p>
<p>你可以通过附属脚本来得到结果,比如<code>threeTimesTable[6]</code>。这条语句访问了<code>threeTimesTable</code>的第六个元素,返回<code>6</code><code>3</code>倍即<code>18</code></p>
</code></pre>
<p>在上例中,通过<code>TimesTable</code>结构体创建了一个用来表示索引值三倍的实例。数值<code>3</code>作为结构体<code>构造函数</code>入参初始化实例成员<code>multiplier</code></p>
<p>你可以通过下标脚本来得到结果,比如<code>threeTimesTable[6]</code>。这条语句访问了<code>threeTimesTable</code>的第六个元素,返回<code>6</code><code>3</code>倍即<code>18</code></p>
<p>&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD</p>
<blockquote>
<p>注意: </p>
<h1 id="-timestable-threetimestable-someindex-"><code>TimesTable</code>例子是基于一个固定的数学公式。它并不适合开放写权限来对<code>threeTimesTable[someIndex]</code>进行赋值操作,这也是为什么附属脚本只定义为只读的原因。 </h1>
<p>注意:</p>
<p><code>TimesTable</code>例子是基于一个固定的数学公式。它并不适合开放写权限来对<code>threeTimesTable[someIndex]</code>进行赋值操作,这也是为什么附属脚本只定义为只读的原因。</p>
<p><code>TimesTable</code>例子是基于一个固定的数学公式。它并不适合开放写权限来对<code>threeTimesTable[someIndex]</code>进行赋值操作,这也是为什么下标脚本只定义为只读的原因。</p>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>a516af6a531a104ec88da0d236ecf389a5ec72af</p>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
<p><a name="subscript_usage"></a></p>
<h2 id="-">附属脚本用法</h2>
<p>根据使用场景不同附属脚本也具有不同的含义。通常附属脚本是用来访问集合collection列表list或序列sequence中元素的快捷方式。你可以在你自己特定的类或结构体中自由的实现附属脚本来提供合适的功能。</p>
<p>例如Swift 的字典Dictionary实现了通过附属脚本来对其实例中存放的值进行存取操作。在附属脚本中使用和字典索引相同类型的值,并且把一个字典值类型的值赋值给这个附属脚本来为字典设值:</p>
<pre><code>var numberOfLegs = [&quot;spider&quot;: 8, &quot;ant&quot;: 6, &quot;cat&quot;: 4]
<h2 id="-">下标脚本用法</h2>
<p>根据使用场景不同下标脚本也具有不同的含义。通常下标脚本是用来访问集合collection列表list或序列sequence中元素的快捷方式。你可以在你自己特定的类或结构体中自由的实现下标脚本来提供合适的功能。</p>
<p>例如Swift 的字典Dictionary实现了通过下标脚本来对其实例中存放的值进行存取操作。在下标脚本中使用和字典索引相同类型的值,并且把一个字典值类型的值赋值给这个下标脚本来为字典设值:</p>
<pre><code class="lang-swift">var numberOfLegs = [&quot;spider&quot;: 8, &quot;ant&quot;: 6, &quot;cat&quot;: 4]
numberOfLegs[&quot;bird&quot;] = 2
</code></pre><p>上例定义一个名为<code>numberOfLegs</code>的变量并用一个字典字面量初始化出了包含三对键值的字典实例。<code>numberOfLegs</code>的字典存放值类型推断为<code>Dictionary&lt;String, Int&gt;</code>。字典实例创建完成之后通过附属脚本的方式将整型值<code>2</code>赋值到字典实例的索引为<code>bird</code>的位置中。</p>
<p>更多关于字典Dictionary附属脚本的信息请参考<a href="../chapter2/04_Collection_Types.html">读取和修改字典</a></p>
</code></pre>
<p>上例定义一个名为<code>numberOfLegs</code>的变量并用一个字典字面量初始化出了包含三对键值的字典实例。<code>numberOfLegs</code>的字典存放值类型推断为<code>Dictionary&lt;String, Int&gt;</code>。字典实例创建完成之后通过下标脚本的方式将整型值<code>2</code>赋值到字典实例的索引为<code>bird</code>的位置中。</p>
<p>更多关于字典Dictionary下标脚本的信息请参考<a href="../chapter2/04_Collection_Types.html">读取和修改字典</a></p>
<p>&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD</p>
<blockquote>
<p>注意: </p>
<h1 id="swift-get-int-numberoflegs-int-int-nil-nil-">Swift 中字典的附属脚本实现中,在<code>get</code>部分返回值是<code>Int?</code>,上例中的<code>numberOfLegs</code>字典通过附属脚本返回的是一个<code>Int?</code>或者说“可选的int”不是每个字典的索引都能得到一个整型值对于没有设过值的索引的访问返回的结果就是<code>nil</code>;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为<code>nil</code>即可。 </h1>
<p>注意:</p>
<p>Swift 中字典的附属脚本实现中,在<code>get</code>部分返回值是<code>Int?</code>,上例中的<code>numberOfLegs</code>字典通过附属脚本返回的是一个<code>Int?</code>或者说“可选的int”不是每个字典的索引都能得到一个整型值对于没有设过值的索引的访问返回的结果就是<code>nil</code>;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为<code>nil</code>即可。</p>
<p>Swift 中字典的下标脚本实现中,在<code>get</code>部分返回值是<code>Int?</code>,上例中的<code>numberOfLegs</code>字典通过下标脚本返回的是一个<code>Int?</code>或者说“可选的int”不是每个字典的索引都能得到一个整型值对于没有设过值的索引的访问返回的结果就是<code>nil</code>;同样想要从字典实例中删除某个索引下的值也只需要给这个索引赋值为<code>nil</code>即可。</p>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<blockquote>
<p>a516af6a531a104ec88da0d236ecf389a5ec72af</p>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
</blockquote>
<p><a name="subscript_options"></a></p>
<h2 id="-">附属脚本选项</h2>
<p>附属脚本允许任意数量的入参索引,并且每个入参类型也没有限制。附属脚本的返回值也可以是任何类型。附属脚本可以使用变量参数和可变参数但使用写入读出in-out参数或给参数设置默认值都是不允许的。</p>
<p>一个类或结构体可以根据自身需要提供多个附属脚本实现,在定义附属脚本时通过入参个类型进行区分,使用附属脚本时会自动匹配合适的附属脚本实现运行,这就是<em>附属脚本的重载</em></p>
<p>一个附属脚本入参是最常见的情况,但只要有合适的场景也可以定义多个附属脚本入参。如下例定义了一个<code>Matrix</code>结构体,将呈现一个<code>Double</code>类型的二维矩阵。<code>Matrix</code>结构体的附属脚本需要两个整型参数:</p>
<pre><code>struct Matrix {
<h2 id="-">下标脚本选项</h2>
<p>下标脚本允许任意数量的入参索引,并且每个入参类型也没有限制。下标脚本的返回值也可以是任何类型。下标脚本可以使用变量参数和可变参数但使用写入读出in-out参数或给参数设置默认值都是不允许的。</p>
<p>一个类或结构体可以根据自身需要提供多个下标脚本实现,在定义下标脚本时通过入参个类型进行区分,使用下标脚本时会自动匹配合适的下标脚本实现运行,这就是<em>下标脚本的重载</em></p>
<p>一个下标脚本入参是最常见的情况,但只要有合适的场景也可以定义多个下标脚本入参。如下例定义了一个<code>Matrix</code>结构体,将呈现一个<code>Double</code>类型的二维矩阵。<code>Matrix</code>结构体的下标脚本需要两个整型参数:</p>
<pre><code class="lang-swift">struct Matrix {
let rows: Int, columns: Int
var grid: Double[]
init(rows: Int, columns: Int) {
self.rows = rows
self.columns = columns
grid = Array(count: rows * columns, repeatedValue: 0.0)
self.rows = rows
self.columns = columns
grid = Array(count: rows * columns, repeatedValue: 0.0)
}
func indexIsValidForRow(row: Int, column: Int) -&gt; Bool {
return row &gt;= 0 &amp;&amp; row &lt; rows &amp;&amp; column &gt;= 0 &amp;&amp; column &lt; columns
@ -678,30 +728,37 @@ numberOfLegs[&quot;bird&quot;] = 2
}
}
}
</code></pre><p><code>Matrix</code>提供了一个两个入参的构造方法,入参分别是<code>rows</code><code>columns</code>,创建了一个足够容纳<code>rows * columns</code>个数的<code>Double</code>类型数组。为了存储将数组的大小和数组每个元素初始值0.0,都传入数组的构造方法中来创建一个正确大小的新数组。关于数组的构造方法和析构方法请参考<a href="../chapter2/04_Collection_Types.html">创建并且构造一个数组</a></p>
</code></pre>
<p><code>Matrix</code>提供了一个两个入参的构造方法,入参分别是<code>rows</code><code>columns</code>,创建了一个足够容纳<code>rows * columns</code>个数的<code>Double</code>类型数组。为了存储将数组的大小和数组每个元素初始值0.0,都传入数组的构造方法中来创建一个正确大小的新数组。关于数组的构造方法和析构方法请参考<a href="../chapter2/04_Collection_Types.html">创建并且构造一个数组</a></p>
<p>你可以通过传入合适的<code>row</code><code>column</code>的数量来构造一个新的<code>Matrix</code>实例:</p>
<pre><code>var matrix = Matrix(rows: 2, columns: 2)
</code></pre><p>上例中创建了一个新的两行两列的<code>Matrix</code>实例。在阅读顺序从左上到右下的<code>Matrix</code>实例中的数组实例<code>grid</code>是矩阵二维数组的扁平化存储:</p>
<pre><code>// 示意图
<pre><code class="lang-swift">var matrix = Matrix(rows: 2, columns: 2)
</code></pre>
<p>上例中创建了一个新的两行两列的<code>Matrix</code>实例。在阅读顺序从左上到右下的<code>Matrix</code>实例中的数组实例<code>grid</code>是矩阵二维数组的扁平化存储:</p>
<pre><code class="lang-swift">// 示意图
grid = [0.0, 0.0, 0.0, 0.0]
col0 col1
col0 col1
row0 [0.0, 0.0,
row1 0.0, 0.0]
</code></pre><p>将值赋给带有<code>row</code><code>column</code>附属脚本的<code>matrix</code>实例表达式可以完成赋值操作,附属脚本入参使用逗号分割</p>
<pre><code>matrix[0, 1] = 1.5
row1 0.0, 0.0]
</code></pre>
<p>将值赋给带有<code>row</code><code>column</code>下标脚本的<code>matrix</code>实例表达式可以完成赋值操作,下标脚本入参使用逗号分割</p>
<pre><code class="lang-swift">matrix[0, 1] = 1.5
matrix[1, 0] = 3.2
</code></pre><p>上面两条语句分别<code>让matrix</code>的右上值为 1.5,坐下值为 3.2</p>
<pre><code>[0.0, 1.5,
</code></pre>
<p>上面两条语句分别<code>让matrix</code>的右上值为 1.5,坐下值为 3.2</p>
<pre><code class="lang-swift">[0.0, 1.5,
3.2, 0.0]
</code></pre><p><code>Matrix</code>附属脚本的<code>getter</code><code>setter</code>中同时调用了附属脚本入参的<code>row</code><code>column</code>是否有效的判断。为了方便进行断言,<code>Matrix</code>包含了一个名为<code>indexIsValid</code>的成员方法,用来确认入参的<code>row</code><code>column</code>值是否会造成数组越界:</p>
<pre><code>func indexIsValidForRow(row: Int, column: Int) -&gt; Bool {
</code></pre>
<p><code>Matrix</code>下标脚本的<code>getter</code><code>setter</code>中同时调用了下标脚本入参的<code>row</code><code>column</code>是否有效的判断。为了方便进行断言,<code>Matrix</code>包含了一个名为<code>indexIsValid</code>的成员方法,用来确认入参的<code>row</code><code>column</code>值是否会造成数组越界:</p>
<pre><code class="lang-swift">func indexIsValidForRow(row: Int, column: Int) -&gt; Bool {
return row &gt;= 0 &amp;&amp; row &lt; rows &amp;&amp; column &gt;= 0 &amp;&amp; column &lt; columns
}
</code></pre><p>断言在附属脚本越界时触发:</p>
<pre><code>let someValue = matrix[2, 2]
</code></pre>
<p>断言在下标脚本越界时触发:</p>
<pre><code class="lang-swift">let someValue = matrix[2, 2]
// 断言将会触发,因为 [2, 2] 已经超过了matrix的最大长度
</code></pre>
</section>

115
chapter2/13_Inheritance.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.13" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.13" 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_88">
<section class="normal" id="section-gitbook_35">
<blockquote>
<p>翻译Hawstein</p>
<p>校对menlongsheng</p>
<p>翻译Hawstein<br>校对menlongsheng </p>
</blockquote>
<h1 id="-inheritance-">继承Inheritance</h1>
<hr>
@ -603,17 +602,16 @@
<li><a href="#preventing_overrides">防止重写</a></li>
</ul>
<p>一个类可以<em>继承inherit</em>另一个类的方法methods属性property和其它特性。当一个类继承其它类时继承类叫<em>子类subclass</em>,被继承类叫<em>超类或父类superclass</em>。在 Swift 中,继承是区分「类」与其它类型的一个基本特征。</p>
<p>在 Swift 中,类可以调用和访问超类的方法,属性和附属脚本subscripts并且可以重写override这些方法属性和附属脚本来优化或修改它们的行为。Swift 会检查你的重写定义在超类中是否有匹配的定义,以此确保你的重写行为是正确的。</p>
<p>在 Swift 中,类可以调用和访问超类的方法,属性和下标脚本subscripts并且可以重写override这些方法属性和下标脚本来优化或修改它们的行为。Swift 会检查你的重写定义在超类中是否有匹配的定义,以此确保你的重写行为是正确的。</p>
<p>可以为类中继承来的属性添加属性观察器property observer这样一来当属性值改变时类就会被通知到。可以为任何属性添加属性观察器无论它原本被定义为存储型属性stored property还是计算型属性computed property</p>
<p><a name="defining_a_base_class"></a></p>
<h2 id="-base-class-">定义一个基类Base class</h2>
<p>不继承于其它类的类,称之为<em>基类base calss</em></p>
<blockquote>
<p>注意:</p>
<p>Swift 中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。</p>
<p>注意:<br>Swift 中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。</p>
</blockquote>
<p>下面的例子定义了一个叫<code>Vehicle</code>的基类。这个基类声明了两个对所有车辆都通用的属性(<code>numberOfWheels</code><code>maxPassengers</code>)。这些属性在<code>description</code>方法中使用,这个方法返回一个<code>String</code>类型的,对车辆特征的描述:</p>
<pre><code>class Vehicle {
<pre><code class="lang-swift">class Vehicle {
var numberOfWheels: Int
var maxPassengers: Int
func description() -&gt; String {
@ -624,77 +622,84 @@
maxPassengers = 1
}
}
</code></pre><p><code>Vehicle</code>类定义了<em>构造器initializer</em>来设置属性的值。构造器会在<a href="../chapter2/_14Initialization.html">构造过程</a>一节中详细介绍,这里我们做一下简单介绍,以便于讲解子类中继承来的属性如何被修改。</p>
</code></pre>
<p><code>Vehicle</code>类定义了<em>构造器initializer</em>来设置属性的值。构造器会在<a href="../chapter2/_14Initialization.html">构造过程</a>一节中详细介绍,这里我们做一下简单介绍,以便于讲解子类中继承来的属性如何被修改。</p>
<p>构造器用于创建某个类型的一个新实例。尽管构造器并不是方法,但在语法上,两者很相似。构造器的工作是准备新实例以供使用,并确保实例中的所有属性都拥有有效的初始化值。</p>
<p>构造器的最简单形式就像一个没有参数的实例方法,使用<code>init</code>关键字:</p>
<pre><code>init() {
<pre><code class="lang-swift">init() {
// 执行构造过程
}
</code></pre><p>如果要创建一个<code>Vehicle</code>类的新实例,使用<em>构造器</em>语法调用上面的初始化器,即类名后面跟一个空的小括号:</p>
<pre><code>let someVehicle = Vehicle()
</code></pre><p>这个<code>Vehicle</code>类的构造器为任意的一辆车设置一些初始化属性值(<code>numberOfWheels = 0</code><code>maxPassengers = 1</code>)。</p>
</code></pre>
<p>如果要创建一个<code>Vehicle</code>类的新实例,使用<em>构造器</em>语法调用上面的初始化器,即类名后面跟一个空的小括号:</p>
<pre><code class="lang-swift">let someVehicle = Vehicle()
</code></pre>
<p>这个<code>Vehicle</code>类的构造器为任意的一辆车设置一些初始化属性值(<code>numberOfWheels = 0</code><code>maxPassengers = 1</code>)。</p>
<p><code>Vehicle</code>类定义了车辆的共同特性,但这个类本身并没太大用处。为了使它更为实用,你需要进一步细化它来描述更具体的车辆。</p>
<p><a name="subclassing"></a></p>
<h2 id="-subclassing-">子类生成Subclassing</h2>
<p><em>子类生成Subclassing</em>指的是在一个已有类的基础上创建一个新的类。子类继承超类的特性,并且可以优化或改变它。你还可以为子类添加新的特性。</p>
<p>为了指明某个类的超类,将超类名写在子类名的后面,用冒号分隔:</p>
<pre><code>class SomeClass: SomeSuperclass {
<pre><code class="lang-swift">class SomeClass: SomeSuperclass {
// 类的定义
}
</code></pre><p>下一个例子,定义一个更具体的车辆类叫<code>Bicycle</code>。这个新类是在 <code>Vehicle</code>类的基础上创建起来。因此你需要将<code>Vehicle</code>类放在 <code>Bicycle</code>类后面,用冒号分隔。</p>
</code></pre>
<p>下一个例子,定义一个更具体的车辆类叫<code>Bicycle</code>。这个新类是在 <code>Vehicle</code>类的基础上创建起来。因此你需要将<code>Vehicle</code>类放在 <code>Bicycle</code>类后面,用冒号分隔。</p>
<p>我们可以将这读作:</p>
<p>“定义一个新的类叫<code>Bicycle</code>,它继承了<code>Vehicle</code>的特性”;</p>
<pre><code>class Bicycle: Vehicle {
<pre><code class="lang-swift">class Bicycle: Vehicle {
init() {
super.init()
numberOfWheels = 2
}
}
</code></pre><p> <code>Bicycle</code><code>Vehicle</code>的子类,<code>Vehicle</code><code>Bicycle</code>的超类。新的<code>Bicycle</code>类自动获得<code>Vehicle</code>类的特性,比如 <code>maxPassengers</code><code>numberOfWheels</code>属性。你可以在子类中定制这些特性,或添加新的特性来更好地描述<code>Bicycle</code>类。</p>
</code></pre>
<p>preview
<code>Bicycle</code><code>Vehicle</code>的子类,<code>Vehicle</code><code>Bicycle</code>的超类。新的<code>Bicycle</code>类自动获得<code>Vehicle</code>类的特性,比如 <code>maxPassengers</code><code>numberOfWheels</code>属性。你可以在子类中定制这些特性,或添加新的特性来更好地描述<code>Bicycle</code>类。</p>
<p><code>Bicycle</code>类定义了一个构造器来设置它定制的特性自行车只有2个轮子<code>Bicycle</code>的构造器调用了它父类<code>Vehicle</code>的构造器 <code>super.init()</code>,以此确保在<code>Bicycle</code>类试图修改那些继承来的属性前<code>Vehicle</code>类已经初始化过它们了。</p>
<blockquote>
<p>注意:</p>
<p>不像 Objective-C在 Swift 中,初始化器默认是不继承的,见<a href="../chapter2/_14Initialization.html#initializer_inheritance_and_ overriding">初始化器的继承与重写</a></p>
<p>注意:<br>不像 Objective-C在 Swift 中,初始化器默认是不继承的,见<a href="../chapter2/_14Initialization.html#initializer_inheritance_and_ overriding">初始化器的继承与重写</a></p>
</blockquote>
<p><code>Vehicle</code>类中<code>maxPassengers</code>的默认值对自行车来说已经是正确的,因此在<code>Bicycle</code>的构造器中并没有改变它。而<code>numberOfWheels</code>原来的值对自行车来说是不正确的,因此在初始化器中将它更改为 2。</p>
<p><code>Bicycle</code>不仅可以继承<code>Vehicle</code>的属性,还可以继承它的方法。如果你创建了一个<code>Bicycle</code>类的实例,你就可以调用它继承来的<code>description</code>方法,并且可以看到,它输出的属性值已经发生了变化:</p>
<pre><code>let bicycle = Bicycle()
<pre><code class="lang-swift">let bicycle = Bicycle()
println(&quot;Bicycle: \(bicycle.description())&quot;)
// Bicycle: 2 wheels; up to 1 passengers
</code></pre><p>子类还可以继续被其它类继承:</p>
<pre><code>class Tandem: Bicycle {
</code></pre>
<p>子类还可以继续被其它类继承:</p>
<pre><code class="lang-swift">class Tandem: Bicycle {
init() {
super.init()
maxPassengers = 2
}
}
</code></pre><p>上面的例子创建了<code>Bicycle</code>的一个子类双人自行车tandem<code>Tandem</code><code>Bicycle</code>继承了两个属性,而这两个属性是<code>Bicycle</code><code>Vehicle</code>继承而来的。<code>Tandem</code>并不修改轮子的数量,因为它仍是一辆自行车,有 2 个轮子。但它需要修改<code>maxPassengers</code>的值,因为双人自行车可以坐两个人。</p>
</code></pre>
<p>上面的例子创建了<code>Bicycle</code>的一个子类双人自行车tandem<code>Tandem</code><code>Bicycle</code>继承了两个属性,而这两个属性是<code>Bicycle</code><code>Vehicle</code>继承而来的。<code>Tandem</code>并不修改轮子的数量,因为它仍是一辆自行车,有 2 个轮子。但它需要修改<code>maxPassengers</code>的值,因为双人自行车可以坐两个人。</p>
<blockquote>
<p>注意:</p>
<p>子类只允许修改从超类继承来的变量属性,而不能修改继承来的常量属性。</p>
<p>注意:<br>子类只允许修改从超类继承来的变量属性,而不能修改继承来的常量属性。</p>
</blockquote>
<p>创建一个<code>Tandem</code>类的实例,打印它的描述,即可看到它的属性已被更新:</p>
<pre><code>let tandem = Tandem()
<pre><code class="lang-swift">let tandem = Tandem()
println(&quot;Tandem: \(tandem.description())&quot;)
// Tandem: 2 wheels; up to 2 passengers
</code></pre><p>注意,<code>Tandem</code>类也继承了<code>description</code>方法。一个类的实例方法会被这个类的所有子类继承。</p>
</code></pre>
<p>注意,<code>Tandem</code>类也继承了<code>description</code>方法。一个类的实例方法会被这个类的所有子类继承。</p>
<p><a name="overriding"></a></p>
<h2 id="-overriding-">重写Overriding</h2>
<p>子类可以为继承来的实例方法instance method类方法class method实例属性instance property附属脚本subscript提供自己定制的实现implementation。我们把这种行为叫<em>重写overriding</em></p>
<p>子类可以为继承来的实例方法instance method类方法class method实例属性instance property下标脚本subscript提供自己定制的实现implementation。我们把这种行为叫<em>重写overriding</em></p>
<p>如果要重写某个特性,你需要在重写定义的前面加上<code>override</code>关键字。这么做,你就表明了你是想提供一个重写版本,而非错误地提供了一个相同的定义。意外的重写行为可能会导致不可预知的错误,任何缺少<code>override</code>关键字的重写都会在编译时被诊断为错误。</p>
<p><code>override</code>关键字会提醒 Swift 编译器去检查该类的超类(或其中一个父类)是否有匹配重写版本的声明。这个检查可以确保你的重写定义是正确的。</p>
<h3 id="-">访问超类的方法,属性及附属脚本</h3>
<p>当你在子类中重写超类的方法,属性或附属脚本时,有时在你的重写版本中使用已经存在的超类实现会大有裨益。比如,你可以优化已有实现的行为,或在一个继承来的变量中存储一个修改过的值。</p>
<p>在合适的地方,你可以通过使用<code>super</code>前缀来访问超类版本的方法,属性或附属脚本:</p>
<h3 id="-">访问超类的方法,属性及下标脚本</h3>
<p>当你在子类中重写超类的方法,属性或下标脚本时,有时在你的重写版本中使用已经存在的超类实现会大有裨益。比如,你可以优化已有实现的行为,或在一个继承来的变量中存储一个修改过的值。</p>
<p>在合适的地方,你可以通过使用<code>super</code>前缀来访问超类版本的方法,属性或下标脚本:</p>
<ul>
<li>在方法<code>someMethod</code>的重写实现中,可以通过<code>super.someMethod()</code>来调用超类版本的<code>someMethod</code>方法。</li>
<li>在属性<code>someProperty</code>的 getter 或 setter 的重写实现中,可以通过<code>super.someProperty</code>来访问超类版本的<code>someProperty</code>属性。</li>
<li>附属脚本的重写实现中,可以通过<code>super[someIndex]</code>来访问超类版本中的相同附属脚本。</li>
<li>下标脚本的重写实现中,可以通过<code>super[someIndex]</code>来访问超类版本中的相同下标脚本。</li>
</ul>
<h3 id="-">重写方法</h3>
<p>在子类中,你可以重写继承来的实例方法或类方法,提供一个定制或替代的方法实现。</p>
<p>下面的例子定义了<code>Vehicle</code>的一个新的子类,叫<code>Car</code>,它重写了从<code>Vehicle</code>类继承来的<code>description</code>方法:</p>
<pre><code>class Car: Vehicle {
<pre><code class="lang-swift">class Car: Vehicle {
var speed: Double = 0.0
init() {
super.init()
@ -706,24 +711,25 @@ println(&quot;Tandem: \(tandem.description())&quot;)
+ &quot;traveling at \(speed) mph&quot;
}
}
</code></pre><p><code>Car</code>声明了一个新的存储型属性<code>speed</code>,它是<code>Double</code>类型的,默认值是<code>0.0</code>表示“时速是0英里”。<code>Car</code>有自己的初始化器它将乘客的最大数量设为5轮子数量设为4。</p>
</code></pre>
<p><code>Car</code>声明了一个新的存储型属性<code>speed</code>,它是<code>Double</code>类型的,默认值是<code>0.0</code>表示“时速是0英里”。<code>Car</code>有自己的初始化器它将乘客的最大数量设为5轮子数量设为4。</p>
<p><code>Car</code>重写了继承来的<code>description</code>方法,它的声明与<code>Vehicle</code>中的<code>description</code>方法一致,声明前面加上了<code>override</code>关键字。</p>
<p><code>Car</code>中的<code>description</code>方法并非完全自定义,而是通过<code>super.description</code>使用了超类<code>Vehicle</code>中的<code>description</code>方法,然后再追加一些额外的信息,比如汽车的当前速度。</p>
<p>如果你创建一个<code>Car</code>的新实例,并打印<code>description</code>方法的输出,你就会发现描述信息已经发生了改变:</p>
<pre><code>let car = Car()
<pre><code class="lang-swift">let car = Car()
println(&quot;Car: \(car.description())&quot;)
// Car: 4 wheels; up to 5 passengers; traveling at 0.0 mph
</code></pre><h3 id="-">重写属性</h3>
</code></pre>
<h3 id="-">重写属性</h3>
<p>你可以重写继承来的实例属性或类属性提供自己定制的getter和setter或添加属性观察器使重写的属性观察属性值什么时候发生改变。</p>
<h4 id="-getters-setters">重写属性的Getters和Setters</h4>
<p>你可以提供定制的 getter或 setter来重写任意继承来的属性无论继承来的属性是存储型的还是计算型的属性。子类并不知道继承来的属性是存储型的还是计算型的它只知道继承来的属性会有一个名字和类型。你在重写一个属性时必需将它的名字和类型都写出来。这样才能使编译器去检查你重写的属性是与超类中同名同类型的属性相匹配的。</p>
<p>你可以将一个继承来的只读属性重写为一个读写属性,只需要你在重写版本的属性里提供 getter 和 setter 即可。但是,你不可以将一个继承来的读写属性重写为一个只读属性。</p>
<blockquote>
<p>注意:</p>
<p>如果你在重写属性中提供了 setter那么你也一定要提供 getter。如果你不想在重写版本中的 getter 里修改继承来的属性值,你可以直接返回<code>super.someProperty</code>来返回继承来的值。正如下面的<code>SpeedLimitedCar</code>的例子所示。</p>
<p>注意:<br>如果你在重写属性中提供了 setter那么你也一定要提供 getter。如果你不想在重写版本中的 getter 里修改继承来的属性值,你可以直接返回<code>super.someProperty</code>来返回继承来的值。正如下面的<code>SpeedLimitedCar</code>的例子所示。</p>
</blockquote>
<p>以下的例子定义了一个新类,叫<code>SpeedLimitedCar</code>,它是<code>Car</code>的子类。类<code>SpeedLimitedCar</code>表示安装了限速装置的车它的最高速度只能达到40mph。你可以通过重写继承来的<code>speed</code>属性来实现这个速度限制:</p>
<pre><code>class SpeedLimitedCar: Car {
<pre><code class="lang-swift">class SpeedLimitedCar: Car {
override var speed: Double {
get {
return super.speed
@ -733,20 +739,21 @@ println(&quot;Car: \(car.description())&quot;)
}
}
}
</code></pre><p>当你设置一个<code>SpeedLimitedCar</code>实例的<code>speed</code>属性时属性setter的实现会去检查新值与限制值40mph的大小它会将超类的<code>speed</code>设置为<code>newValue</code><code>40.0</code>中较小的那个。这两个值哪个较小由<code>min</code>函数决定它是Swift标准库中的一个全局函数。<code>min</code>函数接收两个或更多的数,返回其中最小的那个。</p>
</code></pre>
<p>当你设置一个<code>SpeedLimitedCar</code>实例的<code>speed</code>属性时属性setter的实现会去检查新值与限制值40mph的大小它会将超类的<code>speed</code>设置为<code>newValue</code><code>40.0</code>中较小的那个。这两个值哪个较小由<code>min</code>函数决定它是Swift标准库中的一个全局函数。<code>min</code>函数接收两个或更多的数,返回其中最小的那个。</p>
<p>如果你尝试将<code>SpeedLimitedCar</code>实例的<code>speed</code>属性设置为一个大于40mph的数然后打印<code>description</code>函数的输出你会发现速度被限制在40mph</p>
<pre><code>let limitedCar = SpeedLimitedCar()
<pre><code class="lang-swift">let limitedCar = SpeedLimitedCar()
limitedCar.speed = 60.0
println(&quot;SpeedLimitedCar: \(limitedCar.description())&quot;)
// SpeedLimitedCar: 4 wheels; up to 5 passengers; traveling at 40.0 mph
</code></pre><h4 id="-property-observer-">重写属性观察器Property Observer</h4>
</code></pre>
<h4 id="-property-observer-">重写属性观察器Property Observer</h4>
<p>你可以在属性重写中为一个继承来的属性添加属性观察器。这样一来,当继承来的属性值发生改变时,你就会被通知到,无论那个属性原本是如何实现的。关于属性观察器的更多内容,请看<a href="../chapter2/_10Properties.html#property_observer">属性观察器</a></p>
<blockquote>
<p>注意:</p>
<p>你不可以为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。这些属性的值是不可以被设置的,所以,为它们提供<code>willSet</code><code>didSet</code>实现是不恰当。此外还要注意,你不可以同时提供重写的 setter 和重写的属性观察器。如果你想观察属性值的变化,并且你已经为那个属性提供了定制的 setter那么你在 setter 中就可以观察到任何值变化了。</p>
<p>注意:<br>你不可以为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。这些属性的值是不可以被设置的,所以,为它们提供<code>willSet</code><code>didSet</code>实现是不恰当。此外还要注意,你不可以同时提供重写的 setter 和重写的属性观察器。如果你想观察属性值的变化,并且你已经为那个属性提供了定制的 setter那么你在 setter 中就可以观察到任何值变化了。</p>
</blockquote>
<p>下面的例子定义了一个新类叫<code>AutomaticCar</code>,它是<code>Car</code>的子类。<code>AutomaticCar</code>表示自动挡汽车,它可以根据当前的速度自动选择合适的挡位。<code>AutomaticCar</code>也提供了定制的<code>description</code>方法,可以输出当前挡位。</p>
<pre><code>class AutomaticCar: Car {
<pre><code class="lang-swift">class AutomaticCar: Car {
var gear = 1
override var speed: Double {
didSet {
@ -757,15 +764,17 @@ println(&quot;SpeedLimitedCar: \(limitedCar.description())&quot;)
return super.description() + &quot; in gear \(gear)&quot;
}
}
</code></pre><p>当你设置<code>AutomaticCar</code><code>speed</code>属性,属性的<code>didSet</code>观察器就会自动地设置<code>gear</code>属性为新的速度选择一个合适的挡位。具体来说就是属性观察器将新的速度值除以10然后向下取得最接近的整数值最后加1来得到档位<code>gear</code>的值。例如速度为10.0时挡位为1速度为35.0时挡位为4</p>
<pre><code>let automatic = AutomaticCar()
</code></pre>
<p>当你设置<code>AutomaticCar</code><code>speed</code>属性,属性的<code>didSet</code>观察器就会自动地设置<code>gear</code>属性为新的速度选择一个合适的挡位。具体来说就是属性观察器将新的速度值除以10然后向下取得最接近的整数值最后加1来得到档位<code>gear</code>的值。例如速度为10.0时挡位为1速度为35.0时挡位为4</p>
<pre><code class="lang-swift">let automatic = AutomaticCar()
automatic.speed = 35.0
println(&quot;AutomaticCar: \(automatic.description())&quot;)
// AutomaticCar: 4 wheels; up to 5 passengers; traveling at 35.0 mph in gear 4
</code></pre><p><a name="preventing_overrides"></a></p>
</code></pre>
<p><a name="preventing_overrides"></a></p>
<h2 id="-">防止重写</h2>
<p>你可以通过把方法,属性或附属脚本标记为<em><code>final</code></em>来防止它们被重写,只需要在声明关键字前加上<code>@final</code>特性即可。(例如:<code>@final var</code>, <code>@final func</code>, <code>@final class func</code>, 以及 <code>@final subscript</code></p>
<p>如果你重写了<code>final</code>方法,属性或附属脚本,在编译时会报错。在扩展中,你添加到类里的方法,属性或附属脚本也可以在扩展的定义里标记为 final。</p>
<p>你可以通过把方法,属性或下标脚本标记为<em><code>final</code></em>来防止它们被重写,只需要在声明关键字前加上<code>@final</code>特性即可。(例如:<code>@final var</code>, <code>@final func</code>, <code>@final class func</code>, 以及 <code>@final subscript</code></p>
<p>如果你重写了<code>final</code>方法,属性或下标脚本,在编译时会报错。在扩展中,你添加到类里的方法,属性或下标脚本也可以在扩展的定义里标记为 final。</p>
<p>你可以通过在关键字<code>class</code>前添加<code>@final</code>特性(<code>@final class</code>)来将整个类标记为 final 的,这样的类是不可被继承的,否则会报编译错误。</p>
@ -776,7 +785,7 @@ println(&quot;AutomaticCar: \(automatic.description())&quot;)
</div>
<a href="../chapter2/12_Subscripts.html" class="navigation navigation-prev " aria-label="Previous page: 附属脚本"><i class="fa fa-angle-left"></i></a>
<a href="../chapter2/12_Subscripts.html" class="navigation navigation-prev " aria-label="Previous page: 下标脚本"><i class="fa fa-angle-left"></i></a>
<a href="../chapter2/14_Initialization.html" class="navigation navigation-next " aria-label="Next page: 构造过程"><i class="fa fa-angle-right"></i></a>

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>

61
chapter2/15_Deinitialization.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.15" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.15" 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_115">
<section class="normal" id="section-gitbook_62">
<blockquote>
<p>翻译bruce0505</p>
<p>校对fd5788</p>
<p>翻译bruce0505<br>校对fd5788 </p>
</blockquote>
<h1 id="-deinitialization-">析构过程Deinitialization</h1>
<hr>
@ -605,15 +604,16 @@
<h2 id="-">析构过程原理</h2>
<p>Swift 会自动释放不再需要的实例以释放资源。如<a href="16_Automatic_Reference_Counting.html">自动引用计数</a>那一章描述Swift 通过<em>自动引用计数</em>ARC处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是当使用自己的资源时你可能需要进行一些额外的清理。例如如果创建了一个自定义的类来打开一个文件并写入一些数据你可能需要在类实例被释放之前关闭该文件。</p>
<p>在类的定义中,每个类最多只能有一个析构函数。析构函数不带任何参数,在写法上不带括号:</p>
<pre><code>deinit {
<pre><code class="lang-swift">deinit {
// 执行析构过程
}
</code></pre><p>析构函数是在实例释放发生前一步被自动调用。不允许主动调用自己的析构函数。子类继承了父类的析构函数,并且在子类析构函数实现的最后,父类的析构函数被自动调用。即使子类没有提供自己的析构函数,父类的析构函数也总是被调用。</p>
</code></pre>
<p>析构函数是在实例释放发生前一步被自动调用。不允许主动调用自己的析构函数。子类继承了父类的析构函数,并且在子类析构函数实现的最后,父类的析构函数被自动调用。即使子类没有提供自己的析构函数,父类的析构函数也总是被调用。</p>
<p>因为直到实例的析构函数被调用时,实例才会被释放,所以析构函数可以访问所有请求实例的属性,并且根据那些属性可以修改它的行为(比如查找一个需要被关闭的文件的名称)。</p>
<p><a name="deinitializers_in_action"></a></p>
<h2 id="-">析构函数操作</h2>
<p>这里是一个析构函数操作的例子。这个例子是一个简单的游戏,定义了两种新类型,<code>Bank</code><code>Player</code><code>Bank</code>结构体管理一个虚拟货币的流通,在这个流通中<code>Bank</code>永远不可能拥有超过 10,000 的硬币。在这个游戏中有且只能有一个<code>Bank</code>存在,因此<code>Bank</code>由带有静态属性和静态方法的结构体实现,从而存储和管理其当前的状态。</p>
<pre><code>struct Bank {
<pre><code class="lang-swift">struct Bank {
static var coinsInBank = 10_000
static func vendCoins(var numberOfCoinsToVend: Int) -&gt; Int {
numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank)
@ -621,46 +621,51 @@
return numberOfCoinsToVend
}
static func receiveCoins(coins: Int) {
coinsInBank += coins
coinsInBank += coins
}
}
</code></pre><p><code>Bank</code>根据它的<code>coinsInBank</code>属性来跟踪当前它拥有的硬币数量。银行还提供两个方法——<code>vendCoins</code><code>receiveCoins</code>——用来处理硬币的分发和收集。</p>
</code></pre>
<p><code>Bank</code>根据它的<code>coinsInBank</code>属性来跟踪当前它拥有的硬币数量。银行还提供两个方法——<code>vendCoins</code><code>receiveCoins</code>——用来处理硬币的分发和收集。</p>
<p><code>vendCoins</code>方法在 bank 分发硬币之前检查是否有足够的硬币。如果没有足够多的硬币,<code>Bank</code>返回一个比请求时小的数字(如果没有硬币留在 bank 中就返回 0)。<code>vendCoins</code>方法声明<code>numberOfCoinsToVend</code>为一个变量参数,这样就可以在方法体的内部修改数字,而不需要定义一个新的变量。<code>vendCoins</code>方法返回一个整型值,表明了提供的硬币的实际数目。</p>
<p><code>receiveCoins</code>方法只是将 bank 的硬币存储和接收到的硬币数目相加,再保存回 bank。</p>
<p><code>Player</code>类描述了游戏中的一个玩家。每一个 player 在任何时刻都有一定数量的硬币存储在他们的钱包中。这通过 player 的<code>coinsInPurse</code>属性来体现:</p>
<pre><code> class Player {
var coinsInPurse: Int
init(coins: Int) {
coinsInPurse = Bank.vendCoins(coins)
<pre><code class="lang-swift">class Player {
var coinsInPurse: Int
init(coins: Int) {
coinsInPurse = Bank.vendCoins(coins)
}
func winCoins(coins: Int) {
coinsInPurse += Bank.vendCoins(coins)
coinsInPurse += Bank.vendCoins(coins)
}
deinit {
Bank.receiveCoins(coinsInPurse)
Bank.receiveCoins(coinsInPurse)
}
}
</code></pre><p>每个<code>Player</code>实例都由一个指定数目硬币组成的启动额度初始化,这些硬币在 bank 初始化的过程中得到。如果没有足够的硬币可用,<code>Player</code>实例可能收到比指定数目少的硬币。</p>
}
</code></pre>
<p>每个<code>Player</code>实例都由一个指定数目硬币组成的启动额度初始化,这些硬币在 bank 初始化的过程中得到。如果没有足够的硬币可用,<code>Player</code>实例可能收到比指定数目少的硬币。</p>
<p><code>Player</code>类定义了一个<code>winCoins</code>方法,该方法从银行获取一定数量的硬币,并把它们添加到玩家的钱包。<code>Player</code>类还实现了一个析构函数,这个析构函数在<code>Player</code>实例释放前一步被调用。这里析构函数只是将玩家的所有硬币都返回给银行:</p>
<pre><code>var playerOne: Player? = Player(coins: 100)
<pre><code class="lang-swift">var playerOne: Player? = Player(coins: 100)
println(&quot;A new player has joined the game with \(playerOne!.coinsInPurse) coins&quot;)
// 输出 &quot;A new player has joined the game with 100 coins&quot;
println(&quot;There are now \(Bank.coinsInBank) coins left in the bank&quot;)
// 输出 &quot;A new player has joined the game with 100 coins&quot;
println(&quot;There are now \(Bank.coinsInBank) coins left in the bank&quot;)
// 输出 &quot;There are now 9900 coins left in the bank&quot;
</code></pre><p>一个新的<code>Player</code>实例随着一个 100 个硬币(如果有)的请求而被创建。这<code>个Player</code>实例存储在一个名为<code>playerOne</code>的可选<code>Player</code>变量中。这里使用一个可选变量,是因为玩家可以随时离开游戏。设置为可选使得你可以跟踪当前是否有玩家在游戏中。</p>
</code></pre>
<p>一个新的<code>Player</code>实例随着一个 100 个硬币(如果有)的请求而被创建。这<code>个Player</code>实例存储在一个名为<code>playerOne</code>的可选<code>Player</code>变量中。这里使用一个可选变量,是因为玩家可以随时离开游戏。设置为可选使得你可以跟踪当前是否有玩家在游戏中。</p>
<p>因为<code>playerOne</code>是可选的,所以由一个感叹号(<code>!</code>)来修饰,每当其<code>winCoins</code>方法被调用时,<code>coinsInPurse</code>属性被访问并打印出它的默认硬币数目。</p>
<pre><code>playerOne!.winCoins(2_000)
println(&quot;PlayerOne won 2000 coins &amp; now has \ (playerOne!.coinsInPurse) coins&quot;)
<pre><code class="lang-swift">playerOne!.winCoins(2_000)
println(&quot;PlayerOne won 2000 coins &amp; now has \ (playerOne!.coinsInPurse) coins&quot;)
// 输出 &quot;PlayerOne won 2000 coins &amp; now has 2100 coins&quot;
println(&quot;The bank now only has \(Bank.coinsInBank) coins left&quot;)
// 输出 &quot;The bank now only has 7900 coins left&quot;
</code></pre><p>这里player 已经赢得了 2,000 硬币。player 的钱包现在有 2,100 硬币bank 只剩余 7,900 硬币。</p>
<pre><code>playerOne = nil
</code></pre>
<p>这里player 已经赢得了 2,000 硬币。player 的钱包现在有 2,100 硬币bank 只剩余 7,900 硬币。</p>
<pre><code class="lang-swift">playerOne = nil
println(&quot;PlayerOne has left the game&quot;)
// 输出 &quot;PlayerOne has left the game&quot;
println(&quot;The bank now has \(Bank.coinsInBank) coins&quot;)
// 输出 &quot;The bank now has 10000 coins&quot;
</code></pre><p>玩家现在已经离开了游戏。这表明是要将可选的<code>playerOne</code>变量设置为<code>nil</code>,意思是“没有<code>Player</code>实例”。当这种情况发生的时候,<code>playerOne</code>变量对<code>Player</code>实例的引用被破坏了。没有其它属性或者变量引用<code>Player</code>实例,因此为了清空它占用的内存从而释放它。在这发生前一步,其析构函数被自动调用,其硬币被返回到银行。</p>
</code></pre>
<p>玩家现在已经离开了游戏。这表明是要将可选的<code>playerOne</code>变量设置为<code>nil</code>,意思是“没有<code>Player</code>实例”。当这种情况发生的时候,<code>playerOne</code>变量对<code>Player</code>实例的引用被破坏了。没有其它属性或者变量引用<code>Player</code>实例,因此为了清空它占用的内存从而释放它。在这发生前一步,其析构函数被自动调用,其硬币被返回到银行。</p>
</section>

14
chapter2/16_Automatic_Reference_Counting.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.16" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.16" 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,7 +587,7 @@
<div class="page-inner">
<section class="normal" id="section-gitbook_117">
<section class="normal" id="section-gitbook_64">
<blockquote>
<p>翻译TimothyYe</p>
@ -779,7 +779,7 @@ class CreditCard {
</code></pre><p>现在你可以创建<code>Customer</code>类的实例,用它初始化<code>CreditCard</code>实例,并将新创建的<code>CreditCard</code>实例赋值为客户的<code>card</code>属性。</p>
<pre><code>john = Customer(name: &quot;John Appleseed&quot;)
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
</code></pre><p>在你关联两个实例后,们的引用关系如下图所示:</p>
</code></pre><p>在你关联两个实例后,们的引用关系如下图所示:</p>
<p><img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/unownedReference01_2x.png" alt=""></p>
<p><code>Customer</code>实例持有对<code>CreditCard</code>实例的强引用,而<code>CreditCard</code>实例持有对<code>Customer</code>实例的无主引用。</p>
<p>由于<code>customer</code>的无主引用,当你断开<code>john</code>变量持有的强引用时,再也没有指向<code>Customer</code>实例的强引用了:</p>
@ -815,7 +815,7 @@ class City {
}
</code></pre><p>为了建立两个类的依赖关系,<code>City</code>的构造函数有一个<code>Country</code>实例的参数,并且将实例保存为<code>country</code>属性。</p>
<p><code>Country</code>的构造函数调用了<code>City</code>的构造函数。然而,只有<code>Country</code>的实例完全初始化完后,<code>Country</code>的构造函数才能把<code>self</code>传给<code>City</code>的构造函数。(<a href="14_Initialization.html">在两段式构造过程中有具体描述</a></p>
<p>为了满足这种需求通过在类型结尾处加上感叹号City!)的方式,将<code>Country</code><code>capitalCity</code>属性声明为隐式解析可选类型的属性。这表示像其他可选类型一样,<code>capitalCity</code>属性的默认值为<code>nil</code>,但是不需要展开的值就能访问它。(<a href="01_The_Basics.html">在隐式解析可选类型中有描述</a></p>
<p>为了满足这种需求通过在类型结尾处加上感叹号City!)的方式,将<code>Country</code><code>capitalCity</code>属性声明为隐式解析可选类型的属性。这表示像其他可选类型一样,<code>capitalCity</code>属性的默认值为<code>nil</code>,但是不需要展开的值就能访问它。(<a href="01_The_Basics.html">在隐式解析可选类型中有描述</a></p>
<p>由于<code>capitalCity</code>默认值为<code>nil</code>,一旦<code>Country</code>的实例在构造函数中给<code>name</code>属性赋值后,整个初始化过程就完成了。这代表一旦<code>name</code>属性被赋值后,<code>Country</code>的构造函数就能引用并传递隐式的<code>self</code><code>Country</code>的构造函数在赋值<code>capitalCity</code>时,就能将<code>self</code>作为参数传递给<code>City</code>的构造函数。</p>
<p>以上的意义在于你可以通过一条语句同时创建<code>Country</code><code>City</code>的实例,而不产生循环强引用,并且<code>capitalCity</code>的属性能被直接访问,而不需要通过感叹号来展开它的可选值:</p>
<pre><code>var country = Country(name: &quot;Canada&quot;, capitalName: &quot;Ottawa&quot;)
@ -900,7 +900,7 @@ println(paragraph!.asHTML())
}
</code></pre><h3 id="-">弱引用和无主引用</h3>
<p>当闭包和捕获的实例总是互相引用时并且总是同时销毁时,将闭包内的捕获定义为无主引用。</p>
<p>相反的,当捕获引用有时可能会是<code>nil</code>时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为<code>nil</code>。这使我们可以在闭包内检查们是否存在。</p>
<p>相反的,当捕获引用有时可能会是<code>nil</code>时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为<code>nil</code>。这使我们可以在闭包内检查们是否存在。</p>
<blockquote>
<p>注意:</p>
<p>如果捕获的引用绝对不会置为<code>nil</code>,应该用无主引用,而不是弱引用。</p>

12
chapter2/17_Optional_Chaining.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.17" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.17" 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,7 +587,7 @@
<div class="page-inner">
<section class="normal" id="section-gitbook_119">
<section class="normal" id="section-gitbook_66">
<blockquote>
<p>翻译Jasonbroker</p>
@ -612,7 +612,7 @@ Swift 的可选链和 Objective-C 中的消息为空有些相像,但是 Swift
</blockquote>
<p><a name="optional_chaining_as_an_alternative_to_forced_unwrapping"></a></p>
<h2 id="-">可选链可替代强制解析</h2>
<p>通过在想调用的属性、方法、或子脚本的可选值(<code>optional value</code>)(非空)后面放一个问号,可以定义一个可选链。这一点很像在可选值后面放一个叹号来强制拆得其封包内的值。们的主要的区别在于当可选值为空时可选链即刻失败,然而一般的强制解析将会引发运行时错误。</p>
<p>通过在想调用的属性、方法、或子脚本的可选值(<code>optional value</code>)(非空)后面放一个问号,可以定义一个可选链。这一点很像在可选值后面放一个叹号来强制拆得其封包内的值。们的主要的区别在于当可选值为空时可选链即刻失败,然而一般的强制解析将会引发运行时错误。</p>
<p>为了反映可选链可以调用空(<code>nil</code>不论你调用的属性、方法、子脚本等返回的值是不是可选值它的返回结果都是一个可选值。你可以利用这个返回值来检测你的可选链是否调用成功有返回值即成功返回nil则失败。</p>
<p>调用可选链的返回结果与原本的返回结果具有相同的类型,但是原本的返回结果被包装成了一个可选值,当可选链调用成功时,一个应该返回<code>Int</code>的属性将会返回<code>Int?</code></p>
<p>下面几段代码将解释可选链和强制解析的不同。</p>
@ -760,7 +760,7 @@ if let firstRoomName = john.residence?[0].name {
<p>因此:</p>
<p>如果你试图通过可选链获得<code>Int</code>值,不论使用了多少层链接返回的总是<code>Int?</code>
相似的,如果你试图通过可选链获得<code>Int?</code>值,不论使用了多少层链接返回的总是<code>Int?</code></p>
<p>下面的例子试图获取<code>john</code><code>residence</code>属性里的<code>address</code><code>street</code>属性。这里使用了两层可选链来联系<code>residence</code><code>address</code>属性,们两者都是可选类型:</p>
<p>下面的例子试图获取<code>john</code><code>residence</code>属性里的<code>address</code><code>street</code>属性。这里使用了两层可选链来联系<code>residence</code><code>address</code>属性,们两者都是可选类型:</p>
<pre><code>if let johnsStreet = john.residence?.address?.street {
println(&quot;John&#39;s street name is \(johnsStreet).&quot;)
} else {

8
chapter2/18_Type_Casting.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.18" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.18" 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,7 +587,7 @@
<div class="page-inner">
<section class="normal" id="section-gitbook_121">
<section class="normal" id="section-gitbook_68">
<blockquote>
<p>翻译xiehurricane</p>

8
chapter2/19_Nested_Types.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.19" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.19" 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,7 +587,7 @@
<div class="page-inner">
<section class="normal" id="section-gitbook_123">
<section class="normal" id="section-gitbook_70">
<blockquote>
<p>翻译Lin-H</p>

18
chapter2/20_Extensions.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.20" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.20" 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,7 +587,7 @@
<div class="page-inner">
<section class="normal" id="section-gitbook_125">
<section class="normal" id="section-gitbook_72">
<blockquote>
<p>翻译lyuka</p>
@ -612,7 +612,7 @@
<li>提供新的构造器</li>
<li>定义下标</li>
<li>定义和使用新的嵌套类型</li>
<li>使一个已有类型符合某个接口</li>
<li>使一个已有类型符合某个协议</li>
</ul>
<blockquote>
<p>注意:</p>
@ -624,7 +624,7 @@
<pre><code>extension SomeType {
// 加到SomeType的新功能写到这里
}
</code></pre><p>一个扩展可以扩展一个已有类型使其能够适配一个或多个协议protocol。当这种情况发生时接口的名字应该完全按照类或结构体的名字的方式进行书写:</p>
</code></pre><p>一个扩展可以扩展一个已有类型使其能够适配一个或多个协议protocol。当这种情况发生时协议的名字应该完全按照类或结构体的名字的方式进行书写:</p>
<pre><code>extension SomeType: SomeProtocol, AnotherProctocol {
// 协议实现写到这里
}
@ -658,11 +658,11 @@ println(&quot;A marathon is \(aMarathon) meters long&quot;)
</blockquote>
<p><a name="initializers"></a></p>
<h2 id="-initializers-">构造器Initializers</h2>
<p>扩展可以向已有类型添加新的构造器。这可以让你扩展其它类型,将你自己的定制类型作为构造器参数,或者提供该类型的原始实现中没有包含的额外初始化选项。</p>
<p>扩展可以向已有类型添加新的构造器。这可以让你扩展其它类型,将你自己的定制类型作为构造器参数,或者提供该类型的原始实现中没有包含的额外初始化选项。 </p>
<p>扩展能向类中添加新的便利构造器,但是它们不能向类中添加新的指定构造器或析构函数。指定构造器和析构函数必须总是由原始的类实现来提供。</p>
<blockquote>
<p>注意:</p>
<p>如果你使用扩展向一个值类型添加一个构造器该构造器向所有的存储属性提供默认值而且没有定义任何定制构造器custom initializers那么对于来自你的扩展构造器中的值类型你可以调用默认构造器(default initializers)和成员构造器(memberwise initializers)。
正如在值类型的构造器授权中描述的,如果你已经把构造器写成值类型原始实现的一部分,上述规则不再适用。</p>
<p>如果你使用扩展向一个值类型添加一个构造器该构造器向所有的存储属性提供默认值而且没有定义任何定制构造器custom initializers那么对于来自你的扩展构造器中的值类型你可以调用默认构造器(default initializers)和逐一成员构造器(memberwise initializers)。<br>正如在值类型的构造器授权中描述的,如果你已经把构造器写成值类型原始实现的一部分,上述规则不再适用。</p>
</blockquote>
<p>下面的例子定义了一个用于描述几何矩形的定制结构体<code>Rect</code>。这个例子同时定义了两个辅助结构体<code>Size</code><code>Point</code>,它们都把<code>0.0</code>作为所有属性的默认值:</p>
<pre><code>struct Size {

12
chapter2/21_Protocols.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.21" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.21" 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,7 +587,7 @@
<div class="page-inner">
<section class="normal" id="section-gitbook_127">
<section class="normal" id="section-gitbook_74">
<blockquote>
<p>翻译geek5nan</p>
@ -863,7 +863,7 @@ game.play()
}
</code></pre><p>通过<code>扩展</code>为上一节中提到的<code>Dice</code>类遵循<code>TextRepresentable</code>协议</p>
<pre><code>extension Dice: TextRepresentable {
cun asText() -&gt; String {
func asText() -&gt; String {
return &quot;A \(sides)-sided dice&quot;
}
}
@ -1005,7 +1005,7 @@ class Country: HasArea {
var legs: Int
init(legs: Int) { self.legs = legs }
}
</code></pre><p><code>Circle,Country,Animal</code>并没有一个相同的基类,所以采用<code>AnyObject</code>类型的数组来装载在们的实例,如下所示:</p>
</code></pre><p><code>Circle,Country,Animal</code>并没有一个相同的基类,所以采用<code>AnyObject</code>类型的数组来装载在们的实例,如下所示:</p>
<pre><code>let objects: AnyObject[] = [
Circle(radius: 2.0),
Country(area: 243_610),

22
chapter2/22_Generics.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.22" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.22" 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,7 +587,7 @@
<div class="page-inner">
<section class="normal" id="section-gitbook_129">
<section class="normal" id="section-gitbook_76">
<blockquote>
<p>翻译takalard</p>
@ -709,7 +709,7 @@ swapTwoValues(&amp;someString, &amp;anotherString)
return items.removeLast()
}
}
</code></pre><p>这个结构体在栈中使用一个<code>Array</code>性质的<code>items</code>存储值。<code>Stack</code>提供两个方法:<code>push</code><code>pop</code>,从栈中压进一个值和移除一个值。这些方法标记为可变的,因为们需要修改(或<em>转换</em>)结构体的<code>items</code>数组。</p>
</code></pre><p>这个结构体在栈中使用一个<code>Array</code>性质的<code>items</code>存储值。<code>Stack</code>提供两个方法:<code>push</code><code>pop</code>,从栈中压进一个值和移除一个值。这些方法标记为可变的,因为们需要修改(或<em>转换</em>)结构体的<code>items</code>数组。</p>
<p>上面所展现的<code>IntStack</code>类型只能用于<code>Int</code>值,不过,其对于定义一个泛型<code>Stack</code>类(可以处理<em>任何</em>类型值的栈)是非常有用的。</p>
<p>这里是一个相同代码的泛型版本:</p>
<pre><code>struct Stack&lt;T&gt; {
@ -748,7 +748,7 @@ stackOfStrings.push(&quot;cuatro&quot;)
<p><code>swapTwoValues</code>函数和<code>Stack</code>类型可以作用于任何类型,不过,有的时候对使用在泛型函数和泛型类型上的类型强制约束为某种特定类型是非常有用的。类型约束指定了一个必须继承自指定类的类型参数,或者遵循一个特定的协议或协议构成。</p>
<p>例如Swift 的<code>Dictionary</code>类型对作用于其键的类型做了些限制。在[字典][5]的描述中,字典的键类型必须是<em>可哈希</em>,也就是说,必须有一种方法可以使其是唯一的表示。<code>Dictionary</code>之所以需要其键是可哈希是为了以便于其检查其是否包含某个特定键的值。如无此需求,<code>Dictionary</code>即不会告诉是否插入或者替换了某个特定键的值,也不能查找到已经存储在字典里面的给定键值。</p>
<p>这个需求强制加上一个类型约束作用于<code>Dictionary</code>的键上,当然其键类型必须遵循<code>Hashable</code>协议Swift 标准库中定义的一个特定协议)。所有的 Swift 基本类型(如<code>String</code><code>Int</code> <code>Double</code><code>Bool</code>)默认都是可哈希。</p>
<p>当你创建自定义泛型类型时,你可以定义你自己的类型约束,当然,这些约束要支持泛型编程的强力特征中的多数。抽象概念如<code>可哈希</code>具有的类型特征是根据们概念特征来界定的,而不是们的直接类型特征。</p>
<p>当你创建自定义泛型类型时,你可以定义你自己的类型约束,当然,这些约束要支持泛型编程的强力特征中的多数。抽象概念如<code>可哈希</code>具有的类型特征是根据们概念特征来界定的,而不是们的直接类型特征。</p>
<h3 id="-">类型约束语法</h3>
<p>你可以写一个在一个类型参数名后面的类型约束,通过冒号分割,来作为类型参数链的一部分。这种作用于泛型函数的类型约束的基础语法如下所示(和泛型类型的语法相同):</p>
<pre><code>func someFunction&lt;T: SomeClass, U: SomeProtocol&gt;(someT: T, someU: U) {
@ -876,7 +876,7 @@ let stringIndex = findIndex([&quot;Mike&quot;, &quot;Malcolm&quot;, &quot;Andrea
<p>[类型约束][8]中描述的类型约束确保你定义关于类型参数的需求和一泛型函数或类型有关联。</p>
<p>对于关联类型的定义需求也是非常有用的。你可以通过这样去定义<em>where语句</em>作为一个类型参数队列的一部分。一个<code>where</code>语句使你能够要求一个关联类型遵循一个特定的协议,以及(或)那个特定的类型参数和关联类型可以是相同的。你可写一个<code>where</code>语句,通过紧随放置<code>where</code>关键字在类型参数队列后面,其后跟着一个或者多个针对关联类型的约束,以及(或)一个或多个类型和关联类型的等于关系。</p>
<p>下面的列子定义了一个名为<code>allItemsMatch</code>的泛型函数,用来检查是否两个<code>Container</code>单例包含具有相同顺序的相同元素。如果匹配到所有的元素,那么返回一个为<code>true</code><code>Boolean</code>值,反之,则相反。</p>
<p>这两个容器可以被检查出是否是相同类型的容器(虽然它们可以是),但们确实拥有相同类型的元素。这个需求通过一个类型约束和<code>where</code>语句结合来表示:</p>
<p>这两个容器可以被检查出是否是相同类型的容器(虽然它们可以是),但们确实拥有相同类型的元素。这个需求通过一个类型约束和<code>where</code>语句结合来表示:</p>
<pre><code>func allItemsMatch&lt;
C1: Container, C2: Container
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable&gt;
@ -912,9 +912,9 @@ let stringIndex = findIndex([&quot;Mike&quot;, &quot;Malcolm&quot;, &quot;Andrea
<code>anotherContainer</code>是一个<code>C2</code>类型的容器。
<code>someContainer</code><code>anotherContainer</code>包含相同的元素类型。
<code>someContainer</code>中的元素可以通过不等于操作(<code>!=</code>)来检查它们是否彼此不同。</p>
<p>第三个和第四个要求结合起来的意思是<code>anotherContainer</code>中的元素也可以通过 <code>!=</code> 操作来检查,因为们在<code>someContainer</code>中元素确实是相同的类型。</p>
<p>这些要求能够使<code>allItemsMatch</code>函数比较两个容器,即便们是不同的容器类型。</p>
<p><code>allItemsMatch</code>首先检查两个容器是否拥有同样数目的items如果们的元素数目不同,没有办法进行匹配,函数就会<code>false</code></p>
<p>第三个和第四个要求结合起来的意思是<code>anotherContainer</code>中的元素也可以通过 <code>!=</code> 操作来检查,因为们在<code>someContainer</code>中元素确实是相同的类型。</p>
<p>这些要求能够使<code>allItemsMatch</code>函数比较两个容器,即便们是不同的容器类型。</p>
<p><code>allItemsMatch</code>首先检查两个容器是否拥有同样数目的items如果们的元素数目不同,没有办法进行匹配,函数就会<code>false</code></p>
<p>检查完之后,函数通过<code>for-in</code>循环和半闭区间操作(..)来迭代<code>someContainer</code>中的所有元素。对于每个元素,函数检查是否<code>someContainer</code>中的元素不等于对应的<code>anotherContainer</code>中的元素,如果这两个元素不等,则这两个容器不匹配,返回<code>false</code></p>
<p>如果循环体结束后未发现没有任何的不匹配,那表明两个容器匹配,函数返回<code>true</code></p>
<p>这里演示了allItemsMatch函数运算的过程</p>
@ -931,7 +931,7 @@ if allItemsMatch(stackOfStrings, arrayOfStrings) {
println(&quot;Not all items match.&quot;)
}
// 输出 &quot;All items match.&quot;
</code></pre><p> 上面的例子创建一个<code>Stack</code>单例来存储<code>String</code>,然后压了三个字符串进栈。这个例子也创建了一个<code>Array</code>单例,并初始化包含三个同栈里一样的原始字符串。即便栈和数组否是不同的类型,但们都遵循<code>Container</code>协议,而且们都包含同样的类型值。你因此可以调用<code>allItemsMatch</code>函数,用这两个容器作为它的参数。在上面的例子中,<code>allItemsMatch</code>函数正确的显示了所有的这两个容器的<code>items</code>匹配。</p>
</code></pre><p> 上面的例子创建一个<code>Stack</code>单例来存储<code>String</code>,然后压了三个字符串进栈。这个例子也创建了一个<code>Array</code>单例,并初始化包含三个同栈里一样的原始字符串。即便栈和数组否是不同的类型,但们都遵循<code>Container</code>协议,而且们都包含同样的类型值。你因此可以调用<code>allItemsMatch</code>函数,用这两个容器作为它的参数。在上面的例子中,<code>allItemsMatch</code>函数正确的显示了所有的这两个容器的<code>items</code>匹配。</p>
</section>

12
chapter2/23_Advanced_Operators.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2.23" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2.23" 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,7 +587,7 @@
<div class="page-inner">
<section class="normal" id="section-gitbook_131">
<section class="normal" id="section-gitbook_78">
<blockquote>
<p>翻译xielingwang</p>
@ -855,7 +855,7 @@ let afterIncrement = ++toIncrement
return !(left == right)
}
</code></pre>
<p>上述代码实现了相等运算符<code>==</code>来判断两个<code>Vector2D</code>对象是否有相等的值,相等的概念就是们有相同的<code>x</code>值和相同的<code>y</code>值,我们就用这个逻辑来实现。接着使用<code>==</code>的结果实现了不相等运算符<code>!=</code></p>
<p>上述代码实现了相等运算符<code>==</code>来判断两个<code>Vector2D</code>对象是否有相等的值,相等的概念就是们有相同的<code>x</code>值和相同的<code>y</code>值,我们就用这个逻辑来实现。接着使用<code>==</code>的结果实现了不相等运算符<code>!=</code></p>
<p>现在我们可以使用这两个运算符来判断两个<code>Vector2D</code>对象是否相等。</p>
<pre><code class="lang-swift">let twoThree = Vector2D(x: 2.0, y: 3.0)
let anotherTwoThree = Vector2D(x: 2.0, y: 3.0)
@ -895,7 +895,7 @@ let secondVector = Vector2D(x: 3.0, y: 4.0)
let plusMinusVector = firstVector +- secondVector
// plusMinusVector 此时的值为 (4.0, -2.0)
</code></pre>
<p>这个运算符把两个向量的<code>x</code>相加,把向量的<code>y</code>相减。因为实际是属于加减运算,所以让它保持了和加法一样的结合性和优先级(<code>left</code><code>140</code>)。查阅完整的Swift默认结合性和优先级的设置请移步<a href="../chapter3/04_Expressions.html">表达式</a>;</p>
<p>这个运算符把两个向量的<code>x</code>相加,把向量的<code>y</code>相减。因为实际是属于加减运算,所以让它保持了和加法一样的结合性和优先级(<code>left</code><code>140</code>)。查阅完整的Swift默认结合性和优先级的设置请移步<a href="../chapter3/04_Expressions.html">表达式</a>;</p>
</section>

8
chapter2/chapter2.html Executable file → Normal file
View File

@ -46,7 +46,7 @@
<div class="book" data-level="2" data-basepath=".." data-revision="1402677669306">
<div class="book" data-level="2" 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 " data-progress="2.11" style="left: 18.42105263157895%;"></a>
<a href="../chapter2/12_Subscripts.html" title="附属脚本" class="chapter " data-progress="2.12" style="left: 21.05263157894737%;"></a>
<a href="../chapter2/12_Subscripts.html" title="下标脚本" class="chapter " data-progress="2.12" style="left: 21.05263157894737%;"></a>
<a href="../chapter2/13_Inheritance.html" title="继承" class="chapter " data-progress="2.13" style="left: 23.68421052631579%;"></a>
@ -587,7 +587,7 @@
<div class="page-inner">
<section class="normal" id="section-gitbook_133">
<section class="normal" id="section-gitbook_80">
<h1 id="swift-">Swift 教程</h1>
<p>本章介绍了 Swift 的各种特性及其使用方法,是全书的核心部分。</p>