翻译细节与 markdown 统一格式修改 (#779)

* 修正全角逗号、句号的使用

* 修正逗号使用

* 修正一处代码空格错误

* 修正斜体范围,引用的空格使用

* 修正示例代码错误

* 修正标点,修正示例代码

* 修正标点

* 修正标点

* 添加 Swift 3.1 的更新

* 修改 Swift 3.0.1 位置

* 添加 Swift 4.0.3 更新

* 添加 Swift 4.1 更新

* 修正示例代码

* 修正 markdown 引用语法,优化翻译语句

* 修正示例代码

* 修正标点使用,优化翻译语句

* 修正示例代码

* 修正示例代码

* 优化翻译语句,修正示例代码语法

* 更新示例代码以符合 Swift 4.1

* 优化 markdown 引用格式的使用

* 优化 markdown 行内代码块使用,代码块与正文使用空格分隔

* 人工校验 markdown 行内代码块使用

* 中英文空格分隔

* 移除行末空格

* 人工校验 markdown 行内代码块使用

* 修正 markdown 无序列表使用
This commit is contained in:
BqLin
2018-04-14 10:32:56 +08:00
committed by 安正超
parent 78e2e93ae0
commit c4e5f11769
48 changed files with 2414 additions and 2386 deletions

View File

@ -1,6 +1,6 @@
> 2016.9.23: 已经更新到 Swift 3.0。
# 3.0 更新说明
Swift 3.0 是自 Swift 开源以来第一个大的版本更新。从语言角度不兼容之前的 Swift 2.2 和 Swift 2.3 版本。Swift 3.0 的更新说明,大家可以查看[官方blog的说明](https://swift.org/blog/swift-3-0-released/),也可以关注 [SwiftGG](http://swift.gg) 最新的文章。学习官方文档,是掌握语言特性点的最佳途径,感谢翻译的小伙伴们为 Swift 社区所做贡献!
Swift 3.0 是自 Swift 开源以来第一个大的版本更新。从语言角度不兼容之前的 Swift 2.2 和 Swift 2.3 版本。Swift 3.0 的更新说明,大家可以查看[官方 blog 的说明](https://swift.org/blog/swift-3-0-released/),也可以关注 [SwiftGG](http://swift.gg) 最新的文章。学习官方文档,是掌握语言特性点的最佳途径,感谢翻译的小伙伴们为 Swift 社区所做贡献!
# 3.0 译者记录
相关[issue](https://github.com/numbbbbb/the-swift-programming-language-in-chinese/issues/628)

View File

@ -45,11 +45,11 @@
* [泛型参数](chapter3/09_Generic_Parameters_and_Arguments.md)
* [语法总结](chapter3/10_Summary_of_the_Grammar.md)
* 苹果官方Blog官方翻译
* 苹果官方 Blog 官方翻译
* [Access Control 权限控制的黑与白](chapter4/01_Access_Control.md)
* [造个类型不是梦-白话Swift类型创建](chapter4/02_Type_Custom.md)
* [WWDC里面的那个“大炮打气球”](chapter4/03_Ballons.md)
* [Swift与C语言指针友好合作](chapter4/04_Interacting_with_C_Pointers.md)
* [造个类型不是梦-白话 Swift 类型创建](chapter4/02_Type_Custom.md)
* [WWDC 里面的那个“大炮打气球”](chapter4/03_Ballons.md)
* [Swift 与 C 语言指针友好合作](chapter4/04_Interacting_with_C_Pointers.md)
* [引用类型和值类型的恩怨](chapter4/05_Value_and_Reference_Types.md)
* [访问控制和Protected](chapter4/06_Access_Control_and_Protected.md)
* [访问控制和 Protected](chapter4/06_Access_Control_and_Protected.md)
* [可选类型完美解决占位问题](chapter4/07_Optional_Case_Study.md)

View File

@ -8,10 +8,14 @@
> 2.0
> 翻译+校对:[xtymichael](https://github.com/xtymichael)
> 3.0 翻译+校对:[shanks](http://codebuild.me)2016-10-06
> 3.0.1 review : 2016-11-09
> 3.0
> 翻译+校对:[shanks](http://codebuild.me)2016-10-06
> 3.1 校对: [SketchK](https://github.com/SketchK) 2017-04-08
> 3.0.1
> review : 2016-11-09
> 3.1
> 校对: [SketchK](https://github.com/SketchK) 2017-04-08
> 4.0
> 翻译:[rain2540](https://github.com/rain2540) 2017-09-21
@ -23,7 +27,7 @@ Swift 是一种非常好的编写软件的方式,无论是手机,台式机
Swift 对于初学者来说也很友好。它是第一个既满足工业标准又像脚本语言一样充满表现力和趣味的系统编程语言。它支持代码预览(playgrounds),这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。
Swift通过采用现代编程模式来避免大量常见编程错误
Swift 通过采用现代编程模式来避免大量常见编程错误:
* 变量始终在使用前初始化。
* 检查数组索引超出范围的错误。

View File

@ -10,7 +10,7 @@
本书描述的是 Swift 4.1,是 Xcode 9.2 中包含的默认版本。你可以用 Xcode 9.2 来构建用 Swift 4 或 Swift 3 写的项目。
> 注意
>
>
> 当 Swift 4 编译器编译 Swift 3 版本的代码时,它识别的语言版本为 3.2 版本。因此,你可以使用像 `#if swift(>=3.2)` 条件编译块来编写多版本编译器可以并存的代码。
当你用 Xcode 9.2 编译 Swift 3 的代码Swift 4 中大部分功能是可以使用的。也就是说,下面的功能仅仅是 Swift 4 的代码中可以使用:

View File

@ -10,13 +10,15 @@
> 翻译+校对:[xtymichael](https://github.com/xtymichael)
> 2.2
> 翻译:[175](https://github.com/Brian175)2016-04-09 校对:[SketchK](https://github.com/SketchK)2016-05-11
>
> 翻译:[175](https://github.com/Brian175)2016-04-09
> 校对:[SketchK](https://github.com/SketchK)2016-05-11
> 3.0
> 翻译+校对:[shanks](http://codebuild.me)2016-10-06
> 3.0.1 review: 2016-11-09
>
> 3.0.1
> review: 2016-11-09
> 3.1 校对: [SketchK](https://github.com/SketchK) 2017-04-08
> 4.0
@ -27,14 +29,14 @@
本页内容包括:
- [简单值Simple Values](#simple_values)
- [控制流Control Flow](#control_flow)
- [函数和闭包Functions and Closures](#functions_and_closures)
- [对象和类Objects and Classes](#objects_and_classes)
- [枚举和结构体Enumerations and Structures](#enumerations_and_structures)
- [协议和扩展Protocols and Extensions](#protocols_and_extensions)
- [错误处理Error Handling](#error_handling)
- [泛型Generics](#generics)
- [简单值Simple Values](#simple_values)
- [控制流Control Flow](#control_flow)
- [函数和闭包Functions and Closures](#functions_and_closures)
- [对象和类Objects and Classes](#objects_and_classes)
- [枚举和结构体Enumerations and Structures](#enumerations_and_structures)
- [协议和扩展Protocols and Extensions](#protocols_and_extensions)
- [错误处理Error Handling](#error_handling)
- [泛型Generics](#generics)
通常来说,编程语言教程中的第一个程序应该在屏幕上打印 “Hello, world”。在 Swift 中,可以用一行代码实现:
@ -48,8 +50,8 @@ print("Hello, world!")
> 注意
>
> 最好的体验是把这一章作为Playground文件在Xcode中打开。 Playgrounds允许你可以编辑代码并立刻看到输出结果。
>
> 最好的体验是把这一章作为 Playground 文件在 Xcode 中打开。 Playgrounds 允许你可以编辑代码并立刻看到输出结果。
>
> [Download Playground](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.playground.zip)
<a name="simple_values"></a>
@ -74,7 +76,7 @@ let explicitDouble: Double = 70
```
> 练习
>
>
> 创建一个常量,显式指定类型为 `Float` 并指定初始值为 4。
值永远不会被隐式转换为其他类型。如果你需要把一个值转换成其他类型,请显式转换。
@ -140,7 +142,7 @@ occupations = [:]
<a name="control_flow"></a>
## 控制流
使用 `if``switch` 来进行条件操作,使用 `for-in` `while``repeat-while` 来进行循环。包裹条件和循环变量的括号可以省略,但是语句体的大括号是必须的。
使用 `if``switch` 来进行条件操作,使用 `for-in``while``repeat-while` 来进行循环。包裹条件和循环变量的括号可以省略,但是语句体的大括号是必须的。
```swift
let individualScores = [75, 43, 103, 87, 12]
@ -157,7 +159,7 @@ print(teamScore)
`if` 语句中,条件必须是一个布尔表达式——这意味着像 `if score { ... }` 这样的代码将报错,而不会隐形地与 0 做对比。
你可以一起使用 `if``let` 一起来处理值缺失的情况。这些值可由可选值来代表。一个可选的值是一个具体的值或者是 `nil` 以表示值缺失。在类型后面加一个问号(`?ß`)来标记这个变量的值是可选的。
你可以一起使用 `if``let` 一起来处理值缺失的情况。这些值可由可选值来代表。一个可选的值是一个具体的值或者是 `nil` 以表示值缺失。在类型后面加一个问号(`?`)来标记这个变量的值是可选的。
```swift
var optionalString: String? = "Hello"
@ -713,7 +715,7 @@ print(protocolValue.simpleDescription)
// print(protocolValue.anotherProperty) // 去掉注释可以看到错误
```
即使 `protocolValue` 变量运行时的类型是 `simpleClass` ,编译器还是会把它的类型当做`ExampleProtocol`。这表示你不能调用在协议之外的方法或者属性。
即使 `protocolValue` 变量运行时的类型是 `simpleClass` ,编译器还是会把它的类型当做 `ExampleProtocol`。这表示你不能调用在协议之外的方法或者属性。
<a name="error_handling"></a>
## 错误处理

View File

@ -187,10 +187,10 @@
更新<a href="https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-ID48">自动引用计数</a>章节中关于 weak 和 unowned 引用的讨论。
</li>
<li>
增加<a href="https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID381">声明标识符</a>章节中关于新的标识符`unowned``unowend(safe)``unowned(unsafe)`的描述。
增加<a href="https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID381">声明标识符</a>章节中关于新的标识符 `unowned``unowend(safe)``unowned(unsafe)` 的描述。
</li>
<li>
增加<a href="https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html#//apple_ref/doc/uid/TP40014097-CH22-ID342">Any 和 AnyObject 的类型转换</a>一节中关于使用类型`Any`作为可选值的描述。
增加<a href="https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html#//apple_ref/doc/uid/TP40014097-CH22-ID342">Any 和 AnyObject 的类型转换</a>一节中关于使用类型 `Any` 作为可选值的描述。
</li>
<li>
更新<a href="https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/doc/uid/TP40014097-CH32-ID383">表达式</a>章节,把括号表达式和元组表达式的描述分开。
@ -450,11 +450,11 @@
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html#//apple_ref/doc/uid/TP40014097-CH12-ID145">枚举</a>一章的<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html#//apple_ref/doc/uid/TP40014097-CH12-ID536">递归枚举</a>一节和<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID351">声明</a>一章的<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID365">任意类型用例的枚举</a>一节中关于递归枚举的内容。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html#//apple_ref/doc/uid/TP40014097-CH9-ID120">控制流</a>一章中a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html#//apple_ref/doc/uid/TP40014097-CH9-ID523">检查 API 可用性</a>一节和<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/doc/uid/TP40014097-CH33-ID428">语句</a>一章中<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/doc/uid/TP40014097-CH33-ID522">可用性条件</a>一节中关于 API 可用性检查的内容。
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html#//apple_ref/doc/uid/TP40014097-CH9-ID120">控制流</a>一章中 a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html#//apple_ref/doc/uid/TP40014097-CH9-ID523">检查 API 可用性</a>一节和<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/doc/uid/TP40014097-CH33-ID428">语句</a>一章中<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/doc/uid/TP40014097-CH33-ID522">可用性条件</a>一节中关于 API 可用性检查的内容。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html#//apple_ref/doc/uid/TP40014097-CH9-ID120">控制流</a>一章的<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html#//apple_ref/doc/uid/TP40014097-CH9-ID525">早期退出</a>一节和<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/doc/uid/TP40014097-CH33-ID428">语句</a>一章的<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/doc/uid/TP40014097-CH33-ID524">guard语句</a>中关于新 <code>guard</code> 语句的内容。
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html#//apple_ref/doc/uid/TP40014097-CH9-ID120">控制流</a>一章的<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html#//apple_ref/doc/uid/TP40014097-CH9-ID525">早期退出</a>一节和<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/doc/uid/TP40014097-CH33-ID428">语句</a>一章的<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/doc/uid/TP40014097-CH33-ID524">guard 语句</a>中关于新 <code>guard</code> 语句的内容。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID267">协议</a>一章中<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID521">协议扩展</a>一节中关于协议扩展的内容。
@ -577,7 +577,7 @@
更新至 Swift 1.2。
</li>
<li>
Swift现在自身提供了一个<code>Set</code>集合类型,更多信息请看<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html#//apple_ref/doc/uid/TP40014097-CH8-ID484">集合</a>
Swift 现在自身提供了一个<code>Set</code>集合类型,更多信息请看<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html#//apple_ref/doc/uid/TP40014097-CH8-ID484">集合</a>
</li>
<li>
@ -587,7 +587,7 @@
对于类型属性和方法现在可以使用<code>static</code>关键字作为声明描述符,更多信息,请看<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID483">类型变量属性</a>
</li>
<li>
Swift现在包含一个<code>as?</code><code>as!</code>的向下可失败类型转换运算符。更多信息,请看<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID283">协议遵循性检查</a>
Swift 现在包含一个<code>as?</code><code>as!</code>的向下可失败类型转换运算符。更多信息,请看<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID283">协议遵循性检查</a>
</li>
<li>
增加了一个新的指导章节,它是关于<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-ID495">字符串索引</a>
@ -599,7 +599,7 @@
更新了常量和常量属性在声明和构造时的规则,更多信息,请看<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID355">常量声明</a>
</li>
<li>
更新了字符串字面量中Unicode标量集的定义请看<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-ID295">字符串字面量中的特殊字符</a>
更新了字符串字面量中 Unicode 标量集的定义,请看<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-ID295">字符串字面量中的特殊字符</a>
</li>
<li>
更新了<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-ID73">区间运算符</a>章节来提示当半开区间运算符含有相同的起止索引时,其区间为空。
@ -617,7 +617,7 @@
更新了<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-ID58">捕获列表</a>章节来澄清对于闭包捕获列表中的弱引用和无主引用的使用语法。
</li>
<li>
更新了<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-ID418">运算符</a>章节来明确指明一些例子来说明自定义运算符所支持的特性如数学运算符各种符号Unicode符号块等
更新了<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-ID418">运算符</a>章节来明确指明一些例子来说明自定义运算符所支持的特性如数学运算符各种符号Unicode 符号块等
</li>
<li>
在函数作用域中的常量声明时可以不被初始化,它必须在第一次使用前被赋值。更多的信息,请看<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID355">常量声明</a>
@ -673,7 +673,7 @@
带有原始值的枚举类型增加了一个<code>rawValue</code>属性替代<code>toRaw()</code>方法,同时使用了一个以<code>rawValue</code>为参数的失败构造器来替代<code>fromRaw()</code>方法。更多的信息,请看<a href="http://developer.apple.com/library/etc/redirect/xcode/devtools/419f35/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html">原始值(Raw Values)</a><a href="http://developer.apple.com/library/etc/redirect/xcode/devtools/419f35/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html">带原始值的枚举类型(Enumerations with Cases of a Raw-Value Type)</a>部分。
</li>
<li>
自定义运算符现在可以包含`?`字符,更新的<a href="http://developer.apple.com/library/etc/redirect/xcode/devtools/419f35/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html">运算符(Operators)</a>章节描述了改进后的规则,并且从<a href="http://developer.apple.com/library/etc/redirect/xcode/devtools/419f35/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html">自定义运算符(Custom Operators)</a>章节删除了重复的运算符有效字符集合
自定义运算符现在可以包含 `?` 字符,更新的<a href="http://developer.apple.com/library/etc/redirect/xcode/devtools/419f35/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html">运算符(Operators)</a>章节描述了改进后的规则,并且从<a href="http://developer.apple.com/library/etc/redirect/xcode/devtools/419f35/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html">自定义运算符(Custom Operators)</a>章节删除了重复的运算符有效字符集合
</li>
</ul>
@ -697,7 +697,7 @@
<td scope="row">2014-08-18</td>
<td><ul class="list-bullet">
<li>
发布新的文档用以详述 Swift 1.0苹果公司针对iOSOS X应用的全新开发语言。
发布新的文档用以详述 Swift 1.0,苹果公司针对 iOSOS X 应用的全新开发语言。
</li>
<li>
在章节协议中,增加新的小节:<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-XID_397">对构造器的规定Initializer Requirements</a>
@ -715,10 +715,10 @@
<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Attributes.html#//apple_ref/doc/uid/TP40014097-CH35-XID_516">声明特性Declaration Attributes</a>章节增加了关于<code>availability</code>特性的一些信息
</li>
<li>
<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_478">可选类型Optionals</a> 若有值时,不再隐式的转换为 <code>true</code>,同样,若无值时,也不再隐式的转换为 <code>false</code>,这是为了避免在判别 optional <code>Bool</code> 的值时产生困惑。 替代的方案是,用<code>==</code><code>!=</code> 运算符显式地去判断Optinal是否是 <code>nil</code>,以确认其是否包含值。
<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-XID_478">可选类型Optionals</a> 若有值时,不再隐式的转换为 <code>true</code>,同样,若无值时,也不再隐式的转换为 <code>false</code>,这是为了避免在判别 optional <code>Bool</code> 的值时产生困惑。 替代的方案是,用<code>==</code><code>!=</code> 运算符显式地去判断 Optinal 是否是 <code>nil</code>,以确认其是否包含值。
</li>
<li>
Swift新增了一个 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-XID_124" data-id="//apple_ref/doc/uid/TP40014097-CH6-XID_124">Nil合并运算符Nil Coalescing Operator</a> (<code>a ?? b</code>), 该表达式中如果Optional <code>a</code>的值存在则取得它并返回若Optional <code>a</code><code>nil</code>,则返回默认值 <code>b</code>
Swift 新增了一个 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-XID_124" data-id="//apple_ref/doc/uid/TP40014097-CH6-XID_124">Nil 合并运算符Nil Coalescing Operator</a> (<code>a ?? b</code>), 该表达式中,如果 Optional <code>a</code>的值存在,则取得它并返回,若 Optional <code>a</code><code>nil</code>,则返回默认值 <code>b</code>
</li>
<li>
更新和扩展 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-XID_434">字符串的比较Comparing Strings</a> 章节,用以反映和展示'字符串和字符的比较',以及'前缀prefix/后缀(postfix)比较'都开始基于扩展字符集(extended grapheme clusters)规范的等价比较。
@ -736,13 +736,13 @@
添加了一个提示:在 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-XID_126">范围运算符Range Operators</a>中,比如, <code>a...b</code><code>a..&lt;b</code> ,起始值<code>a</code>不能大于结束值<code>b</code>.
</li>
<li>
重写了<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Inheritance.html#//apple_ref/doc/uid/TP40014097-CH17-XID_293">继承Inheritance</a> 这一章删除了本章中关于构造器重写的介绍性报道转而将更多的注意力放到新增的部分——子类的新功能以及如何通过重写overrides修改已有的功能。另外小节 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Inheritance.html#//apple_ref/doc/uid/TP40014097-CH17-XID_301">重写属性的GettersSettersOverriding Property Getters and Setters</a> 中的例子已经被替换为展示如何重写一个 <code>description</code> 属性。 (而关于如何在子类的构造器中修改继承属性的默认值的例子,已经被移到 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Inheritance.html#//apple_ref/doc/uid/TP40014097-CH17-XID_293">构造过程Initialization</a> 这一章。)
重写了<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Inheritance.html#//apple_ref/doc/uid/TP40014097-CH17-XID_293">继承Inheritance</a> 这一章删除了本章中关于构造器重写的介绍性报道转而将更多的注意力放到新增的部分——子类的新功能以及如何通过重写overrides修改已有的功能。另外小节 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Inheritance.html#//apple_ref/doc/uid/TP40014097-CH17-XID_301">重写属性的 GettersSettersOverriding Property Getters and Setters</a> 中的例子已经被替换为展示如何重写一个 <code>description</code> 属性。 (而关于如何在子类的构造器中修改继承属性的默认值的例子,已经被移到 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Inheritance.html#//apple_ref/doc/uid/TP40014097-CH17-XID_293">构造过程Initialization</a> 这一章。)
</li>
<li>
更新了 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_331">构造器的继承与重写Initializer Inheritance and Overriding</a> 小节以标示: 重写一个特定的构造器必须使用 <code>override</code> 修饰符。
</li>
<li>
更新 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_339"> Required构造器Required Initializers</a> 小节以标示:<code>required</code> 修饰符现在需要出现在所有子类的required构造器的声明中而required构造器的实现现在可以仅从父类自动继承。
更新 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_339"> Required 构造器Required Initializers</a> 小节以标示:<code>required</code> 修饰符现在需要出现在所有子类的 required 构造器的声明中,而 required 构造器的实现,现在可以仅从父类自动继承。
</li>
<li>
中置Infix<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_80">运算符函数Operator Functions</a> 不再需要<code>@infix</code> 属性。
@ -751,7 +751,7 @@
<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/RevisionHistory.html#//apple_ref/doc/uid/TP40014097-CH40-XID_1631">前置和后置运算符(Prefix and Postfix Operators)</a><code>@prefix</code><code>@postfix</code> 属性,已变更为 <code>prefix</code><code>postfix</code> 声明修饰符declaration modifiers
</li>
<li>
增加一条注解当Prefixpostfix运算符被作用于同一个操作数时关于<a href="AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_81" data-id="//apple_ref/doc/uid/TP40014097-CH27-XID_81">前置和后置运算符(Prefix and Postfix Operators)</a>的顺序(postfix运算符会先被执行)
增加一条注解:当 Prefixpostfix 运算符被作用于同一个操作数时,关于<a href="AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_81" data-id="//apple_ref/doc/uid/TP40014097-CH27-XID_81">前置和后置运算符(Prefix and Postfix Operators)</a>的顺序(postfix 运算符会先被执行)
</li>
<li>
在运算符函数Operator functions <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_82" data-id="//apple_ref/doc/uid/TP40014097-CH27-XID_82">组合赋值运算符Compound Assignment Operators</a> 不再使用 <code>@assignment</code> 属性来定义函数。
@ -763,7 +763,7 @@
增加信息:关于<code>dynamic</code> 声明修饰符declaration modifier于章节 <a href="Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-XID_705" data-id="//apple_ref/doc/uid/TP40014097-CH34-XID_705">声明修饰符Declaration Modifiers</a>.
</li>
<li>
增加信息:<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-XID_886">字面量Literals</a> 的类型推导type inference
增加信息:<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-XID_886">字面量 Literals</a> 的类型推导type inference
</li>
<li>
为章节<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-XID_597">Curried Functions</a>添加了更多的信息。
@ -772,22 +772,22 @@
加入新的章节 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AccessControl.html#//apple_ref/doc/uid/TP40014097-CH41-XID_29">权限控制Access Control</a>.
</li>
<li>
更新了章节 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-XID_413">字符串和字符Strings and Characters</a> 用以表明在Swift中<code>Character</code> 类型现在代表的是扩展字符集(extended grapheme cluster)中的一个Unicode为此新增了小节 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-XID_431">Extended Grapheme Clusters</a> 。同时,为小节 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-XID_428">Unicode标量Unicode Scalars</a><a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-XID_434">字符串比较Comparing Strings</a>增加了更多内容。
更新了章节 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-XID_413">字符串和字符Strings and Characters</a> 用以表明,在 Swift 中,<code>Character</code> 类型现在代表的是扩展字符集(extended grapheme cluster)中的一个 Unicode为此新增了小节 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-XID_431">Extended Grapheme Clusters</a> 。同时,为小节 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-XID_428">Unicode 标量Unicode Scalars</a><a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-XID_434">字符串比较Comparing Strings</a>增加了更多内容。
</li>
<li>
更新章节<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-XID_856">字符串字面量String Literals</a>在一个字符串中Unicode标量Unicode scalars<code>\u{n}</code>的形式来表示,<code>n</code> 是一个最大可以有8位的16进制数hexadecimal digits
更新章节<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-XID_856">字符串字面量String Literals</a>在一个字符串中Unicode 标量Unicode scalars<code>\u{n}</code>的形式来表示,<code>n</code> 是一个最大可以有8位的16进制数hexadecimal digits
</li>
<li>
<code>NSString</code> <code>length</code> 属性已被映射到Swift的内建 <code>String</code>类型。(注意,这两属性的类型是<code>utf16Count</code>,而非 <code>utf16count</code>)。
<code>NSString</code> <code>length</code> 属性已被映射到 Swift 的内建 <code>String</code>类型。(注意,这两属性的类型是<code>utf16Count</code>,而非 <code>utf16count</code>)。
</li>
<li>
Swift的内建 <code>String</code> 类型不再拥有 <code>uppercaseString</code><code>lowercaseString</code> 属性。其对应部分在章节 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-XID_413">字符串和字符Strings and Characters</a>已经被删除,并且各种对应的代码用例也已被更新。
Swift 的内建 <code>String</code> 类型不再拥有 <code>uppercaseString</code><code>lowercaseString</code> 属性。其对应部分在章节 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-XID_413">字符串和字符Strings and Characters</a>已经被删除,并且各种对应的代码用例也已被更新。
</li>
<li>
加入新的章节 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_315">没有外部名的构造器参数Initializer Parameters Without External Names</a>.
</li>
<li>
加入新的章节 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_339"> Required构造器Required Initializers</a>.
加入新的章节 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_339"> Required 构造器Required Initializers</a>.
</li>
<li>
加入新的章节 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html#//apple_ref/doc/uid/TP40014097-CH10-XID_252">可选元祖(函数)返回类型 Optional Tuple Return Types</a>.
@ -802,7 +802,7 @@
更新整本书 —— 引用 <code>..&lt;</code> 作为<a href="BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-XID_128" data-id="//apple_ref/doc/uid/TP40014097-CH6-XID_128">区间运算符Half-Open Range Operator</a> (取代原先的<code>..</code> ).
</li>
<li>
更新了小节 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html#//apple_ref/doc/uid/TP40014097-CH8-XID_185">读取和修改字典Accessing and Modifying a Dictionary</a> <code>Dictionary</code> 现在早呢更加了一个 Boolean型的属性 <code>isEmpty</code>
更新了小节 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html#//apple_ref/doc/uid/TP40014097-CH8-XID_185">读取和修改字典Accessing and Modifying a Dictionary</a> <code>Dictionary</code> 现在早呢更加了一个 Boolean 型的属性: <code>isEmpty</code>
</li>
<li>
解释了哪些字符(集)可被用来定义<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-XID_85">自定义操作符 Custom Operators</a>
@ -811,7 +811,7 @@
<code>nil</code> 和布尔运算中的 <code>true</code><code>false</code> 现在被定义为字面量<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-XID_886">Literals</a>.
</li>
<li>
Swift 中的数组 <code>Array</code> 类型从现在起具备了完整的值语义。具体信息被更新到 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html#//apple_ref/doc/uid/TP40014097-CH8-XID_170">集合的可变性Mutability of Collections</a><a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html#//apple_ref/doc/uid/TP40014097-CH8-XID_172">数组Arrays</a> 两小节,以反映这个新的变化。 此外,还解释了如何 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097-CH13-XID_150">给Strings, ArraysDictionaries进行赋值和拷贝 Assignment and Copy Behavior for Strings, Arrays, and Dictionaries</a>.
Swift 中的数组 <code>Array</code> 类型从现在起具备了完整的值语义。具体信息被更新到 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html#//apple_ref/doc/uid/TP40014097-CH8-XID_170">集合的可变性Mutability of Collections</a><a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html#//apple_ref/doc/uid/TP40014097-CH8-XID_172">数组Arrays</a> 两小节,以反映这个新的变化。 此外,还解释了如何 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html#//apple_ref/doc/uid/TP40014097-CH13-XID_150"> Strings, ArraysDictionaries 进行赋值和拷贝 Assignment and Copy Behavior for Strings, Arrays, and Dictionaries</a>.
</li>
<li>
<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html#//apple_ref/doc/uid/TP40014097-CH8-XID_173">数组类型速记语法Array Type Shorthand Syntax</a><code>SomeType[]</code>.更新为<code>[SomeType]</code>

View File

@ -11,20 +11,20 @@
> 2.1
> 翻译:[Prayer](https://github.com/futantan)
> 校对:[shanks](http://codebuild.me)[overtrue](https://github.com/overtrue)
>
> 2.2
> 校对:[SketchK](https://github.com/SketchK)
>
> 3.0
> 校对:[CMB](https://github.com/chenmingbiao)版本时间2016-09-13
>
> 3.0.1, 2016-11-11shanks
> 4.0
> 校对:[kemchenj](https://kemchenj.github.io)
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
本页包含内容:
@ -58,7 +58,7 @@
Swift 是一门开发 iOS, macOS, watchOS 和 tvOS 应用的新语言。然而,如果你有 C 或者 Objective-C 开发经验的话,你会发现 Swift 的很多内容都是你熟悉的。
Swift 包含了 C 和 Objective-C 上所有基础数据类型,`Int`表示整型值; `Double``Float` 表示浮点型值; `Bool` 是布尔型值;`String` 是文本型数据。 Swift 还提供了三个基本的集合类型,`Array` `Set``Dictionary` ,详见[集合类型](./04_Collection_Types.html)。
Swift 包含了 C 和 Objective-C 上所有基础数据类型,`Int` 表示整型值; `Double``Float` 表示浮点型值; `Bool` 是布尔型值;`String` 是文本型数据。 Swift 还提供了三个基本的集合类型,`Array` `Set``Dictionary` ,详见[集合类型](./04_Collection_Types.html)。
就像 C 语言一样Swift 使用变量来进行存储并通过变量名来关联值。在 Swift 中,广泛的使用着值不可变的变量,它们就是常量,而且比 C 语言的常量更强大。在 Swift 中,如果你要处理的值不需要改变,那使用常量可以让你的代码更加安全并且更清晰地表达你的意图。
@ -95,7 +95,8 @@ var currentLoginAttempt = 0
var x = 0.0, y = 0.0, z = 0.0
```
> 注意
> 注意
>
> 如果你的代码中有不需要改变的值,请使用 `let` 关键字将它声明为常量。只将需要改变的值声明为变量。
<a name="type_annotations"></a>
@ -127,9 +128,9 @@ welcomeMessage = "Hello"
var red, green, blue: Double
```
> 注意
> 注意
>
> 一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值Swift可以推断出这个常量或者变量的类型请参考[类型安全和类型推断](#type_safety_and_type_inference)。在上面的例子中,没有给 `welcomeMessage` 赋初始值,所以变量 `welcomeMessage` 的类型是通过一个类型标注指定的,而不是通过初始值推断的。
> 一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值Swift 可以推断出这个常量或者变量的类型,请参考[类型安全和类型推断](#type_safety_and_type_inference)。在上面的例子中,没有给 `welcomeMessage` 赋初始值,所以变量 `welcomeMessage` 的类型是通过一个类型标注指定的,而不是通过初始值推断的。
<a name="naming"></a>
### 常量和变量的命名
@ -146,11 +147,11 @@ let 🐶🐮 = "dogcow"
一旦你将常量或者变量声明为确定的类型,你就不能使用相同的名字再次进行声明,或者改变其存储的值的类型。同时,你也不能将常量与变量进行互转。
> 注意
> 注意
>
> 如果你需要使用与Swift保留关键字相同的名称作为常量或者变量名你可以使用反引号`)将关键字包围的方式将其作为名字使用。无论如何,你应当避免使用关键字作为常量或变量名,除非你别无选择。
> 如果你需要使用与 Swift 保留关键字相同的名称作为常量或者变量名,你可以使用反引号(`)将关键字包围的方式将其作为名字使用。无论如何,你应当避免使用关键字作为常量或变量名,除非你别无选择。
你可以更改现有的变量值为其他同类型的值,在下面的例子中,`friendlyWelcome`的值从`"Hello!"`改为了`"Bonjour!"`:
你可以更改现有的变量值为其他同类型的值,在下面的例子中,`friendlyWelcome` 的值从 `"Hello!"` 改为了 `"Bonjour!"`:
```swift
var friendlyWelcome = "Hello!"
@ -169,7 +170,7 @@ languageName = "Swift++"
<a name="printing"></a>
### 输出常量和变量
你可以用`print(_:separator:terminator:)`函数来输出当前常量或变量的值:
你可以用 `print(_:separator:terminator:)` 函数来输出当前常量或变量的值:
```swift
print(friendlyWelcome)
@ -185,7 +186,7 @@ print("The current value of friendlyWelcome is \(friendlyWelcome)")
// 输出 "The current value of friendlyWelcome is Bonjour!
```
> 注意
> 注意
>
> 字符串插值所有可用的选项,请参考[字符串插值](./03_Strings_and_Characters.html#string_interpolation)。
@ -232,7 +233,7 @@ let cat = "🐱"; print(cat)
整数就是没有小数部分的数字,比如 `42``-23` 。整数可以是 `有符号`(正、负、零)或者 `无符号`(正、零)。
Swift 提供了81632和64位的有符号和无符号整数类型。这些整数类型和 C 语言的命名方式很像比如8位无符号整数类型是`UInt8`32位有符号整数类型是 `Int32` 。就像 Swift 的其他类型一样,整数类型采用大写命名法。
Swift 提供了81632和64位的有符号和无符号整数类型。这些整数类型和 C 语言的命名方式很像比如8位无符号整数类型是 `UInt8`32位有符号整数类型是 `Int32` 。就像 Swift 的其他类型一样,整数类型采用大写命名法。
<a name="integer_bounds"></a>
### 整数范围
@ -244,17 +245,17 @@ let minValue = UInt8.min // minValue 为 0是 UInt8 类型
let maxValue = UInt8.max // maxValue 为 255是 UInt8 类型
```
`min``max` 所传回值的类型,正是其所对的整数类型(如上例UInt8, 所传回的类型是UInt8),可用在表达式中相同类型值旁。
`min``max` 所传回值的类型,正是其所对的整数类型(如上例 UInt8, 所传回的类型是 UInt8),可用在表达式中相同类型值旁。
<a name="Int"></a>
### Int
一般来说你不需要专门指定整数的长度。Swift 提供了一个特殊的整数类型`Int`,长度与当前平台的原生字长相同:
一般来说你不需要专门指定整数的长度。Swift 提供了一个特殊的整数类型 `Int`,长度与当前平台的原生字长相同:
* 在32位平台上`Int``Int32` 长度相同。
* 在64位平台上`Int``Int64` 长度相同。
除非你需要特定长度的整数,一般来说使用 `Int` 就够了。这可以提高代码一致性和可复用性。即使是在32位平台上`Int` 可以存储的整数范围也可以达到 `-2,147,483,648` ~ `2,147,483,647` ,大多数时候这已经足够大了。
除非你需要特定长度的整数,一般来说使用 `Int` 就够了。这可以提高代码一致性和可复用性。即使是在32位平台上`Int` 可以存储的整数范围也可以达到 `-2,147,483,648` ~ `2,147,483,647`,大多数时候这已经足够大了。
<a name="UInt"></a>
### UInt
@ -264,9 +265,9 @@ Swift 也提供了一个特殊的无符号类型 `UInt`,长度与当前平台
* 在32位平台上`UInt``UInt32` 长度相同。
* 在64位平台上`UInt``UInt64` 长度相同。
> 注意
>
> 尽量不要使用`UInt`,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用`Int`,即使你要存储的值已知是非负的。统一使用`Int`可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断,请参考[类型安全和类型推断](#type_safety_and_type_inference)。
> 注意
>
> 尽量不要使用 `UInt`,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用 `Int`,即使你要存储的值已知是非负的。统一使用 `Int` 可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断,请参考[类型安全和类型推断](#type_safety_and_type_inference)。
<a name="floating-point_numbers"></a>
## 浮点数
@ -275,17 +276,17 @@ Swift 也提供了一个特殊的无符号类型 `UInt`,长度与当前平台
浮点类型比整数类型表示的范围更大,可以存储比 `Int` 类型更大或者更小的数字。Swift 提供了两种有符号浮点数类型:
* `Double`表示64位浮点数。当你需要存储很大或者很高精度的浮点数时请使用此类型。
* `Float`表示32位浮点数。精度要求不高的话可以使用此类型。
* `Double` 表示64位浮点数。当你需要存储很大或者很高精度的浮点数时请使用此类型。
* `Float` 表示32位浮点数。精度要求不高的话可以使用此类型。
> 注意
>
> `Double`精确度很高至少有15位数字而`Float`只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围在两种类型都匹配的情况下将优先选择 `Double`。
> 注意
>
> `Double` 精确度很高至少有15位数字 `Float` 只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围在两种类型都匹配的情况下将优先选择 `Double`。
<a name="type_safety_and_type_inference"></a>
## 类型安全和类型推断
Swift 是一个*类型安全type safe*的语言。类型安全的语言可以让你清楚地知道代码要处理的值的类型。如果你的代码需要一个`String`,你绝对不可能不小心传进去一个`Int`
Swift 是一个*类型安全type safe*的语言。类型安全的语言可以让你清楚地知道代码要处理的值的类型。如果你的代码需要一个 `String`,你绝对不可能不小心传进去一个 `Int`
由于 Swift 是类型安全的,所以它会在编译你的代码时进行*类型检查type checks*,并把不匹配的类型标记为错误。这可以让你在开发的时候尽早发现并修复错误。
@ -309,7 +310,7 @@ let pi = 3.14159
// pi 会被推测为 Double 类型
```
当推断浮点数的类型时Swift 总是会选择 `Double` 而不是`Float`
当推断浮点数的类型时Swift 总是会选择 `Double` 而不是 `Float`
如果表达式中同时出现了整数和浮点数,会被推断为 `Double` 类型:
@ -326,11 +327,11 @@ let anotherPi = 3 + 0.14159
整数字面量可以被写作:
* 一个*十进制*数,没有前缀
* 一个*二进制*数,前缀是`0b`
* 一个*八进制*数,前缀是`0o`
* 一个*十六进制*数,前缀是`0x`
* 一个*二进制*数,前缀是 `0b`
* 一个*八进制*数,前缀是 `0o`
* 一个*十六进制*数,前缀是 `0x`
下面的所有整数字面量的十进制值都是`17`:
下面的所有整数字面量的十进制值都是 `17`:
```swift
let decimalInteger = 17
@ -341,17 +342,17 @@ let hexadecimalInteger = 0x11 // 十六进制的17
浮点字面量可以是十进制(没有前缀)或者是十六进制(前缀是 `0x` 。小数点两边必须有至少一个十进制数字或者是十六进制的数字。十进制浮点数也可以有一个可选的指数exponent),通过大写或者小写的 `e` 来指定;十六进制浮点数必须有一个指数,通过大写或者小写的 `p` 来指定。
如果一个十进制数的指数为 `exp`那这个数相当于基数和10^exp的乘积
如果一个十进制数的指数为 `exp`那这个数相当于基数和10^exp 的乘积:
* `1.25e2` 表示 1.25 × 10^2等于 `125.0`
* `1.25e-2` 表示 1.25 × 10^-2等于 `0.0125`
如果一个十六进制数的指数为`exp`那这个数相当于基数和2^exp的乘积
如果一个十六进制数的指数为 `exp`那这个数相当于基数和2^exp 的乘积:
* `0xFp2` 表示 15 × 2^2等于 `60.0`
* `0xFp-2` 表示 15 × 2^-2等于 `3.75`
下面的这些浮点字面量都等于十进制的`12.1875`
下面的这些浮点字面量都等于十进制的 `12.1875`
```swift
let decimalDouble = 12.1875
@ -370,14 +371,14 @@ let justOverOneMillion = 1_000_000.000_000_1
<a name="numeric_type_conversion"></a>
## 数值型类型转换
通常来讲,即使代码中的整数常量和变量已知非负,也请使用`Int`类型。总是使用默认的整数类型可以保证你的整数常量和变量可以直接被复用并且可以匹配整数类字面量的类型推断。
通常来讲,即使代码中的整数常量和变量已知非负,也请使用 `Int` 类型。总是使用默认的整数类型可以保证你的整数常量和变量可以直接被复用并且可以匹配整数类字面量的类型推断。
只有在必要的时候才使用其他整数类型,比如要处理外部的长度明确的数据或者为了优化性能、内存占用等等。使用显式指定长度的类型可以及时发现值溢出并且可以暗示正在处理特殊数据。
<a name="integer_conversion"></a>
### 整数转换
不同整数类型的变量和常量可以存储不同范围的数字。`Int8`类型的常量或者变量可以存储的数字范围是`-128`~`127`,而`UInt8`类型的常量或者变量能存储的数字范围是`0`~`255`。如果数字超出了常量或者变量可存储的范围,编译的时候会报错:
不同整数类型的变量和常量可以存储不同范围的数字。`Int8` 类型的常量或者变量可以存储的数字范围是 `-128`~`127`,而 `UInt8` 类型的常量或者变量能存储的数字范围是 `0`~`255`。如果数字超出了常量或者变量可存储的范围,编译的时候会报错:
```swift
let cannotBeNegative: UInt8 = -1
@ -388,7 +389,7 @@ let tooBig: Int8 = Int8.max + 1
由于每种整数类型都可以存储不同范围的值,所以你必须根据不同情况选择性使用数值型类型转换。这种选择性使用的方式,可以预防隐式转换的错误并让你的代码中的类型转换意图变得清晰。
要将一种数字类型转换成另一种,你要用当前值来初始化一个期望类型的新数字,这个数字的类型就是你的目标类型。在下面的例子中,常量`twoThousand``UInt16`类型,然而常量`one``UInt8`类型。它们不能直接相加,因为它们类型不同。所以要调用`UInt16(one)`来创建一个新的`UInt16`数字并用`one`的值来初始化,然后使用这个新数字来计算:
要将一种数字类型转换成另一种,你要用当前值来初始化一个期望类型的新数字,这个数字的类型就是你的目标类型。在下面的例子中,常量 `twoThousand``UInt16` 类型,然而常量 `one``UInt8` 类型。它们不能直接相加,因为它们类型不同。所以要调用 `UInt16(one)` 来创建一个新的 `UInt16` 数字并用 `one` 的值来初始化,然后使用这个新数字来计算:
```swift
let twoThousand: UInt16 = 2_000
@ -398,7 +399,7 @@ let twoThousandAndOne = twoThousand + UInt16(one)
现在两个数字的类型都是 `UInt16`,可以进行相加。目标常量 `twoThousandAndOne` 的类型被推断为 `UInt16`,因为它是两个 `UInt16` 值的和。
`SomeType(ofInitialValue)` 是调用 Swift 构造器并传入一个初始值的默认方法。在语言内部,`UInt16` 有一个构造器,可以接受一个`UInt8`类型的值,所以这个构造器可以用现有的 `UInt8` 来创建一个新的 `UInt16`。注意,你并不能传入任意类型的值,只能传入 `UInt16` 内部有对应构造器的值。不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型),请参考[扩展](./20_Extensions.html)。
`SomeType(ofInitialValue)` 是调用 Swift 构造器并传入一个初始值的默认方法。在语言内部,`UInt16` 有一个构造器,可以接受一个 `UInt8` 类型的值,所以这个构造器可以用现有的 `UInt8` 来创建一个新的 `UInt16`。注意,你并不能传入任意类型的值,只能传入 `UInt16` 内部有对应构造器的值。不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型),请参考[扩展](./20_Extensions.html)。
<a name="integer_and_floating_point_conversion"></a>
### 整数和浮点数转换
@ -423,14 +424,14 @@ let integerPi = Int(pi)
当用这种方式来初始化一个新的整数值时,浮点值会被截断。也就是说 `4.75` 会变成 `4``-3.9` 会变成 `-3`
> 注意
>
> 结合数字类常量和变量不同于结合数字类字面量。字面量`3`可以直接和字面量`0.14159`相加,因为数字字面量本身没有明确的类型。它们的类型只在编译器需要求值的时候被推测。
> 注意
>
> 结合数字类常量和变量不同于结合数字类字面量。字面量 `3` 可以直接和字面量 `0.14159` 相加,因为数字字面量本身没有明确的类型。它们的类型只在编译器需要求值的时候被推测。
<a name="type_aliases"></a>
## 类型别名
*类型别名type aliases*就是给现有类型定义另一个名字。你可以使用`typealias`关键字来定义类型别名。
*类型别名type aliases*就是给现有类型定义另一个名字。你可以使用 `typealias` 关键字来定义类型别名。
当你想要给现有类型起一个更有意义的名字时,类型别名非常有用。假设你正在处理特定长度的外部资源的数据:
@ -445,12 +446,12 @@ var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound 现在是 0
```
本例中,`AudioSample`被定义为`UInt16`的一个别名。因为它是别名,`AudioSample.min`实际上是`UInt16.min`,所以会给`maxAmplitudeFound`赋一个初值`0`
本例中,`AudioSample` 被定义为 `UInt16` 的一个别名。因为它是别名,`AudioSample.min` 实际上是 `UInt16.min`,所以会给 `maxAmplitudeFound` 赋一个初值 `0`
<a name="booleans"></a>
## 布尔值
Swift 有一个基本的*布尔Boolean类型*,叫做`Bool`。布尔值指*逻辑*上的值因为它们只能是真或者假。Swift 有两个布尔常量,`true``false`
Swift 有一个基本的*布尔Boolean类型*,叫做 `Bool`。布尔值指*逻辑*上的值因为它们只能是真或者假。Swift 有两个布尔常量,`true``false`
```swift
let orangesAreOrange = true
@ -470,7 +471,7 @@ if turnipsAreDelicious {
// 输出 "Eww, turnips are horrible."
```
条件语句,例如`if`,请参考[控制流](./05_Control_Flow.html)。
条件语句,例如 `if`,请参考[控制流](./05_Control_Flow.html)。
如果你在需要使用 `Bool` 类型的地方使用了非布尔值Swift 的类型安全机制会报错。下面的例子会报告一个编译时错误:
@ -554,8 +555,8 @@ print("The status message is \(http200Status.description)")
作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个 `(Int, String)` 元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。请参考[函数参数与返回值](./06_Functions.html#Function_Parameters_and_Return_Values)。
> 注意
>
> 注意
>
> 元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。请参考[类和结构体](./09_Classes_and_Structures.html)。
<a name="optionals"></a>
@ -564,9 +565,9 @@ print("The status message is \(http200Status.description)")
使用*可选类型optionals*来处理值可能缺失的情况。可选类型表示两种可能:
或者有值, 你可以解析可选类型访问这个值, 或者根本没有值。
> 注意
>
> C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回`nil``nil`表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型Objective-C 方法一般会返回一个特殊值(比如`NSNotFound`来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而Swift 的可选类型可以让你暗示*任意类型*的值缺失,并不需要一个特殊值。
> 注意
>
> C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回 `nil``nil` 表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型Objective-C 方法一般会返回一个特殊值(比如 `NSNotFound`来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而Swift 的可选类型可以让你暗示*任意类型*的值缺失,并不需要一个特殊值。
来看一个例子。Swift 的 `Int` 类型有一种构造器,作用是将一个 `String` 值转换成一个 `Int` 值。然而,并不是所有的字符串都可以转换成一个整数。字符串 `"123"` 可以被转换成数字 `123` ,但是字符串 `"hello, world"` 不行。
@ -583,7 +584,7 @@ let convertedNumber = Int(possibleNumber)
<a name="nil"></a>
### nil
你可以给可选变量赋值为`nil`来表示它没有值:
你可以给可选变量赋值为 `nil` 来表示它没有值:
```swift
var serverResponseCode: Int? = 404
@ -592,9 +593,9 @@ serverResponseCode = nil
// serverResponseCode 现在不包含值
```
> 注意
>
> `nil`不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。
> 注意
>
> `nil` 不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。
如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为 `nil`
@ -603,8 +604,8 @@ var surveyAnswer: String?
// surveyAnswer 被自动设置为 nil
```
> 注意
>
> 注意
>
> Swift 的 `nil` 和 Objective-C 中的 `nil` 并不一样。在 Objective-C 中,`nil` 是一个指向不存在对象的指针。在 Swift 中,`nil` 不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为 `nil`,不只是对象类型。
<a name="if"></a>
@ -632,8 +633,8 @@ if convertedNumber != nil {
更多关于 `if` 语句的内容,请参考[控制流](./05_Control_Flow.html)。
> 注意
>
> 注意
>
> 使用 `!` 来获取一个不存在的可选值会导致运行时错误。使用 `!` 来强制解析值之前,一定要确定可选包含一个非 `nil` 的值。
<a name="optional_binding"></a>
@ -649,7 +650,7 @@ if let constantName = someOptional {
}
```
你可以像上面这样使用可选绑定来重写 在[可选类型](./01_The_Basics.html#optionals)举出的`possibleNumber`例子:
你可以像上面这样使用可选绑定来重写 在[可选类型](./01_The_Basics.html#optionals)举出的 `possibleNumber` 例子:
```swift
if let actualNumber = Int(possibleNumber) {
@ -666,9 +667,9 @@ if let actualNumber = Int(possibleNumber) {
如果转换成功,`actualNumber` 常量可以在 `if` 语句的第一个分支中使用。它已经被可选类型 *包含的* 值初始化过,所以不需要再使用 `!` 后缀来获取它的值。在这个例子中,`actualNumber` 只被用来输出转换结果。
你可以在可选绑定中使用常量和变量。如果你想在`if`语句的第一个分支中操作 `actualNumber` 的值,你可以改成 `if var actualNumber`,这样可选类型包含的值就会被赋给一个变量而非常量。
你可以在可选绑定中使用常量和变量。如果你想在 `if` 语句的第一个分支中操作 `actualNumber` 的值,你可以改成 `if var actualNumber`,这样可选类型包含的值就会被赋给一个变量而非常量。
你可以包含多个可选绑定或多个布尔条件在一个 `if` 语句中,只要使用逗号分开就行。只要有任意一个可选绑定的值为`nil`,或者任意一个布尔条件为`false`,则整个`if`条件判断为`false`,这时你就需要使用嵌套 `if` 条件语句来处理,如下所示:
你可以包含多个可选绑定或多个布尔条件在一个 `if` 语句中,只要使用逗号分开就行。只要有任意一个可选绑定的值为 `nil`,或者任意一个布尔条件为 `false`,则整个 `if` 条件判断为 `false`,这时你就需要使用嵌套 `if` 条件语句来处理,如下所示:
```swift
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
@ -686,8 +687,8 @@ if let firstNumber = Int("4") {
// 输出 "4 < 42 < 100"
```
> 注意
>
> 注意
>
> 在 `if` 条件语句中使用常量和变量来创建一个可选绑定,仅在 `if` 语句的句中(`body`)中才能获取到值。相反,在 `guard` 语句中使用常量和变量来创建一个可选绑定,仅在 `guard` 语句外且在语句后才能获取到值,请参考[提前退出](./05_Control_Flow.html#early_exit)。
<a name="implicityly_unwrapped_optionals"></a>
@ -713,8 +714,8 @@ let implicitString: String = assumedString // 不需要感叹号
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。
> 注意
>
> 注意
>
> 如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。
你仍然可以把隐式解析可选类型当做普通可选类型来判断它是否包含值:
@ -735,9 +736,9 @@ if let definiteString = assumedString {
// 输出 "An implicitly unwrapped optional string."
```
> 注意
>
> 如果一个变量之后可能变成`nil`的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是`nil`的话,请使用普通可选类型。
> 注意
>
> 如果一个变量之后可能变成 `nil` 的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是 `nil` 的话,请使用普通可选类型。
<a name="error_handling"></a>
## 错误处理
@ -754,7 +755,7 @@ func canThrowAnError() throws {
}
```
一个函数可以通过在声明中添加`throws`关键词来抛出错误消息。当你的函数能抛出错误消息时,你应该在表达式中前置`try`关键词。
一个函数可以通过在声明中添加 `throws` 关键词来抛出错误消息。当你的函数能抛出错误消息时,你应该在表达式中前置 `try` 关键词。
```swift
do {
@ -765,7 +766,7 @@ do {
}
```
一个`do`语句创建了一个新的包含作用域,使得错误能被传播到一个或多个`catch`从句。
一个 `do` 语句创建了一个新的包含作用域,使得错误能被传播到一个或多个 `catch` 从句。
这里有一个错误处理如何用来应对不同错误条件的例子。
@ -793,7 +794,7 @@ do {
<a name="assertions_and_Preconditions"></a>
## 断言和先决条件
断言和先决条件是在运行时所做的检查。你可以用他们来检查在执行后续代码之前是否一个必要的条件已经被满足了。如果断言或者先决条件中的布尔条件评估的结果为 true则代码像往常一样继续执行。如果布尔条件评估结果为false程序的当前状态是无效的则代码执行结束应用程序中止。
断言和先决条件是在运行时所做的检查。你可以用他们来检查在执行后续代码之前是否一个必要的条件已经被满足了。如果断言或者先决条件中的布尔条件评估的结果为 true则代码像往常一样继续执行。如果布尔条件评估结果为 false程序的当前状态是无效的则代码执行结束应用程序中止。
你使用断言和先决条件来表达你所做的假设和你在编码时候的期望。你可以将这些包含在你的代码中。断言帮助你在开发阶段找到错误和不正确的假设,先决条件帮助你在生产环境中探测到存在的问题。
@ -821,7 +822,7 @@ assert(age >= 0, "A person's age cannot be less than zero")
assert(age >= 0)
```
如果代码已经检查了条件,你可以使用 `assertionFailure(_:file:line:)`函数来表明断言失败了,例如:
如果代码已经检查了条件,你可以使用 `assertionFailure(_:file:line:)` 函数来表明断言失败了,例如:
```swift
if age > 10 {
@ -844,10 +845,10 @@ if age > 10 {
precondition(index > 0, "Index must be greater than zero.")
```
你可以调用 `precondition(_:_:file:line:)`方法来表明出现了一个错误例如switch 进入了 default 分支,但是所有的有效值应该被任意一个其他分支(非 default 分支)处理。
你可以调用 `precondition(_:_:file:line:)` 方法来表明出现了一个错误例如switch 进入了 default 分支,但是所有的有效值应该被任意一个其他分支(非 default 分支)处理。
> 注意
> 注意
>
> 如果你使用unchecked模式-Ounchecked编译代码先决条件将不会进行检查。编译器假设所有的先决条件总是为true他将优化你的代码。然而`fatalError(_:file:line:)`函数总是中断执行,无论你怎么进行优化设定。
> 如果你使用 unchecked 模式(-Ounchecked编译代码先决条件将不会进行检查。编译器假设所有的先决条件总是为 true他将优化你的代码。然而`fatalError(_:file:line:)` 函数总是中断执行,无论你怎么进行优化设定。
>
>你能使用 `fatalError(_:file:line:)`函数在设计原型和早期开发阶段这个阶段只有方法的声明但是没有具体实现你可以在方法体中写上fatalError("Unimplemented")作为具体实现。因为fatalError不会像断言和先决条件那样被优化掉所以你可以确保当代码执行到一个没有被实现的方法时程序会被中断。
> 你能使用 `fatalError(_:file:line:)` 函数在设计原型和早期开发阶段,这个阶段只有方法的声明,但是没有具体实现,你可以在方法体中写上 fatalError("Unimplemented")作为具体实现。因为 fatalError 不会像断言和先决条件那样被优化掉,所以你可以确保当代码执行到一个没有被实现的方法时,程序会被中断。

View File

@ -17,9 +17,9 @@
> 4.0
> 翻译+校对:[kemchenj](https://kemchenj.github.io)
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
本页包含内容:
@ -35,7 +35,7 @@
*运算符*是检查、改变、合并值的特殊符号或短语。例如,加号(`+`)将两个数相加(如 `let i = 1 + 2`)。更复杂的运算例子包括逻辑与运算符 `&&`(如 `if enteredDoorCode && passedRetinaScan`)。
Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减少常规编码错误。如:赋值符(`=`)不返回值,以防止把想要判断相等运算符(`==`)的地方写成赋值符导致的错误。算术运算符(`+``-``*``/``%`等)会检测并不允许值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见[溢出运算符](./26_Advanced_Operators.html#overflow_operators)。
Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减少常规编码错误。如:赋值符(`=`)不返回值,以防止把想要判断相等运算符(`==`)的地方写成赋值符导致的错误。算术运算符(`+``-``*``/``%` 等)会检测并不允许值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见[溢出运算符](./26_Advanced_Operators.html#overflow_operators)。
Swift 还提供了 C 语言没有的区间运算符,例如 `a..<b``a...b`,这方便我们表达一个区间内的数值。
@ -108,10 +108,10 @@ Swift 中所有数值类型都支持了基本的四则*算术运算符*
### 求余运算符
*求余运算符*`a % b`)是计算 `b` 的多少倍刚刚好可以容入`a`,返回多出来的那部分(余数)。
*求余运算符*`a % b`)是计算 `b` 的多少倍刚刚好可以容入 `a`,返回多出来的那部分(余数)。
> 注意
>
> 注意
>
> 求余运算符(`%`)在其他语言也叫*取模运算符*。但是严格说来,我们看该运算符对负数的操作结果,「求余」比「取模」更合适些。
我们来谈谈取余是怎么回事,计算 `9 % 4`,你先计算出 `4` 的多少倍会刚好可以容入 `9` 中:
@ -126,11 +126,11 @@ Swift 中所有数值类型都支持了基本的四则*算术运算符*
9 % 4 // 等于 1
```
为了得到 `a % b` 的结果,`%` 计算了以下等式,并输出`余数`作为结果:
为了得到 `a % b` 的结果,`%` 计算了以下等式,并输出 `余数`作为结果:
a = (b × 倍数) + 余数
`倍数`取最大值的时候,就会刚好可以容入 `a` 中。
`倍数`取最大值的时候,就会刚好可以容入 `a` 中。
`9``4` 代入等式中,我们得 `1`
@ -186,9 +186,9 @@ a += 2
表达式 `a += 2``a = a + 2` 的简写,一个组合加运算就是把加法运算和赋值运算组合成进一个运算符里,同时完成两个运算任务。
> 注意
>
> 复合赋值运算没有返回值,`let b = a += 2`这类代码是错误。这不同于上面提到的自增和自减运算符。
> 注意
>
> 复合赋值运算没有返回值,`let b = a += 2` 这类代码是错误。这不同于上面提到的自增和自减运算符。
更多 Swift 标准库运算符的信息,请看[运算符声明](https://developer.apple.com/documentation/swift/operator_declarations)。
@ -204,8 +204,8 @@ a += 2
- 大于等于(`a >= b`
- 小于等于(`a <= b`
> 注意
>
> 注意
>
> Swift 也提供恒等(`===`)和不恒等(`!==`)这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在[类与结构](./09_Classes_and_Structures.html)。
每个比较运算都返回了一个标识表达式是否成立的布尔值:
@ -219,7 +219,7 @@ a += 2
2 <= 1 // false, 因为 2 并不小于等于 1
```
比较运算多用于条件语句,如`if`条件:
比较运算多用于条件语句,如 `if` 条件:
```swift
let name = "world"
@ -241,7 +241,7 @@ if name == "world" {
(4, "dog") == (4, "dog") // true因为 4 等于 4dog 等于 dog
```
在上面的例子中,你可以看到,在第一行中从左到右的比较行为。因为`1`小于`2`,所以`(1, "zebra")`小于`(2, "apple")`,不管元组剩下的值如何。所以`"zebra"`大于`"apple"`对结果没有任何影响,因为元组的比较结果已经被第一个元素决定了。不过,当元组的第一个元素相同时候,第二个元素将会用作比较-第二行和第三行代码就发生了这样的比较。
在上面的例子中,你可以看到,在第一行中从左到右的比较行为。因为 `1` 小于 `2`,所以 `(1, "zebra")` 小于 `(2, "apple")`,不管元组剩下的值如何。所以 `"zebra"` 大于 `"apple"` 对结果没有任何影响,因为元组的比较结果已经被第一个元素决定了。不过,当元组的第一个元素相同时候,第二个元素将会用作比较-第二行和第三行代码就发生了这样的比较。
当元组中的元素都可以被比较时,你也可以使用这些运算符来比较它们的大小。例如,像下面展示的代码,你可以比较两个类型为 `(String, Int)` 的元组,因为 `Int``String` 类型的值可以比较。相反,`Bool` 不能被比较,也意味着存有布尔类型的元组不能被比较。
@ -250,9 +250,9 @@ if name == "world" {
("blue", false) < ("purple", true) // 错误,因为 < 不能比较布尔类型
```
>注意
>
>Swift 标准库只能比较七个以内元素的元组比较函数。如果你的元组元素超过七个时,你需要自己实现比较运算符。
> 注意
>
> Swift 标准库只能比较七个以内元素的元组比较函数。如果你的元组元素超过七个时,你需要自己实现比较运算符。
<a name="ternary_conditional_operator"></a>
## 三元运算符Ternary Conditional Operator
@ -309,8 +309,8 @@ a != nil ? a! : b
上述代码使用了三元运算符。当可选类型 `a` 的值不为空时,进行强制解封(`a!`),访问 `a` 中的值;反之返回默认值 `b`。无疑空合运算符(`??`)提供了一种更为优雅的方式去封装条件判断和解封两种行为,显得简洁以及更具可读性。
> 注意
>
> 注意
>
> 如果 `a` 为非空值(`non-nil`),那么值 `b` 将不会被计算。这也就是所谓的*短路求值*。
下文例子采用空合运算符,实现了在默认颜色名和可选自定义颜色名之间抉择:
@ -341,7 +341,7 @@ Swift 提供了几种方便表达一个区间的值的*区间运算符*。
### 闭区间运算符
*闭区间运算符*`a...b`)定义一个包含从 `a``b`(包括 `a``b`)的所有值的区间。`a` 的值不能超过 `b`
*闭区间运算符*`a...b`)定义一个包含从 `a``b`(包括 `a``b`)的所有值的区间。`a` 的值不能超过 `b`
闭区间运算符在迭代一个区间的所有值时是非常有用的,如在 `for-in` 循环中:
@ -356,7 +356,7 @@ for index in 1...5 {
// 5 * 5 = 25
```
关于 `for-in`循环,请看[控制流](./05_Control_Flow.html)。
关于 `for-in` 循环,请看[控制流](./05_Control_Flow.html)。
### 半开区间运算符
@ -502,8 +502,8 @@ if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
前两种情况,我们都不满足,所以前两个简单逻辑的结果是 `false`,但是我们是知道紧急情况下重置的密码的,所以整个复杂表达式的值还是 `true`
> 注意
>
> 注意
>
> Swift 逻辑操作符 `&&` 和 `||` 是左结合的,这意味着拥有多元逻辑操作符的复合表达式优先计算最左边的子表达式。
### 使用括号来明确优先级

View File

@ -42,19 +42,19 @@
- [比较字符串](#comparing_strings)
- [字符串的 Unicode 表示形式](#unicode_representations_of_strings)
*字符串*是是一系列字符的集合,例如`"hello, world"``"albatross"`。Swift的字符串通过`String`类型来表示。
一个`String`的内容可以用许多方式读取,包括作为一个`Character`值的集合。
*字符串*是是一系列字符的集合,例如 `"hello, world"``"albatross"`。Swift 的字符串通过 `String` 类型来表示。
一个 `String` 的内容可以用许多方式读取,包括作为一个 `Character` 值的集合。
Swift 的`String``Character`类型提供了快速和兼容 Unicode 的方式供你的代码使用。创建和操作字符串的语法与 C 语言中字符串操作相似,轻量并且易读。字符串连接操作只需要简单地通过`+`符号将两个字符串相连即可。与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。你也可以在字符串内插过程中使用字符串插入常量、变量、字面量表达成更长的字符串,这样可以很容易的创建自定义的字符串值,进行展示、存储以及打印。
Swift 的 `String``Character` 类型提供了快速和兼容 Unicode 的方式供你的代码使用。创建和操作字符串的语法与 C 语言中字符串操作相似,轻量并且易读。字符串连接操作只需要简单地通过 `+` 符号将两个字符串相连即可。与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。你也可以在字符串内插过程中使用字符串插入常量、变量、字面量表达成更长的字符串,这样可以很容易的创建自定义的字符串值,进行展示、存储以及打印。
尽管语法简易,但`String`类型是一种快速、现代化的字符串实现。
尽管语法简易,但 `String` 类型是一种快速、现代化的字符串实现。
每一个字符串都是由编码无关的 Unicode 字符组成,并支持访问字符的多种 Unicode 表示形式。
> 注意
>
> Swift 的`String`类型与 Foundation `NSString`类进行了无缝桥接。Foundation也可以对`String`进行扩展,暴露在`NSString`中定义的方法。 这意味着,如果你在`String`中调用这些`NSString`的方法,将不用进行转换。
> Swift 的 `String` 类型与 Foundation `NSString` 类进行了无缝桥接。Foundation 也可以对 `String` 进行扩展,暴露在 `NSString` 中定义的方法。 这意味着,如果你在 `String` 中调用这些 `NSString` 的方法,将不用进行转换。
>
> 更多关于在 Foundation 和 Cocoa 中使用`String`的信息请查看 *[Using Swift with Cocoa and Objective-C (Swift 4)](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)*。
> 更多关于在 Foundation 和 Cocoa 中使用 `String` 的信息请查看 *[Using Swift with Cocoa and Objective-C (Swift 4)](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)*。
<a name="string_literals"></a>
## 字符串字面量
@ -67,7 +67,7 @@ Swift 的`String`和`Character`类型提供了快速和兼容 Unicode 的方式
let someString = "Some string literal value"
```
注意`someString`常量通过字符串字面量进行初始化Swift 会推断该常量为`String`类型。
注意 `someString` 常量通过字符串字面量进行初始化Swift 会推断该常量为 `String` 类型。
<a name="multiline_string_literals"></a>
### 多行字符串字面量
@ -116,7 +116,7 @@ It also ends with a line break.
"""
```
一个多行字符串字面量能够缩进来匹配周围的代码。关闭引号(`"""`)之前的空白字符串告诉Swift编译器其他各行多少空白字符串需要忽略。然而如果你在某行的前面写的空白字符串超出了关闭引号(`"""`)之前的空白字符串,则超出部分将被包含在多行字符串字面量中。
一个多行字符串字面量能够缩进来匹配周围的代码。关闭引号(`"""`)之前的空白字符串告诉 Swift 编译器其他各行多少空白字符串需要忽略。然而,如果你在某行的前面写的空白字符串超出了关闭引号(`"""`)之前的空白字符串,则超出部分将被包含在多行字符串字面量中。
![](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Art/multilineStringWhitespace_2x.png)
@ -127,12 +127,12 @@ It also ends with a line break.
字符串字面量可以包含以下特殊字符:
* 转义字符`\0`(空字符)、`\\`(反斜线)、`\t`(水平制表符)、`\n`(换行符)、`\r`(回车符)、`\"`(双引号)、`\'`(单引号)。
* Unicode 标量,写成`\u{n}`(u为小写),其中`n`为任意一到八位十六进制数且可用的 Unicode 位码。
* 转义字符 `\0`(空字符)、`\\`(反斜线)、`\t`(水平制表符)、`\n`(换行符)、`\r`(回车符)、`\"`(双引号)、`\'`(单引号)。
* Unicode 标量,写成 `\u{n}`(u 为小写),其中 `n` 为任意一到八位十六进制数且可用的 Unicode 位码。
下面的代码为各种特殊字符的使用示例。
`wiseWords`常量包含了两个双引号。
`dollarSign``blackHeart``sparklingHeart`常量演示了三种不同格式的 Unicode 标量:
`wiseWords` 常量包含了两个双引号。
`dollarSign``blackHeart``sparklingHeart` 常量演示了三种不同格式的 Unicode 标量:
```swift
let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
@ -154,7 +154,7 @@ Escaping all three quotes \"\"\"
<a name="initializing_an_empty_string"></a>
## 初始化空字符串
要创建一个空字符串作为初始值,可以将空的字符串字面量赋值给变量,也可以初始化一个新的`String`实例:
要创建一个空字符串作为初始值,可以将空的字符串字面量赋值给变量,也可以初始化一个新的 `String` 实例:
```swift
var emptyString = "" // 空字符串字面量
@ -162,7 +162,7 @@ var anotherEmptyString = String() // 初始化方法
// 两个字符串均为空并等价。
```
您可以通过检查其`Bool`类型的`isEmpty`属性来判断该字符串是否为空:
您可以通过检查其 `Bool` 类型的 `isEmpty` 属性来判断该字符串是否为空:
```swift
if emptyString.isEmpty {
@ -188,12 +188,12 @@ constantString += " and another Highlander"
> 注意
>
> 在 Objective-C 和 Cocoa 中,您需要通过选择两个不同的类(`NSString``NSMutableString`)来指定字符串是否可以被修改。
> 在 Objective-C 和 Cocoa 中,您需要通过选择两个不同的类(`NSString``NSMutableString`)来指定字符串是否可以被修改。
<a name="strings_are_value_types"></a>
## 字符串是值类型
Swift 的`String`类型是*值类型*。
Swift 的 `String` 类型是*值类型*。
如果您创建了一个新的字符串,那么当其进行常量、变量赋值操作,或在函数/方法中传递时,会进行值拷贝。
任何情况下,都会对已有字符串值创建新副本,并对该新副本进行传递或赋值操作。
值类型在 [结构体和枚举是值类型](./09_Classes_and_Structures.html#structures_and_enumerations_are_value_types) 中进行了详细描述。
@ -207,7 +207,7 @@ Swift 默认字符串拷贝的方式保证了在函数/方法中传递的是字
<a name="working_with_characters"></a>
## 使用字符
您可通过`for-in`循环来遍历字符串,获取字符串中每一个字符的值:
您可通过 `for-in` 循环来遍历字符串,获取字符串中每一个字符的值:
```swift
for character in "Dog!🐶" {
@ -220,14 +220,14 @@ for character in "Dog!🐶" {
// 🐶
```
`for-in`循环在 [For 循环](./05_Control_Flow.html#for_loops) 中进行了详细描述。
`for-in` 循环在 [For 循环](./05_Control_Flow.html#for_loops) 中进行了详细描述。
另外,通过标明一个`Character`类型并用字符字面量进行赋值,可以建立一个独立的字符常量或变量:
另外,通过标明一个 `Character` 类型并用字符字面量进行赋值,可以建立一个独立的字符常量或变量:
```swift
let exclamationMark: Character = "!"
```
字符串可以通过传递一个值类型为`Character`的数组作为自变量来初始化:
字符串可以通过传递一个值类型为 `Character` 的数组作为自变量来初始化:
```swift
let catCharacters: [Character] = ["C", "a", "t", "!", "🐱"]
@ -256,7 +256,7 @@ instruction += string2
// instruction 现在等于 "look over there"
```
您可以用`append()`方法将一个字符附加到一个字符串变量的尾部:
您可以用 `append()` 方法将一个字符附加到一个字符串变量的尾部:
```swift
let exclamationMark: Character = "!"
@ -309,12 +309,12 @@ let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message 是 "3 times 2.5 is 7.5"
```
在上面的例子中,`multiplier`作为`\(multiplier)`被插入到一个字符串常量量中。
当创建字符串执行插值计算时此占位符会被替换为`multiplier`实际的值。
在上面的例子中,`multiplier` 作为 `\(multiplier)` 被插入到一个字符串常量量中。
当创建字符串执行插值计算时此占位符会被替换为 `multiplier` 实际的值。
`multiplier`的值也作为字符串中后面表达式的一部分。
该表达式计算`Double(multiplier) * 2.5`的值并将结果 (`7.5`) 插入到字符串中。
在这个例子中,表达式写为`\(Double(multiplier) * 2.5)`并包含在字符串字面量中。
`multiplier` 的值也作为字符串中后面表达式的一部分。
该表达式计算 `Double(multiplier) * 2.5` 的值并将结果 (`7.5`) 插入到字符串中。
在这个例子中,表达式写为 `\(Double(multiplier) * 2.5)` 并包含在字符串字面量中。
> 注意
>
@ -325,31 +325,31 @@ let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
*Unicode*是一个国际标准,用于文本的编码和表示。
它使您可以用标准格式表示来自任意语言几乎所有的字符,并能够对文本文件或网页这样的外部资源中的字符进行读写操作。
Swift 的`String``Character`类型是完全兼容 Unicode 标准的。
Swift 的 `String``Character` 类型是完全兼容 Unicode 标准的。
<a name="unicode_scalars"></a>
### Unicode 标量
Swift 的`String`类型是基于 *Unicode 标量* 建立的。
Unicode 标量是对应字符或者修饰符的唯一的21位数字例如`U+0061`表示小写的拉丁字母(`LATIN SMALL LETTER A`)("`a`")`U+1F425`表示小鸡表情(`FRONT-FACING BABY CHICK`) ("`🐥`")。
Swift 的 `String` 类型是基于 *Unicode 标量* 建立的。
Unicode 标量是对应字符或者修饰符的唯一的21位数字例如 `U+0061` 表示小写的拉丁字母(`LATIN SMALL LETTER A`)("`a`")`U+1F425` 表示小鸡表情(`FRONT-FACING BABY CHICK`) ("`🐥`")。
> 注意
>
> Unicode *码位(code poing)* 的范围是`U+0000``U+D7FF`或者`U+E000``U+10FFFF`。Unicode 标量不包括 Unicode *代理项(surrogate pair)* 码位,其码位范围是`U+D800``U+DFFF`。
> Unicode *码位(code poing)* 的范围是 `U+0000``U+D7FF` 或者 `U+E000``U+10FFFF`。Unicode 标量不包括 Unicode *代理项(surrogate pair)* 码位,其码位范围是 `U+D800``U+DFFF`。
注意不是所有的21位 Unicode 标量都代表一个字符,因为有一些标量是留作未来分配的。已经代表一个典型字符的标量都有自己的名字,例如上面例子中的`LATIN SMALL LETTER A``FRONT-FACING BABY CHICK`
注意不是所有的21位 Unicode 标量都代表一个字符,因为有一些标量是留作未来分配的。已经代表一个典型字符的标量都有自己的名字,例如上面例子中的 `LATIN SMALL LETTER A``FRONT-FACING BABY CHICK`
<a name="special_characters_in_string_literals"></a>
<a name="extended_grapheme_clusters"></a>
### 可扩展的字形群集
每一个 Swift 的`Character`类型代表一个*可扩展的字形群*。
每一个 Swift 的 `Character` 类型代表一个*可扩展的字形群*。
一个可扩展的字形群是一个或多个可生成人类可读的字符 Unicode 标量的有序排列。
举个例子,字母`é`可以用单一的 Unicode 标量`é`(`LATIN SMALL LETTER E WITH ACUTE`, 或者`U+00E9`)来表示。然而一个标准的字母`e`(`LATIN SMALL LETTER E`或者`U+0065`) 加上一个急促重音(`COMBINING ACTUE ACCENT`)的标量(`U+0301`),这样一对标量就表示了同样的字母`é`
这个急促重音的标量形象的将`e`转换成了`é`
举个例子,字母 `é` 可以用单一的 Unicode 标量 `é`(`LATIN SMALL LETTER E WITH ACUTE`, 或者 `U+00E9`)来表示。然而一个标准的字母 `e`(`LATIN SMALL LETTER E` 或者 `U+0065`) 加上一个急促重音(`COMBINING ACTUE ACCENT`)的标量(`U+0301`),这样一对标量就表示了同样的字母 `é`
这个急促重音的标量形象的将 `e` 转换成了 `é`
在这两种情况中,字母`é`代表了一个单一的 Swift 的`Character`值,同时代表了一个可扩展的字形群。
在这两种情况中,字母 `é` 代表了一个单一的 Swift 的 `Character` 值,同时代表了一个可扩展的字形群。
在第一种情况,这个字形群包含一个单一标量;而在第二种情况,它是包含两个标量的字形群:
```swift
@ -358,9 +358,9 @@ let combinedEAcute: Character = "\u{65}\u{301}" // e 后面加上 ́
// eAcute 是 é, combinedEAcute 是 é
```
可扩展的字符群集是一个灵活的方法,用许多复杂的脚本字符表示单一的`Character`值。
可扩展的字符群集是一个灵活的方法,用许多复杂的脚本字符表示单一的 `Character` 值。
例如,来自朝鲜语字母表的韩语音节能表示为组合或分解的有序排列。
在 Swift 都会表示为同一个单一的`Character`值:
在 Swift 都会表示为同一个单一的 `Character` 值:
```swift
let precomposed: Character = "\u{D55C}" // 한
@ -368,14 +368,14 @@ let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ, ᅡ, ᆫ
// precomposed 是 한, decomposed 是 한
```
可拓展的字符群集可以使包围记号(例如`COMBINING ENCLOSING CIRCLE`或者`U+20DD`)的标量包围其他 Unicode 标量,作为一个单一的`Character`值:
可拓展的字符群集可以使包围记号(例如 `COMBINING ENCLOSING CIRCLE` 或者 `U+20DD`)的标量包围其他 Unicode 标量,作为一个单一的 `Character` 值:
```swift
let enclosedEAcute: Character = "\u{E9}\u{20DD}"
// enclosedEAcute 是 é⃝
```
地域性指示符号的 Unicode 标量可以组合成一个单一的`Character`值,例如`REGIONAL INDICATOR SYMBOL LETTER U`(`U+1F1FA`)和`REGIONAL INDICATOR SYMBOL LETTER S`(`U+1F1F8`)
地域性指示符号的 Unicode 标量可以组合成一个单一的 `Character` 值,例如 `REGIONAL INDICATOR SYMBOL LETTER U`(`U+1F1FA`)和 `REGIONAL INDICATOR SYMBOL LETTER S`(`U+1F1F8`)
```swift
let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
@ -385,7 +385,7 @@ let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
<a name="counting_characters"></a>
## 计算字符数量
如果想要获得一个字符串中`Character`值的数量,可以使用`count`属性:
如果想要获得一个字符串中 `Character` 值的数量,可以使用 `count` 属性:
```swift
let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪"
@ -393,9 +393,9 @@ print("unusualMenagerie has \(unusualMenagerie.count) characters")
// 打印输出 "unusualMenagerie has 40 characters"
```
注意在 Swift 中,使用可拓展的字符群集作为`Character`值来连接或改变字符串时,并不一定会更改字符串的字符数量。
注意在 Swift 中,使用可拓展的字符群集作为 `Character` 值来连接或改变字符串时,并不一定会更改字符串的字符数量。
例如,如果你用四个字符的单词`cafe`初始化一个新的字符串,然后添加一个`COMBINING ACTUE ACCENT`(`U+0301`)作为字符串的结尾。最终这个字符串的字符数量仍然是`4`,因为第四个字符是`é`,而不是`e`
例如,如果你用四个字符的单词 `cafe` 初始化一个新的字符串,然后添加一个 `COMBINING ACTUE ACCENT`(`U+0301`)作为字符串的结尾。最终这个字符串的字符数量仍然是 `4`,因为第四个字符是 `é`,而不是 `e`
```swift
var word = "cafe"
@ -410,9 +410,9 @@ print("the number of characters in \(word) is \(word.count)")
> 注意
>
> 可扩展的字符群集可以组成一个或者多个 Unicode 标量。这意味着不同的字符以及相同字符的不同表示方式可能需要不同数量的内存空间来存储。所以 Swift 中的字符在一个字符串中并不一定占用相同的内存空间数量。因此在没有获取字符串的可扩展的字符群的范围时候,就不能计算出字符串的字符数量。如果您正在处理一个长字符串,需要注意`count`属性必须遍历全部的 Unicode 标量,来确定字符串的字符数量。
> 可扩展的字符群集可以组成一个或者多个 Unicode 标量。这意味着不同的字符以及相同字符的不同表示方式可能需要不同数量的内存空间来存储。所以 Swift 中的字符在一个字符串中并不一定占用相同的内存空间数量。因此在没有获取字符串的可扩展的字符群的范围时候,就不能计算出字符串的字符数量。如果您正在处理一个长字符串,需要注意 `count` 属性必须遍历全部的 Unicode 标量,来确定字符串的字符数量。
>
> 另外需要注意的是通过`count`属性返回的字符数量并不总是与包含相同字符的`NSString``length`属性相同。`NSString``length`属性是利用 UTF-16 表示的十六位代码单元数字,而不是 Unicode 可扩展的字符群集。
> 另外需要注意的是通过 `count` 属性返回的字符数量并不总是与包含相同字符的 `NSString``length` 属性相同。`NSString``length` 属性是利用 UTF-16 表示的十六位代码单元数字,而不是 Unicode 可扩展的字符群集。
<a name="accessing_and_modifying_a_string"></a>
## 访问和修改字符串
@ -422,11 +422,11 @@ print("the number of characters in \(word) is \(word.count)")
<a name="string_indices"></a>
### 字符串索引
每一个`String`值都有一个关联的索引(*index*)类型,`String.Index`,它对应着字符串中的每一个`Character`的位置。
每一个 `String` 值都有一个关联的索引(*index*)类型,`String.Index`,它对应着字符串中的每一个 `Character` 的位置。
前面提到,不同的字符可能会占用不同数量的内存空间,所以要知道`Character`的确定位置,就必须从`String`开头遍历每一个 Unicode 标量直到结尾。因此Swift 的字符串不能用整数(integer)做索引。
前面提到,不同的字符可能会占用不同数量的内存空间,所以要知道 `Character` 的确定位置,就必须从 `String` 开头遍历每一个 Unicode 标量直到结尾。因此Swift 的字符串不能用整数(integer)做索引。
使用`startIndex`属性可以获取一个`String`的第一个`Character`的索引。使用`endIndex`属性可以获取最后一个`Character`的后一个位置的索引。因此,`endIndex`属性不能作为一个字符串的有效下标。如果`String`是空串,`startIndex``endIndex`是相等的。
使用 `startIndex` 属性可以获取一个 `String` 的第一个 `Character` 的索引。使用 `endIndex` 属性可以获取最后一个 `Character` 的后一个位置的索引。因此,`endIndex` 属性不能作为一个字符串的有效下标。如果 `String` 是空串,`startIndex``endIndex` 是相等的。
通过调用 `String``index(before:)``index(after:)` 方法,可以立即得到前面或后面的一个索引。您还可以通过调用 `index(_:offsetBy:)` 方法来获取对应偏移量的索引,这种方式可以避免多次调用 `index(before:)``index(after:)` 方法。
@ -463,7 +463,7 @@ for index in greeting.indices {
> 注意
>
> 您可以使用 `startIndex` 和 `endIndex` 属性或者 `index(before:)` 、`index(after:)` 和 `index(_:offsetBy:)` 方法在任意一个确认的并遵循 `Collection` 协议的类型里面,如上文所示是使用在 `String` 中,您也可以使用在 `Array`、`Dictionary` 和 `Set`中。
> 您可以使用 `startIndex` 和 `endIndex` 属性或者 `index(before:)` 、`index(after:)` 和 `index(_:offsetBy:)` 方法在任意一个确认的并遵循 `Collection` 协议的类型里面,如上文所示是使用在 `String` 中,您也可以使用在 `Array`、`Dictionary` 和 `Set` 中。
<a name="inserting_and_removing"></a>
### 插入和删除
@ -540,7 +540,7 @@ if quotation == sameQuotation {
如果两个字符串(或者两个字符)的可扩展的字形群集是标准相等的,那就认为它们是相等的。在这个情况下,即使可扩展的字形群集是有不同的 Unicode 标量构成的,只要它们有同样的语言意义和外观,就认为它们标准相等。
例如,`LATIN SMALL LETTER E WITH ACUTE`(`U+00E9`)就是标准相等于`LATIN SMALL LETTER E`(`U+0065`)后面加上`COMBINING ACUTE ACCENT`(`U+0301`)。这两个字符群集都是表示字符`é`的有效方式,所以它们被认为是标准相等的:
例如,`LATIN SMALL LETTER E WITH ACUTE`(`U+00E9`)就是标准相等于 `LATIN SMALL LETTER E`(`U+0065`)后面加上 `COMBINING ACUTE ACCENT`(`U+0301`)。这两个字符群集都是表示字符 `é` 的有效方式,所以它们被认为是标准相等的:
```swift
// "Voulez-vous un café?" 使用 LATIN SMALL LETTER E WITH ACUTE
@ -555,7 +555,7 @@ if eAcuteQuestion == combinedEAcuteQuestion {
// 打印输出 "These two strings are considered equal"
```
相反,英语中的`LATIN CAPITAL LETTER A`(`U+0041`,或者`A`)不等于俄语中的`CYRILLIC CAPITAL LETTER A`(`U+0410`,或者`A`)。两个字符看着是一样的,但却有不同的语言意义:
相反,英语中的 `LATIN CAPITAL LETTER A`(`U+0041`,或者 `A`)不等于俄语中的 `CYRILLIC CAPITAL LETTER A`(`U+0410`,或者 `A`)。两个字符看着是一样的,但却有不同的语言意义:
```swift
let latinCapitalLetterA: Character = "\u{41}"
@ -575,7 +575,7 @@ if latinCapitalLetterA != cyrillicCapitalLetterA {
<a name="prefix_and_suffix_equality"></a>
### 前缀/后缀相等
通过调用字符串的`hasPrefix(_:)`/`hasSuffix(_:)`方法来检查字符串是否拥有特定前缀/后缀,两个方法均接收一个`String`类型的参数,并返回一个布尔值。
通过调用字符串的 `hasPrefix(_:)`/`hasSuffix(_:)` 方法来检查字符串是否拥有特定前缀/后缀,两个方法均接收一个 `String` 类型的参数,并返回一个布尔值。
下面的例子以一个字符串数组表示莎士比亚话剧《罗密欧与朱丽叶》中前两场的场景位置:
@ -595,7 +595,7 @@ let romeoAndJuliet = [
]
```
您可以调用`hasPrefix(_:)`方法来计算话剧中第一幕的场景数:
您可以调用 `hasPrefix(_:)` 方法来计算话剧中第一幕的场景数:
```swift
var act1SceneCount = 0
@ -608,7 +608,7 @@ print("There are \(act1SceneCount) scenes in Act 1")
// 打印输出 "There are 5 scenes in Act 1"
```
相似地,您可以用`hasSuffix(_:)`方法来计算发生在不同地方的场景数:
相似地,您可以用 `hasSuffix(_:)` 方法来计算发生在不同地方的场景数:
```swift
var mansionCount = 0
@ -626,24 +626,24 @@ print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
> 注意
>
> `hasPrefix(_:)``hasSuffix(_:)`方法都是在每个字符串中逐字符比较其可扩展的字符群集是否标准相等,详细描述在[字符串/字符相等](#string_and_character_equality)。
> `hasPrefix(_:)``hasSuffix(_:)` 方法都是在每个字符串中逐字符比较其可扩展的字符群集是否标准相等,详细描述在[字符串/字符相等](#string_and_character_equality)。
<a name="unicode_representations_of_strings"></a>
## 字符串的 Unicode 表示形式
当一个 Unicode 字符串被写进文本文件或者其他储存时,字符串中的 Unicode 标量会用 Unicode 定义的几种`编码格式`encoding forms编码。每一个字符串中的小块编码都被称`代码单元`code units。这些包括 UTF-8 编码格式编码字符串为8位的代码单元 UTF-16 编码格式编码字符串位16位的代码单元以及 UTF-32 编码格式编码字符串32位的代码单元
当一个 Unicode 字符串被写进文本文件或者其他储存时,字符串中的 Unicode 标量会用 Unicode 定义的几种 `编码格式`encoding forms编码。每一个字符串中的小块编码都被称 `代码单元`code units。这些包括 UTF-8 编码格式编码字符串为8位的代码单元 UTF-16 编码格式编码字符串位16位的代码单元以及 UTF-32 编码格式编码字符串32位的代码单元
Swift 提供了几种不同的方式来访问字符串的 Unicode 表示形式。
您可以利用`for-in`来对字符串进行遍历,从而以 Unicode 可扩展的字符群集的方式访问每一个`Character`值。
您可以利用 `for-in` 来对字符串进行遍历,从而以 Unicode 可扩展的字符群集的方式访问每一个 `Character` 值。
该过程在 [使用字符](#working_with_characters) 中进行了描述。
另外,能够以其他三种 Unicode 兼容的方式访问字符串的值:
* UTF-8 代码单元集合 (利用字符串的`utf8`属性进行访问)
* UTF-16 代码单元集合 (利用字符串的`utf16`属性进行访问)
* 21位的 Unicode 标量值集合,也就是字符串的 UTF-32 编码格式 (利用字符串的`unicodeScalars`属性进行访问)
* UTF-8 代码单元集合 (利用字符串的 `utf8` 属性进行访问)
* UTF-16 代码单元集合 (利用字符串的 `utf16` 属性进行访问)
* 21位的 Unicode 标量值集合,也就是字符串的 UTF-32 编码格式 (利用字符串的 `unicodeScalars` 属性进行访问)
下面由`D`,`o`,`g`,`‼`(`DOUBLE EXCLAMATION MARK`, Unicode 标量 `U+203C`)和`🐶`(`DOG FACE`Unicode 标量为`U+1F436`)组成的字符串中的每一个字符代表着一种不同的表示:
下面由 `D`,`o`,`g`,`‼`(`DOUBLE EXCLAMATION MARK`, Unicode 标量 `U+203C`)和 `🐶`(`DOG FACE`Unicode 标量为 `U+1F436`)组成的字符串中的每一个字符代表着一种不同的表示:
```swift
let dogString = "Dog‼🐶"
@ -652,8 +652,8 @@ let dogString = "Dog‼🐶"
<a name="UTF-8_representation"></a>
### UTF-8 表示
您可以通过遍历`String``utf8`属性来访问它的`UTF-8`表示。
其为`String.UTF8View`类型的属性,`UTF8View`是无符号8位 (`UInt8`) 值的集合,每一个`UInt8`值都是一个字符的 UTF-8 表示:
您可以通过遍历 `String``utf8` 属性来访问它的 `UTF-8` 表示。
其为 `String.UTF8View` 类型的属性,`UTF8View` 是无符号8位 (`UInt8`) 值的集合,每一个 `UInt8` 值都是一个字符的 UTF-8 表示:
<table style='text-align:center'>
<tr height="77">
@ -700,15 +700,15 @@ print("")
// 68 111 103 226 128 188 240 159 144 182
```
上面的例子中前三个10进制`codeUnit`值 (`68`, `111`, `103`) 代表了字符`D``o``g`,它们的 UTF-8 表示与 ASCII 表示相同。
接下来的三个10进制`codeUnit`值 (`226`, `128`, `188`) 是`DOUBLE EXCLAMATION MARK`的3字节 UTF-8 表示。
最后的四个`codeUnit`值 (`240`, `159`, `144`, `182`) 是`DOG FACE`的4字节 UTF-8 表示。
上面的例子中前三个10进制 `codeUnit` 值 (`68`, `111`, `103`) 代表了字符 `D``o` `g`,它们的 UTF-8 表示与 ASCII 表示相同。
接下来的三个10进制 `codeUnit` 值 (`226`, `128`, `188`) 是 `DOUBLE EXCLAMATION MARK` 的3字节 UTF-8 表示。
最后的四个 `codeUnit` 值 (`240`, `159`, `144`, `182`) 是 `DOG FACE` 的4字节 UTF-8 表示。
<a name="UTF-16_representation"></a>
### UTF-16 表示
您可以通过遍历`String``utf16`属性来访问它的`UTF-16`表示。
其为`String.UTF16View`类型的属性,`UTF16View`是无符号16位 (`UInt16`) 值的集合,每一个`UInt16`都是一个字符的 UTF-16 表示:
您可以通过遍历 `String``utf16` 属性来访问它的 `UTF-16` 表示。
其为 `String.UTF16View` 类型的属性,`UTF16View` 是无符号16位 (`UInt16`) 值的集合,每一个 `UInt16` 都是一个字符的 UTF-16 表示:
<table style='text-align:center'>
<tr height="77">
@ -747,21 +747,21 @@ print("")
// 68 111 103 8252 55357 56374
```
同样,前三个`codeUnit`值 (`68`, `111`, `103`) 代表了字符`D``o``g`,它们的 UTF-16 代码单元和 UTF-8 完全相同(因为这些 Unicode 标量表示 ASCII 字符)。
同样,前三个 `codeUnit` 值 (`68`, `111`, `103`) 代表了字符 `D``o``g`,它们的 UTF-16 代码单元和 UTF-8 完全相同(因为这些 Unicode 标量表示 ASCII 字符)。
第四个`codeUnit`值 (`8252`) 是一个等于十六进制`203C`的的十进制值。这个代表了`DOUBLE EXCLAMATION MARK`字符的 Unicode 标量值`U+203C`。这个字符在 UTF-16 中可以用一个代码单元表示。
第四个 `codeUnit` 值 (`8252`) 是一个等于十六进制 `203C` 的的十进制值。这个代表了 `DOUBLE EXCLAMATION MARK` 字符的 Unicode 标量值 `U+203C`。这个字符在 UTF-16 中可以用一个代码单元表示。
第五和第六个`codeUnit`值 (`55357``56374`) 是`DOG FACE`字符的 UTF-16 表示。
第一个值为`U+D83D`(十进制值为`55357`),第二个值为`U+DC36`(十进制值为`56374`)。
第五和第六个 `codeUnit` 值 (`55357``56374`) 是 `DOG FACE` 字符的 UTF-16 表示。
第一个值为 `U+D83D`(十进制值为 `55357`),第二个值为 `U+DC36`(十进制值为 `56374`)。
<a name="unicode_scalars_representation"></a>
### Unicode 标量表示
您可以通过遍历`String`值的`unicodeScalars`属性来访问它的 Unicode 标量表示。
其为`UnicodeScalarView`类型的属性,`UnicodeScalarView``UnicodeScalar`类型的值的集合。
`UnicodeScalar`是21位的 Unicode 代码点。
您可以通过遍历 `String` 值的 `unicodeScalars` 属性来访问它的 Unicode 标量表示。
其为 `UnicodeScalarView` 类型的属性,`UnicodeScalarView``UnicodeScalar` 类型的值的集合。
`UnicodeScalar` 是21位的 Unicode 代码点。
每一个`UnicodeScalar`拥有一个`value`属性可以返回对应的21位数值`UInt32`来表示:
每一个 `UnicodeScalar` 拥有一个 `value` 属性可以返回对应的21位数值 `UInt32` 来表示:
<table style='text-align:center'>
<tr height="77">
@ -798,13 +798,13 @@ print("")
// 68 111 103 8252 128054
```
前三个`UnicodeScalar`值(`68`, `111`, `103`)的`value`属性仍然代表字符`D``o``g`
前三个 `UnicodeScalar` 值(`68`, `111`, `103`)的 `value` 属性仍然代表字符 `D``o``g`
第四个`codeUnit`值(`8252`)仍然是一个等于十六进制`203C`的十进制值。这个代表了`DOUBLE EXCLAMATION MARK`字符的 Unicode 标量`U+203C`
第四个 `codeUnit` 值(`8252`)仍然是一个等于十六进制 `203C` 的十进制值。这个代表了 `DOUBLE EXCLAMATION MARK` 字符的 Unicode 标量 `U+203C`
第五个`UnicodeScalar`值的`value`属性,`128054`,是一个十六进制`1F436`的十进制表示。其等同于`DOG FACE`的 Unicode 标量`U+1F436`
第五个 `UnicodeScalar` 值的 `value` 属性,`128054`,是一个十六进制 `1F436` 的十进制表示。其等同于 `DOG FACE` 的 Unicode 标量 `U+1F436`
作为查询它们的`value`属性的一种替代方法,每个`UnicodeScalar`值也可以用来构建一个新的`String`值,比如在字符串插值中使用:
作为查询它们的 `value` 属性的一种替代方法,每个 `UnicodeScalar` 值也可以用来构建一个新的 `String` 值,比如在字符串插值中使用:
```swift
for scalar in dogString.unicodeScalars {

View File

@ -13,13 +13,13 @@
> 2.2
> 校对:[SketchK](https://github.com/SketchK) 2016-05-11
>
> 3.0
> 校对:[shanks](http://codebuild.me) 2016-10-09
> 3.0.1shanks2016-11-12
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
本页包含内容:
@ -29,23 +29,23 @@
- [集合操作](#performing_set_operations)
- [字典](#dictionaries)
Swift 语言提供`Arrays``Sets``Dictionaries`三种基本的*集合类型*用来存储集合数据。数组Arrays是有序数据的集。集合Sets是无序无重复数据的集。字典Dictionaries是无序的键值对的集。
Swift 语言提供 `Arrays``Sets``Dictionaries` 三种基本的*集合类型*用来存储集合数据。数组Arrays是有序数据的集。集合Sets是无序无重复数据的集。字典Dictionaries是无序的键值对的集。
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/CollectionTypes_intro_2x.png)
Swift 语言中的`Arrays``Sets``Dictionaries`中存储的数据值类型必须明确。这意味着我们不能把错误的数据类型插入其中。同时这也说明你完全可以对取回值的类型非常放心。
Swift 语言中的 `Arrays``Sets``Dictionaries` 中存储的数据值类型必须明确。这意味着我们不能把错误的数据类型插入其中。同时这也说明你完全可以对取回值的类型非常放心。
> 注意
>
> Swift 的`Arrays`、`Sets``Dictionaries`类型被实现为*泛型集合*。更多关于泛型类型和集合,参见 [泛型](./23_Generics.html)章节。
> 注意
>
> Swift 的 `Arrays`、`Sets``Dictionaries` 类型被实现为*泛型集合*。更多关于泛型类型和集合,参见 [泛型](./23_Generics.html)章节。
<a name="mutability_of_collections"></a>
## 集合的可变性
如果创建一个`Arrays``Sets``Dictionaries`并且把它分配成一个变量,这个集合将会是*可变的*。这意味着你可以在创建之后添加更多或移除已存在的数据项,或者改变集合中的数据项。如果我们把`Arrays``Sets``Dictionaries`分配成常量,那么它就是*不可变的*,它的大小和内容都不能被改变。
如果创建一个 `Arrays``Sets``Dictionaries` 并且把它分配成一个变量,这个集合将会是*可变的*。这意味着你可以在创建之后添加更多或移除已存在的数据项,或者改变集合中的数据项。如果我们把 `Arrays``Sets``Dictionaries` 分配成常量,那么它就是*不可变的*,它的大小和内容都不能被改变。
> 注意
>
> 注意
>
> 在我们不需要改变集合的时候创建不可变集合是很好的实践。如此 Swift 编译器可以优化我们创建的集合。
<a name="arrays"></a>
@ -53,14 +53,14 @@ Swift 语言中的`Arrays`、`Sets`和`Dictionaries`中存储的数据值类型
*数组*使用有序列表存储同一类型的多个值。相同的值可以多次出现在一个数组的不同位置中。
> 注意
>
> Swift 的`Array`类型被桥接到`Foundation`中的`NSArray`类。更多关于在`Foundation``Cocoa`中使用`Array`的信息,参见 [*Using Swift with Cocoa and Obejective-C(Swift 4.1)*](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。
> 注意
>
> Swift 的 `Array` 类型被桥接到 `Foundation` 中的 `NSArray` 类。更多关于在 `Foundation``Cocoa` 中使用 `Array` 的信息,参见 [*Using Swift with Cocoa and Obejective-C(Swift 4.1)*](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。
<a name="array_type_shorthand_syntax"></a>
### 数组的简单语法
写 Swift 数组应该遵循像`Array<Element>`这样的形式,其中`Element`是这个数组中唯一允许存在的数据类型。我们也可以使用像`[Element]`这样的简单语法。尽管两种形式在功能上是一样的,但是推荐较短的那种,而且在本文中都会使用这种形式来使用数组。
写 Swift 数组应该遵循像 `Array<Element>` 这样的形式,其中 `Element` 是这个数组中唯一允许存在的数据类型。我们也可以使用像 `[Element]` 这样的简单语法。尽管两种形式在功能上是一样的,但是推荐较短的那种,而且在本文中都会使用这种形式来使用数组。
<a name="creating_an_empty_array"></a>
### 创建一个空数组
@ -73,7 +73,7 @@ print("someInts is of type [Int] with \(someInts.count) items.")
// 打印 "someInts is of type [Int] with 0 items."
```
注意,通过构造函数的类型,`someInts`的值类型被推断为`[Int]`
注意,通过构造函数的类型,`someInts` 的值类型被推断为 `[Int]`
或者,如果代码上下文中已经提供了类型信息,例如一个函数参数或者一个已经定义好类型的常量或者变量,我们可以使用空数组语句创建一个空数组,它的写法很简单:`[]`(一对空方括号):
@ -87,7 +87,7 @@ someInts = []
<a name="creating_an_array_with_a_default_value"></a>
### 创建一个带有默认值的数组
Swift 中的`Array`类型还提供一个可以创建特定大小并且所有数据都被默认的构造方法。我们可以把准备加入新数组的数据项数量(`count`)和适当类型的初始值(`repeating`)传入数组构造函数:
Swift 中的 `Array` 类型还提供一个可以创建特定大小并且所有数据都被默认的构造方法。我们可以把准备加入新数组的数据项数量(`count`)和适当类型的初始值(`repeating`)传入数组构造函数:
```swift
var threeDoubles = Array(repeating: 0.0, count: 3)
@ -114,42 +114,42 @@ var sixDoubles = threeDoubles + anotherThreeDoubles
`[value 1, value 2, value 3]`
下面这个例子创建了一个叫做`shoppingList`并且存储`String`的数组:
下面这个例子创建了一个叫做 `shoppingList` 并且存储 `String` 的数组:
```swift
var shoppingList: [String] = ["Eggs", "Milk"]
// shoppingList 已经被构造并且拥有两个初始项。
```
`shoppingList`变量被声明为“字符串值类型的数组“,记作`[String]`。 因为这个数组被规定只有`String`一种数据结构,所以只有`String`类型可以在其中被存取。 在这里,`shoppingList`数组由两个`String`值(`"Eggs"``"Milk"`)构造,并且由数组字面量定义。
`shoppingList` 变量被声明为“字符串值类型的数组“,记作 `[String]`。 因为这个数组被规定只有 `String` 一种数据结构,所以只有 `String` 类型可以在其中被存取。 在这里,`shoppingList` 数组由两个 `String` 值(`"Eggs"` `"Milk"`)构造,并且由数组字面量定义。
> 注意
>
> `shoppingList`数组被声明为变量(`var`关键字创建)而不是常量(`let`创建)是因为以后可能会有更多的数据项被插入其中。
> 注意
>
> `shoppingList` 数组被声明为变量(`var` 关键字创建)而不是常量(`let` 创建)是因为以后可能会有更多的数据项被插入其中。
在这个例子中,字面量仅仅包含两个`String`值。匹配了该数组的变量声明(只能包含`String`的数组),所以这个字面量的分配过程可以作为用两个初始项来构造`shoppingList`的一种方式。
在这个例子中,字面量仅仅包含两个 `String` 值。匹配了该数组的变量声明(只能包含 `String` 的数组),所以这个字面量的分配过程可以作为用两个初始项来构造 `shoppingList` 的一种方式。
由于 Swift 的类型推断机制,当我们用字面量构造只拥有相同类型值数组的时候,我们不必把数组的类型定义清楚。 `shoppingList`的构造也可以这样写:
由于 Swift 的类型推断机制,当我们用字面量构造只拥有相同类型值数组的时候,我们不必把数组的类型定义清楚。`shoppingList` 的构造也可以这样写:
```swift
var shoppingList = ["Eggs", "Milk"]
```
因为所有数组字面量中的值都是相同的类型Swift 可以推断出`[String]``shoppingList`中变量的正确类型。
因为所有数组字面量中的值都是相同的类型Swift 可以推断出 `[String]``shoppingList` 中变量的正确类型。
<a name="accessing_and_modifying_an_array"></a>
### 访问和修改数组
我们可以通过数组的方法和属性来访问和修改数组,或者使用下标语法。
可以使用数组的只读属性`count`来获取数组中的数据项数量:
可以使用数组的只读属性 `count` 来获取数组中的数据项数量:
```swift
print("The shopping list contains \(shoppingList.count) items.")
// 输出 "The shopping list contains 2 items."这个数组有2个项
```
使用布尔属性`isEmpty`作为一个缩写形式去检查`count`属性是否为`0`
使用布尔属性 `isEmpty` 作为一个缩写形式去检查 `count` 属性是否为 `0`
```swift
if shoppingList.isEmpty {
@ -160,7 +160,7 @@ if shoppingList.isEmpty {
// 打印 "The shopping list is not empty."shoppinglist 不是空的)
```
也可以使用`append(_:)`方法在数组后面添加新的数据项:
也可以使用 `append(_:)` 方法在数组后面添加新的数据项:
```swift
shoppingList.append("Flour")
@ -183,9 +183,9 @@ var firstItem = shoppingList[0]
// 第一项是 "Eggs"
```
> 注意
>
> 第一项在数组中的索引值是`0`而不是`1`。 Swift 中的数组索引总是从零开始。
> 注意
>
> 第一项在数组中的索引值是 `0` 而不是 `1`。 Swift 中的数组索引总是从零开始。
我们也可以用下标来改变某个已有索引值对应的数据值:
@ -194,18 +194,18 @@ shoppingList[0] = "Six eggs"
// 其中的第一项现在是 "Six eggs" 而不是 "Eggs"
```
还可以利用下标来一次改变一系列数据值,即使新数据和原有数据的数量是不一样的。下面的例子把`"Chocolate Spread"``"Cheese"`,和`"Butter"`替换为`"Bananas"``"Apples"`
还可以利用下标来一次改变一系列数据值,即使新数据和原有数据的数量是不一样的。下面的例子把 `"Chocolate Spread"``"Cheese"`,和 `"Butter"` 替换为 `"Bananas"` `"Apples"`
```swift
shoppingList[4...6] = ["Bananas", "Apples"]
// shoppingList 现在有6项
```
> 注意
>
> 注意
>
> 不可以用下标访问的形式去在数组尾部添加新项。
调用数组的`insert(_:at:)`方法来在某个具体索引值之前添加数据项:
调用数组的 `insert(_:at:)` 方法来在某个具体索引值之前添加数据项:
```swift
shoppingList.insert("Maple Syrup", at: 0)
@ -213,9 +213,9 @@ shoppingList.insert("Maple Syrup", at: 0)
// "Maple Syrup" 现在是这个列表中的第一项
```
这次`insert(_:at:)`方法调用把值为`"Maple Syrup"`的新数据项插入列表的最开始位置,并且使用`0`作为索引值。
这次 `insert(_:at:)` 方法调用把值为 `"Maple Syrup"` 的新数据项插入列表的最开始位置,并且使用 `0` 作为索引值。
类似的我们可以使用`remove(at:)`方法来移除数组中的某一项。这个方法把数组在特定索引值中存储的数据项移除并且返回这个被移除的数据项(我们不需要的时候就可以无视它):
类似的我们可以使用 `remove(at:)` 方法来移除数组中的某一项。这个方法把数组在特定索引值中存储的数据项移除并且返回这个被移除的数据项(我们不需要的时候就可以无视它):
```swift
let mapleSyrup = shoppingList.remove(at: 0)
@ -223,18 +223,18 @@ let mapleSyrup = shoppingList.remove(at: 0)
// shoppingList 现在只有6项而且不包括 Maple Syrup
// mapleSyrup 常量的值等于被移除数据项的值 "Maple Syrup"
```
> 注意
>
> 如果我们试着对索引越界的数据进行检索或者设置新值的操作,会引发一个运行期错误。我们可以使用索引值和数组的`count`属性进行比较来在使用某个索引之前先检验是否有效。除了当`count`等于 0 时(说明这是个空数组),最大索引值一直是`count - 1`,因为数组都是零起索引。
> 注意
>
> 如果我们试着对索引越界的数据进行检索或者设置新值的操作,会引发一个运行期错误。我们可以使用索引值和数组的 `count` 属性进行比较来在使用某个索引之前先检验是否有效。除了当 `count` 等于 0 时(说明这是个空数组),最大索引值一直是 `count - 1`,因为数组都是零起索引。
数据项被移除后数组中的空出项会被自动填补,所以现在索引值为`0`的数据项的值再次等于`"Six eggs"`
数据项被移除后数组中的空出项会被自动填补,所以现在索引值为 `0` 的数据项的值再次等于 `"Six eggs"`
```swift
firstItem = shoppingList[0]
// firstItem 现在等于 "Six eggs"
```
如果我们只想把数组中的最后一项移除,可以使用`removeLast()`方法而不是`remove(at:)`方法来避免我们需要获取数组的`count`属性。就像后者一样,前者也会返回被移除的数据项:
如果我们只想把数组中的最后一项移除,可以使用 `removeLast()` 方法而不是 `remove(at:)` 方法来避免我们需要获取数组的 `count` 属性。就像后者一样,前者也会返回被移除的数据项:
```swift
let apples = shoppingList.removeLast()
@ -246,7 +246,7 @@ let apples = shoppingList.removeLast()
<a name="iterating_over_an_array"></a>
### 数组的遍历
我们可以使用`for-in`循环来遍历所有数组中的数据项:
我们可以使用 `for-in` 循环来遍历所有数组中的数据项:
```swift
for item in shoppingList {
@ -259,7 +259,7 @@ for item in shoppingList {
// Bananas
```
如果我们同时需要每个数据项的值和索引值,可以使用`enumerated()`方法来进行数组遍历。`enumerated()`返回一个由每一个数据项索引值和数据值组成的元组。我们可以把这个元组分解成临时常量或者变量来进行遍历:
如果我们同时需要每个数据项的值和索引值,可以使用 `enumerated()` 方法来进行数组遍历。`enumerated()` 返回一个由每一个数据项索引值和数据值组成的元组。我们可以把这个元组分解成临时常量或者变量来进行遍历:
```swift
for (index, value) in shoppingList. enumerated() {
@ -272,7 +272,7 @@ for (index, value) in shoppingList. enumerated() {
// Item 5: Bananas
```
更多关于`for-in`循环的介绍请参见[for 循环](05_Control_Flow.html#for_loops)。
更多关于 `for-in` 循环的介绍请参见[for 循环](05_Control_Flow.html#for_loops)。
<a name="sets"></a>
## 集合Sets
@ -280,33 +280,33 @@ for (index, value) in shoppingList. enumerated() {
*集合(Set)*用来存储相同类型并且没有确定顺序的值。当集合元素顺序不重要时或者希望确保每个元素只出现一次时可以使用集合而不是数组。
> 注意
> Swift`Set`类型被桥接到`Foundation`中的`NSSet`类。
> Swift`Set` 类型被桥接到 `Foundation` 中的 `NSSet` 类。
>
> 关于使用`Foundation``Cocoa``Set`的知识,参见 [*Using Swift with Cocoa and Obejective-C(Swift 4.1)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。
> 关于使用 `Foundation``Cocoa``Set` 的知识,参见 [*Using Swift with Cocoa and Obejective-C(Swift 4.1)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。
<a name="hash_values_for_set_types"></a>
#### 集合类型的哈希值
一个类型为了存储在集合中,该类型必须是*可哈希化*的--也就是说,该类型必须提供一个方法来计算它的*哈希值*。一个哈希值是`Int`类型的,相等的对象哈希值必须相同,比如`a==b`,因此必须`a.hashValue == b.hashValue`
一个类型为了存储在集合中,该类型必须是*可哈希化*的--也就是说,该类型必须提供一个方法来计算它的*哈希值*。一个哈希值是 `Int` 类型的,相等的对象哈希值必须相同,比如 `a==b`,因此必须 `a.hashValue == b.hashValue`
Swift 的所有基本类型(比如`String`,`Int`,`Double``Bool`)默认都是可哈希化的,可以作为集合的值的类型或者字典的键的类型。没有关联值的枚举成员值(在[枚举](./08_Enumerations.html)有讲述)默认也是可哈希化的。
Swift 的所有基本类型(比如 `String`,`Int`,`Double``Bool`)默认都是可哈希化的,可以作为集合的值的类型或者字典的键的类型。没有关联值的枚举成员值(在[枚举](./08_Enumerations.html)有讲述)默认也是可哈希化的。
> 注意
>
> 你可以使用你自定义的类型作为集合的值的类型或者是字典的键的类型,但你需要使你的自定义类型符合 Swift 标准库中的`Hashable`协议。符合`Hashable`协议的类型需要提供一个类型为`Int`的可读属性`hashValue`。由类型的`hashValue`属性返回的值不需要在同一程序的不同执行周期或者不同程序之间保持相同。
> 注意
>
> 因为`Hashable`协议符合`Equatable`协议,所以遵循该协议的类型也必须提供一个"是否相等"运算符(`==`)的实现。这个`Equatable`协议要求任何符合`==`实现的实例间都是一种相等的关系。也就是说,对于`a,b,c`三个值来说,`==`的实现必须满足下面三种情况:
> 你可以使用你自定义的类型作为集合的值的类型或者是字典的键的类型,但你需要使你的自定义类型符合 Swift 标准库中的 `Hashable` 协议符合 `Hashable` 协议的类型需要提供一个类型为 `Int` 的可读属性 `hashValue`。由类型的 `hashValue` 属性返回的值不需要在同一程序的不同执行周期或者不同程序之间保持相同。
>
> 因为 `Hashable` 协议符合 `Equatable` 协议,所以遵循该协议的类型也必须提供一个"是否相等"运算符(`==`)的实现。这个 `Equatable` 协议要求任何符合 `==` 实现的实例间都是一种相等的关系。也就是说,对于 `a,b,c` 三个值来说,`==` 的实现必须满足下面三种情况:
> * `a == a`(自反性)
> * `a == b`意味着`b == a`(对称性)
> * `a == b && b == c`意味着`a == c`(传递性)
> * `a == b` 意味着 `b == a`(对称性)
> * `a == b && b == c` 意味着 `a == c`(传递性)
关于遵循协议的更多信息,请看[协议](./22_Protocols.html)。
<a name="set_type_syntax"></a>
### 集合类型语法
Swift 中的`Set`类型被写为`Set<Element>`,这里的`Element`表示`Set`中允许存储的类型,和数组不同的是,集合没有等价的简化形式。
Swift 中的 `Set` 类型被写为 `Set<Element>`,这里的 `Element` 表示 `Set` 中允许存储的类型,和数组不同的是,集合没有等价的简化形式。
<a name="creating_and_initalizing_an_empty_set"></a>
### 创建和构造一个空的集合
@ -319,11 +319,11 @@ print("letters is of type Set<Character> with \(letters.count) items.")
// 打印 "letters is of type Set<Character> with 0 items."
```
> 注意
> 注意
>
> 通过构造器,这里的`letters`变量的类型被推断为`Set<Character>`。
> 通过构造器,这里的 `letters` 变量的类型被推断为 `Set<Character>`。
此外,如果上下文提供了类型信息,比如作为函数的参数或者已知类型的变量或常量,我们可以通过一个空的数组字面量创建一个空的`Set`
此外,如果上下文提供了类型信息,比如作为函数的参数或者已知类型的变量或常量,我们可以通过一个空的数组字面量创建一个空的 `Set`
```swift
letters.insert("a")
@ -337,40 +337,40 @@ letters = []
你可以使用数组字面量来构造集合,并且可以使用简化形式写一个或者多个值作为集合元素。
下面的例子创建一个称之为`favoriteGenres`的集合来存储`String`类型的值:
下面的例子创建一个称之为 `favoriteGenres` 的集合来存储 `String` 类型的值:
```swift
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// favoriteGenres 被构造成含有三个初始值的集合
```
这个`favoriteGenres`变量被声明为“一个`String`值的集合”,写为`Set<String>`。由于这个特定的集合含有指定`String`类型的值,所以它只允许存储`String`类型值。这里的`favoriteGenres`变量有三个`String`类型的初始值(`"Rock"``"Classical"``"Hip hop"`),并以数组字面量的方式出现。
这个 `favoriteGenres` 变量被声明为“一个 `String` 值的集合”,写为 `Set<String>`。由于这个特定的集合含有指定 `String` 类型的值,所以它只允许存储 `String` 类型值。这里的 `favoriteGenres` 变量有三个 `String` 类型的初始值(`"Rock"``"Classical"``"Hip hop"`),并以数组字面量的方式出现。
> 注意
> 注意
>
> `favoriteGenres`被声明为一个变量(拥有`var`标示符)而不是一个常量(拥有`let`标示符),因为它里面的元素将会在下面的例子中被增加或者移除。
> `favoriteGenres` 被声明为一个变量(拥有 `var` 标示符)而不是一个常量(拥有 `let` 标示符),因为它里面的元素将会在下面的例子中被增加或者移除。
一个`Set`类型不能从数组字面量中被单独推断出来,因此`Set`类型必须显式声明。然而,由于 Swift 的类型推断功能,如果你想使用一个数组字面量构造一个`Set`并且该数组字面量中的所有元素类型相同,那么你无须写出`Set`的具体类型。`favoriteGenres`的构造形式可以采用简化的方式代替:
一个 `Set` 类型不能从数组字面量中被单独推断出来,因此 `Set` 类型必须显式声明。然而,由于 Swift 的类型推断功能,如果你想使用一个数组字面量构造一个 `Set` 并且该数组字面量中的所有元素类型相同,那么你无须写出 `Set` 的具体类型。`favoriteGenres` 的构造形式可以采用简化的方式代替:
```swift
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
```
由于数组字面量中的所有元素类型相同Swift 可以推断出`Set<String>`作为`favoriteGenres`变量的正确类型。
由于数组字面量中的所有元素类型相同Swift 可以推断出 `Set<String>` 作为 `favoriteGenres` 变量的正确类型。
<a name="accesing_and_modifying_a_set"></a>
### 访问和修改一个集合
你可以通过`Set`的属性和方法来访问和修改一个`Set`
你可以通过 `Set` 的属性和方法来访问和修改一个 `Set`
为了找出一个`Set`中元素的数量,可以使用其只读属性`count`
为了找出一个 `Set` 中元素的数量,可以使用其只读属性 `count`
```swift
print("I have \(favoriteGenres.count) favorite music genres.")
// 打印 "I have 3 favorite music genres."
```
使用布尔属性`isEmpty`作为一个缩写形式去检查`count`属性是否为`0`
使用布尔属性 `isEmpty` 作为一个缩写形式去检查 `count` 属性是否为 `0`
```swift
if favoriteGenres.isEmpty {
@ -381,14 +381,14 @@ if favoriteGenres.isEmpty {
// 打印 "I have particular music preferences."
```
你可以通过调用`Set``insert(_:)`方法来添加一个新元素:
你可以通过调用 `Set``insert(_:)` 方法来添加一个新元素:
```swift
favoriteGenres.insert("Jazz")
// favoriteGenres 现在包含4个元素
```
你可以通过调用`Set``remove(_:)`方法去删除一个元素,如果该值是该`Set`的一个元素则删除该元素并且返回被删除的元素值,否则如果该`Set`不包含该值,则返回`nil`。另外,`Set`中的所有元素可以通过它的`removeAll()`方法删除。
你可以通过调用 `Set``remove(_:)` 方法去删除一个元素,如果该值是该 `Set` 的一个元素则删除该元素并且返回被删除的元素值,否则如果该 `Set` 不包含该值,则返回 `nil`。另外,`Set` 中的所有元素可以通过它的 `removeAll()` 方法删除。
```swift
if let removedGenre = favoriteGenres.remove("Rock") {
@ -399,7 +399,7 @@ if let removedGenre = favoriteGenres.remove("Rock") {
// 打印 "Rock? I'm over it."
```
使用`contains(_:)`方法去检查`Set`中是否包含一个特定的值:
使用 `contains(_:)` 方法去检查 `Set` 中是否包含一个特定的值:
```swift
if favoriteGenres.contains("Funk") {
@ -413,7 +413,7 @@ if favoriteGenres.contains("Funk") {
<a name="iterating_over_a_set"></a>
### 遍历一个集合
你可以在一个`for-in`循环中遍历一个`Set`中的所有值。
你可以在一个 `for-in` 循环中遍历一个 `Set` 中的所有值。
```swift
for genre in favoriteGenres {
@ -424,9 +424,9 @@ for genre in favoriteGenres {
// Hip hop
```
更多关于`for-in`循环的信息,参见[For 循环](./05_Control_Flow.html#for_loops)。
更多关于 `for-in` 循环的信息,参见[For 循环](./05_Control_Flow.html#for_loops)。
Swift 的`Set`类型没有确定的顺序,为了按照特定顺序来遍历一个`Set`中的值可以使用`sorted()`方法,它将返回一个有序数组,这个数组的元素排列顺序由操作符'<'对元素进行比较的结果来确定。
Swift 的 `Set` 类型没有确定的顺序,为了按照特定顺序来遍历一个 `Set` 中的值可以使用 `sorted()` 方法,它将返回一个有序数组,这个数组的元素排列顺序由操作符'<'对元素进行比较的结果来确定。
```swift
for genre in favoriteGenres.sorted() {
@ -440,19 +440,19 @@ for genre in favoriteGenres.sorted() {
<a name="performing_set_operations"></a>
## 集合操作
你可以高效地完成`Set`的一些基本操作,比如把两个集合组合到一起,判断两个集合共有元素,或者判断两个集合是否全包含,部分包含或者不相交。
你可以高效地完成 `Set` 的一些基本操作,比如把两个集合组合到一起,判断两个集合共有元素,或者判断两个集合是否全包含,部分包含或者不相交。
<a name="fundamental_set_operations"></a>
### 基本集合操作
下面的插图描述了两个集合-`a``b`-以及通过阴影部分的区域显示集合各种操作的结果。
下面的插图描述了两个集合-`a``b`-以及通过阴影部分的区域显示集合各种操作的结果。
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/setVennDiagram_2x.png)
* 使用`intersection(_:)`方法根据两个集合中都包含的值创建的一个新的集合。
* 使用`symmetricDifference(_:)`方法根据在一个集合中但不在两个集合中的值创建一个新的集合。
* 使用`union(_:)`方法根据两个集合的值创建一个新的集合。
* 使用`subtracting(_:)`方法根据不在该集合中的值创建一个新的集合。
* 使用 `intersection(_:)` 方法根据两个集合中都包含的值创建的一个新的集合。
* 使用 `symmetricDifference(_:)` 方法根据在一个集合中但不在两个集合中的值创建一个新的集合。
* 使用 `union(_:)` 方法根据两个集合的值创建一个新的集合。
* 使用 `subtracting(_:)` 方法根据不在该集合中的值创建一个新的集合。
```swift
let oddDigits: Set = [1, 3, 5, 7, 9]
@ -472,15 +472,15 @@ oddDigits. symmetricDifference(singleDigitPrimeNumbers).sorted()
<a name="set_membership_and_equality"></a>
### 集合成员关系和相等
下面的插图描述了三个集合-`a`,`b``c`,以及通过重叠区域表述集合间共享的元素。集合`a`是集合`b`的父集合,因为`a`包含了`b`中所有的元素,相反的,集合`b`是集合`a`的子集合,因为属于`b`的元素也被`a`包含。集合`b`和集合`c`彼此不关联,因为它们之间没有共同的元素。
下面的插图描述了三个集合-`a`,`b``c`,以及通过重叠区域表述集合间共享的元素。集合 `a` 是集合 `b` 的父集合,因为 `a` 包含了 `b` 中所有的元素,相反的,集合 `b` 是集合 `a` 的子集合,因为属于 `b` 的元素也被 `a` 包含。集合 `b` 和集合 `c` 彼此不关联,因为它们之间没有共同的元素。
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/setEulerDiagram_2x.png)
* 使用“是否相等”运算符(`==`)来判断两个集合是否包含全部相同的值。
* 使用`isSubset(of:)`方法来判断一个集合中的值是否也被包含在另外一个集合中。
* 使用`isSuperset(of:)`方法来判断一个集合中包含另一个集合中所有的值。
* 使用`isStrictSubset(of:)`或者`isStrictSuperset(of:)`方法来判断一个集合是否是另外一个集合的子集合或者父集合并且两个集合并不相等。
* 使用`isDisjoint(with:)`方法来判断两个集合是否不含有相同的值(是否没有交集)。
* 使用 `isSubset(of:)` 方法来判断一个集合中的值是否也被包含在另外一个集合中。
* 使用 `isSuperset(of:)` 方法来判断一个集合中包含另一个集合中所有的值。
* 使用 `isStrictSubset(of:)` 或者 `isStrictSuperset(of:)` 方法来判断一个集合是否是另外一个集合的子集合或者父集合并且两个集合并不相等。
* 使用 `isDisjoint(with:)` 方法来判断两个集合是否不含有相同的值(是否没有交集)。
```swift
let houseAnimals: Set = ["🐶", "🐱"]
@ -500,22 +500,22 @@ farmAnimals.isDisjoint(with: cityAnimals)
*字典*是一种存储多个相同类型的值的容器。每个值value都关联唯一的键key键作为字典中的这个值数据的标识符。和数组中的数据项不同字典中的数据项并没有具体顺序。我们在需要通过标识符访问数据的时候使用字典这种方法很大程度上和我们在现实世界中使用字典查字义的方法一样。
> 注意
> 注意
>
> Swift 的`Dictionary`类型被桥接到`Foundation``NSDictionary`类。
> Swift 的 `Dictionary` 类型被桥接到 `Foundation``NSDictionary` 类。
>
> 更多关于在`Foundation``Cocoa`中使用`Dictionary`类型的信息,参见 [*Using Swift with Cocoa and Obejective-C(Swift 4.1)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。
> 更多关于在 `Foundation``Cocoa` 中使用 `Dictionary` 类型的信息,参见 [*Using Swift with Cocoa and Obejective-C(Swift 4.1)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。
<a name="dictionary_type_shorthand_syntax"></a>
### 字典类型简化语法
Swift 的字典使用`Dictionary<Key, Value>`定义,其中`Key`是字典中键的数据类型,`Value`是字典中对应于这些键所存储值的数据类型。
Swift 的字典使用 `Dictionary<Key, Value>` 定义,其中 `Key` 是字典中键的数据类型,`Value` 是字典中对应于这些键所存储值的数据类型。
> 注意
> 注意
>
> 一个字典的`Key`类型必须遵循`Hashable`协议,就像`Set`的值类型。
> 一个字典的 `Key` 类型必须遵循 `Hashable` 协议,就像 `Set` 的值类型。
我们也可以用`[Key: Value]`这样简化的形式去创建一个字典类型。虽然这两种形式功能上相同,但是后者是首选,并且这本指导书涉及到字典类型时通篇采用后者。
我们也可以用 `[Key: Value]` 这样简化的形式去创建一个字典类型。虽然这两种形式功能上相同,但是后者是首选,并且这本指导书涉及到字典类型时通篇采用后者。
<a name="creating_an_empty_dictionary"></a>
### 创建一个空字典
@ -527,9 +527,9 @@ var namesOfIntegers = [Int: String]()
// namesOfIntegers 是一个空的 [Int: String] 字典
```
这个例子创建了一个`[Int: String]`类型的空字典来储存整数的英语命名。它的键是`Int`型,值是`String`型。
这个例子创建了一个 `[Int: String]` 类型的空字典来储存整数的英语命名。它的键是 `Int` 型,值是 `String` 型。
如果上下文已经提供了类型信息,我们可以使用空字典字面量来创建一个空字典,记作`[:]`(中括号中放一个冒号):
如果上下文已经提供了类型信息,我们可以使用空字典字面量来创建一个空字典,记作 `[:]`(中括号中放一个冒号):
```swift
namesOfIntegers[16] = "sixteen"
@ -541,9 +541,9 @@ namesOfIntegers = [:]
<a name="creating_a_dictionary_with_a_dictionary_literal"></a>
## 用字典字面量创建字典
我们可以使用*字典字面量*来构造字典,这和我们刚才介绍过的数组字面量拥有相似语法。字典字面量是一种将一个或多个键值对写作`Dictionary`集合的快捷途径。
我们可以使用*字典字面量*来构造字典,这和我们刚才介绍过的数组字面量拥有相似语法。字典字面量是一种将一个或多个键值对写作 `Dictionary` 集合的快捷途径。
一个键值对是一个`key`和一个`value`的结合体。在字典字面量中,每一个键值对的键和值都由冒号分割。这些键值对构成一个列表,其中这些键值对由方括号包含、由逗号分割:
一个键值对是一个 `key` 和一个 `value` 的结合体。在字典字面量中,每一个键值对的键和值都由冒号分割。这些键值对构成一个列表,其中这些键值对由方括号包含、由逗号分割:
```swift
[key 1: value 1, key 2: value 2, key 3: value 3]
@ -555,38 +555,38 @@ namesOfIntegers = [:]
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
```
`airports`字典被声明为一种`[String: String]`类型,这意味着这个字典的键和值都是`String`类型。
`airports` 字典被声明为一种 `[String: String]` 类型,这意味着这个字典的键和值都是 `String` 类型。
> 注意
> 注意
>
> `airports`字典被声明为变量(用`var`关键字)而不是常量(`let`关键字)因为后来更多的机场信息会被添加到这个示例字典中。
> `airports` 字典被声明为变量(用 `var` 关键字)而不是常量(`let` 关键字)因为后来更多的机场信息会被添加到这个示例字典中。
`airports`字典使用字典字面量初始化,包含两个键值对。第一对的键是`YYZ`,值是`Toronto Pearson`。第二对的键是`DUB`,值是`Dublin`
`airports` 字典使用字典字面量初始化,包含两个键值对。第一对的键是 `YYZ`,值是 `Toronto Pearson`。第二对的键是 `DUB`,值是 `Dublin`
这个字典语句包含了两个`String: String`类型的键值对。它们对应`airports`变量声明的类型(一个只有`String`键和`String`值的字典)所以这个字典字面量的任务是构造拥有两个初始数据项的`airport`字典。
这个字典语句包含了两个 `String: String` 类型的键值对。它们对应 `airports` 变量声明的类型(一个只有 `String` 键和 `String` 值的字典)所以这个字典字面量的任务是构造拥有两个初始数据项的 `airport` 字典。
和数组一样,我们在用字典字面量构造字典时,如果它的键和值都有各自一致的类型,那么就不必写出字典的类型。
`airports`字典也可以用这种简短方式定义:
`airports` 字典也可以用这种简短方式定义:
```swift
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
```
因为这个语句中所有的键和值都各自拥有相同的数据类型Swift 可以推断出`Dictionary<String, String>``airports`字典的正确类型。
因为这个语句中所有的键和值都各自拥有相同的数据类型Swift 可以推断出 `Dictionary<String, String>``airports` 字典的正确类型。
<a name="accessing_and_modifying_a_dictionary"></a>
### 访问和修改字典
我们可以通过字典的方法和属性来访问和修改字典,或者通过使用下标语法。
和数组一样,我们可以通过字典的只读属性`count`来获取某个字典的数据项数量:
和数组一样,我们可以通过字典的只读属性 `count` 来获取某个字典的数据项数量:
```swift
print("The dictionary of airports contains \(airports.count) items.")
// 打印 "The dictionary of airports contains 2 items."(这个字典有两个数据项)
```
使用布尔属性`isEmpty`作为一个缩写形式去检查`count`属性是否为`0`
使用布尔属性 `isEmpty` 作为一个缩写形式去检查 `count` 属性是否为 `0`
```swift
if airports.isEmpty {
@ -611,11 +611,11 @@ airports["LHR"] = "London Heathrow"
// "LHR"对应的值 被改为 "London Heathrow
```
作为另一种下标方法,字典的`updateValue(_:forKey:)`方法可以设置或者更新特定键对应的值。就像上面所示的下标示例,`updateValue(_:forKey:)`方法在这个键不存在对应值的时候会设置新值或者在存在时更新已存在的值。和上面的下标方法不同的,`updateValue(_:forKey:)`这个方法返回更新值之前的原值。这样使得我们可以检查更新是否成功。
作为另一种下标方法,字典的 `updateValue(_:forKey:)` 方法可以设置或者更新特定键对应的值。就像上面所示的下标示例,`updateValue(_:forKey:)` 方法在这个键不存在对应值的时候会设置新值或者在存在时更新已存在的值。和上面的下标方法不同的,`updateValue(_:forKey:)` 这个方法返回更新值之前的原值。这样使得我们可以检查更新是否成功。
`updateValue(_:forKey:)`方法会返回对应值的类型的可选值。举例来说:对于存储`String`值的字典,这个函数会返回一个`String?`或者“可选 `String`”类型的值。
`updateValue(_:forKey:)` 方法会返回对应值的类型的可选值。举例来说:对于存储 `String` 值的字典,这个函数会返回一个 `String?` 或者“可选 `String`”类型的值。
如果有值存在于更新前,则这个可选值包含了旧值,否则它将会是`nil`
如果有值存在于更新前,则这个可选值包含了旧值,否则它将会是 `nil`
```swift
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
@ -624,7 +624,7 @@ if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
// 输出 "The old value for DUB was Dublin."
```
我们也可以使用下标语法来在字典中检索特定键对应的值。因为有可能请求的键没有对应的值存在,字典的下标访问会返回对应值的类型的可选值。如果这个字典包含请求键所对应的值,下标会返回一个包含这个存在值的可选值,否则将返回`nil`
我们也可以使用下标语法来在字典中检索特定键对应的值。因为有可能请求的键没有对应的值存在,字典的下标访问会返回对应值的类型的可选值。如果这个字典包含请求键所对应的值,下标会返回一个包含这个存在值的可选值,否则将返回 `nil`
```swift
if let airportName = airports["DUB"] {
@ -635,7 +635,7 @@ if let airportName = airports["DUB"] {
// 打印 "The name of the airport is Dublin Airport."
```
我们还可以使用下标语法来通过给某个键的对应值赋值为`nil`来从字典里移除一个键值对:
我们还可以使用下标语法来通过给某个键的对应值赋值为 `nil` 来从字典里移除一个键值对:
```swift
airports["APL"] = "Apple Internation"
@ -644,7 +644,7 @@ airports["APL"] = nil
// APL 现在被移除了
```
此外,`removeValue(forKey:)`方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的值或者在没有值的情况下返回`nil`
此外,`removeValue(forKey:)` 方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的值或者在没有值的情况下返回 `nil`
```swift
if let removedValue = airports. removeValue(forKey: "DUB") {
@ -658,7 +658,7 @@ if let removedValue = airports. removeValue(forKey: "DUB") {
<a name="iterating_over_a_dictionary"></a>
### 字典遍历
我们可以使用`for-in`循环来遍历某个字典中的键值对。每一个字典中的数据项都以`(key, value)`元组形式返回,并且我们可以使用临时常量或者变量来分解这些元组:
我们可以使用 `for-in` 循环来遍历某个字典中的键值对。每一个字典中的数据项都以 `(key, value)` 元组形式返回,并且我们可以使用临时常量或者变量来分解这些元组:
```swift
for (airportCode, airportName) in airports {
@ -668,9 +668,9 @@ for (airportCode, airportName) in airports {
// LHR: London Heathrow
```
更多关于`for-in`循环的信息,参见[For 循环](./05_Control_Flow.html#for_loops)。
更多关于 `for-in` 循环的信息,参见[For 循环](./05_Control_Flow.html#for_loops)。
通过访问`keys`或者`values`属性,我们也可以遍历字典的键或者值:
通过访问 `keys` 或者 `values` 属性,我们也可以遍历字典的键或者值:
```swift
for airportCode in airports.keys {
@ -686,7 +686,7 @@ for airportName in airports.values {
// Airport name: London Heathrow
```
如果我们只是需要使用某个字典的键集合或者值集合来作为某个接受`Array`实例的 API 的参数,可以直接使用`keys`或者`values`属性构造一个新数组:
如果我们只是需要使用某个字典的键集合或者值集合来作为某个接受 `Array` 实例的 API 的参数,可以直接使用 `keys` 或者 `values` 属性构造一个新数组:
```swift
let airportCodes = [String](airports.keys)
@ -696,4 +696,4 @@ let airportNames = [String](airports.values)
// airportNames 是 ["Toronto Pearson", "London Heathrow"]
```
Swift 的字典类型是无序集合类型。为了以特定的顺序遍历字典的键或值,可以对字典的`keys``values`属性使用`sorted()`方法。
Swift 的字典类型是无序集合类型。为了以特定的顺序遍历字典的键或值,可以对字典的 `keys``values` 属性使用 `sorted()` 方法。

View File

@ -38,11 +38,11 @@
- [提前退出](#early_exit)
- [检测 API 可用性](#checking_api_availability)
Swift提供了多种流程控制结构包括可以多次执行任务的`while`循环,基于特定条件选择执行不同代码分支的`if``guard``switch`语句,还有控制流程跳转到其他代码位置的`break``continue`语句。
Swift 提供了多种流程控制结构,包括可以多次执行任务的 `while` 循环,基于特定条件选择执行不同代码分支的 `if``guard``switch` 语句,还有控制流程跳转到其他代码位置的 `break``continue` 语句。
Swift 还提供了`for-in`循环用来更简单地遍历数组Array字典Dictionary区间Range字符串String和其他序列类型。
Swift 还提供了 `for-in` 循环用来更简单地遍历数组Array字典Dictionary区间Range字符串String和其他序列类型。
Swift 的`switch`语句比 C 语言中更加强大。case 还可以匹配很多不同的模式包括范围匹配元组tuple和特定类型匹配。`switch`语句的 case 中匹配的值可以声明为临时常量或变量,在 case 作用域内使用,也可以配合`where`来描述更复杂的匹配条件。
Swift 的 `switch` 语句比 C 语言中更加强大。case 还可以匹配很多不同的模式包括范围匹配元组tuple和特定类型匹配。`switch` 语句的 case 中匹配的值可以声明为临时常量或变量,在 case 作用域内使用,也可以配合 `where` 来描述更复杂的匹配条件。
<a name="for_in_loops"></a>
## For-In 循环
@ -106,7 +106,7 @@ print("\(base) to the power of \(power) is \(answer)")
// 输出 "3 to the power of 10 is 59049"
```
这个例子计算 base 这个数的 power 次幂(本例中,是 `3``10` 次幂),从 `1` `3``0` 次幂)开始做 `3` 的乘法, 进行 `10` 次,使用 `1``10` 的闭区间循环。这个计算并不需要知道每一次循环中计数器具体的值,只需要执行了正确的循环次数即可。下划线符号 `_` (替代循环中的变量)能够忽略当前值,并且不提供循环遍历时对值的访问。
这个例子计算 base 这个数的 power 次幂(本例中,是 `3``10` 次幂),从 `1``3``0` 次幂)开始做 `3` 的乘法, 进行 `10` 次,使用 `1``10` 的闭区间循环。这个计算并不需要知道每一次循环中计数器具体的值,只需要执行了正确的循环次数即可。下划线符号 `_` (替代循环中的变量)能够忽略当前值,并且不提供循环遍历时对值的访问。
在某些情况下,你可能不想使用闭区间,包括两个端点。想象一下,你在一个手表上绘制分钟的刻度线。总共 `60` 个刻度,从 `0` 分开始。使用半开区间运算符(`..<`)来表示一个左闭右开的区间。有关区间的更多信息,请参阅[区间运算符](./02_Basic_Operators.html#range_operators)。
@ -117,7 +117,7 @@ for tickMark in 0..<minutes {
}
```
一些用户可能在其UI中可能需要较少的刻度。他们可以每5分钟作为一个刻度。使用 `stride(from:to:by:)` 函数跳过不需要的标记。
一些用户可能在其 UI 中可能需要较少的刻度。他们可以每5分钟作为一个刻度。使用 `stride(from:to:by:)` 函数跳过不需要的标记。
```
let minuteInterval = 5
@ -139,15 +139,15 @@ for tickMark in stride(from: 3, through: hours, by: hourInterval) {
<a name="while_loops"></a>
## While 循环
`while`循环会一直运行一段语句直到条件变成`false`。这类循环适合使用在第一次迭代前迭代次数未知的情况下。Swift 提供两种`while`循环形式:
`while` 循环会一直运行一段语句直到条件变成 `false`。这类循环适合使用在第一次迭代前迭代次数未知的情况下。Swift 提供两种 `while` 循环形式:
* `while`循环,每次在循环开始时计算条件是否符合;
* `repeat-while`循环,每次在循环结束时计算条件是否符合。
* `while` 循环,每次在循环开始时计算条件是否符合;
* `repeat-while` 循环,每次在循环结束时计算条件是否符合。
<a name="while"></a>
### While
`while`循环从计算一个条件开始。如果条件为`true`,会重复运行一段语句,直到条件变为`false`
`while` 循环从计算一个条件开始。如果条件为 `true`,会重复运行一段语句,直到条件变为 `false`
下面是 `while` 循环的一般格式:
@ -168,7 +168,7 @@ while condition {
* 如果在某轮结束,你移动到了梯子的底部,可以顺着梯子爬上去;
* 如果在某轮结束,你移动到了蛇的头部,你会顺着蛇的身体滑下去。
游戏盘面可以使用一个`Int`数组来表达。数组的长度由一个`finalSquare`常量储存,用来初始化数组和检测最终胜利条件。游戏盘面由 26 个 `Int` 0 值初始化,而不是 25 个(由`0``25`,一共 26 个):
游戏盘面可以使用一个 `Int` 数组来表达。数组的长度由一个 `finalSquare` 常量储存,用来初始化数组和检测最终胜利条件。游戏盘面由 26 个 `Int` 0 值初始化,而不是 25 个(由 `0``25`,一共 26 个):
```swift
let finalSquare = 25
@ -182,7 +182,7 @@ board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
```
3 号方格是梯子的底部,会让你向上移动到 11 号方格,我们使用`board[03]`等于`+08`(来表示`11``3`之间的差值)。为了对齐语句,这里使用了一元正运算符(`+i`)和一元负运算符(`-i`),并且小于 10 的数字都使用 0 补齐(这些语法的技巧不是必要的,只是为了让代码看起来更加整洁)。
3 号方格是梯子的底部,会让你向上移动到 11 号方格,我们使用 `board[03]` 等于 `+08`(来表示 `11``3` 之间的差值)。为了对齐语句,这里使用了一元正运算符(`+i`)和一元负运算符(`-i`),并且小于 10 的数字都使用 0 补齐(这些语法的技巧不是必要的,只是为了让代码看起来更加整洁)。
玩家由左下角空白处编号为 0 的方格开始游戏。玩家第一次掷骰子后才会进入游戏盘面:
@ -203,28 +203,28 @@ while square < finalSquare {
print("Game over!")
```
本例中使用了最简单的方法来模拟掷骰子。 `diceRoll`的值并不是一个随机数,而是以`0`为初始值,之后每一次`while`循环,`diceRoll`的值增加 1 ,然后检测是否超出了最大值。当`diceRoll`的值等于 7 时,就超过了骰子的最大值,会被重置为`1`。所以`diceRoll`的取值顺序会一直是 `1` `2``3``4``5``6``1``2` 等。
本例中使用了最简单的方法来模拟掷骰子。`diceRoll` 的值并不是一个随机数,而是以 `0` 为初始值,之后每一次 `while` 循环,`diceRoll` 的值增加 1 ,然后检测是否超出了最大值。当 `diceRoll` 的值等于 7 时,就超过了骰子的最大值,会被重置为 `1`。所以 `diceRoll` 的取值顺序会一直是 `1``2``3``4``5``6``1``2` 等。
掷完骰子后,玩家向前移动`diceRoll`个方格,如果玩家移动超过了第 25 个方格,这个时候游戏将会结束,为了应对这种情况,代码会首先判断`square`的值是否小于`board``count`属性,只有小于才会在`board[square]`上增加`square`,来向前或向后移动(遇到了梯子或者蛇)。
掷完骰子后,玩家向前移动 `diceRoll` 个方格,如果玩家移动超过了第 25 个方格,这个时候游戏将会结束,为了应对这种情况,代码会首先判断 `square` 的值是否小于 `board``count` 属性,只有小于才会在 `board[square]` 上增加 `square`,来向前或向后移动(遇到了梯子或者蛇)。
> 注意
>
> 如果没有这个检测(`square < board.count``board[square]`可能会越界访问`board`数组,导致错误。
> 如果没有这个检测(`square < board.count``board[square]` 可能会越界访问 `board` 数组,导致错误。
当本轮`while`循环运行完毕,会再检测循环条件是否需要再运行一次循环。如果玩家移动到或者超过第 25 个方格,循环条件结果为`false`,此时游戏结束。
当本轮 `while` 循环运行完毕,会再检测循环条件是否需要再运行一次循环。如果玩家移动到或者超过第 25 个方格,循环条件结果为 `false`,此时游戏结束。
`while` 循环比较适合本例中的这种情况,因为在 `while` 循环开始时,我们并不知道游戏要跑多久,只有在达成指定条件时循环才会结束。
<a name="repeat_while"></a>
### Repeat-While
`while`循环的另外一种形式是`repeat-while`,它和`while`的区别是在判断循环条件之前,先执行一次循环的代码块。然后重复循环直到条件为`false`
`while` 循环的另外一种形式是 `repeat-while`,它和 `while` 的区别是在判断循环条件之前,先执行一次循环的代码块。然后重复循环直到条件为 `false`
> 注意
>
> Swift语言的`repeat-while`循环和其他语言中的`do-while`循环是类似的。
> Swift 语言的 `repeat-while` 循环和其他语言中的 `do-while` 循环是类似的。
下面是 `repeat-while`循环的一般格式:
下面是 `repeat-while` 循环的一般格式:
```swift
repeat {
@ -232,7 +232,7 @@ repeat {
} while condition
```
还是*蛇和梯子*的游戏,使用`repeat-while`循环来替代`while`循环。`finalSquare``board``square``diceRoll`的值初始化同`while`循环时一样:
还是*蛇和梯子*的游戏,使用 `repeat-while` 循环来替代 `while` 循环。`finalSquare``board``square``diceRoll` 的值初始化同 `while` 循环时一样:
``` swift
let finalSquare = 25
@ -243,9 +243,9 @@ var square = 0
var diceRoll = 0
```
`repeat-while`的循环版本,循环中*第一步*就需要去检测是否在梯子或者蛇的方块上。没有梯子会让玩家直接上到第 25 个方格,所以玩家不会通过梯子直接赢得游戏。这样在循环开始时先检测是否踩在梯子或者蛇上是安全的。
`repeat-while` 的循环版本,循环中*第一步*就需要去检测是否在梯子或者蛇的方块上。没有梯子会让玩家直接上到第 25 个方格,所以玩家不会通过梯子直接赢得游戏。这样在循环开始时先检测是否踩在梯子或者蛇上是安全的。
游戏开始时,玩家在第 0 个方格上,`board[0]`一直等于 0 不会有什么影响:
游戏开始时,玩家在第 0 个方格上,`board[0]` 一直等于 0 不会有什么影响:
```swift
repeat {
@ -260,21 +260,21 @@ repeat {
print("Game over!")
```
检测完玩家是否踩在梯子或者蛇上之后,开始掷骰子,然后玩家向前移动`diceRoll`个方格,本轮循环结束。
检测完玩家是否踩在梯子或者蛇上之后,开始掷骰子,然后玩家向前移动 `diceRoll` 个方格,本轮循环结束。
循环条件(`while square < finalSquare`)和`while`方式相同,但是只会在循环结束后进行计算。在这个游戏中,`repeat-while`表现得比`while`循环更好。`repeat-while`方式会在条件判断`square`没有超出后直接运行`square += board[square]`,这种方式可以比起前面 `while` 循环的版本,可以省去数组越界的检查。
循环条件(`while square < finalSquare`)和 `while` 方式相同,但是只会在循环结束后进行计算。在这个游戏中,`repeat-while` 表现得比 `while` 循环更好。`repeat-while` 方式会在条件判断 `square` 没有超出后直接运行 `square += board[square]`,这种方式可以比起前面 `while` 循环的版本,可以省去数组越界的检查。
<a name="conditional_statement"></a>
## 条件语句
根据特定的条件执行特定的代码通常是十分有用的。当错误发生时,你可能想运行额外的代码;或者,当值太大或太小时,向用户显示一条消息。要实现这些功能,你就需要使用*条件语句*。
Swift 提供两种类型的条件语句:`if`语句和`switch`语句。通常,当条件较为简单且可能的情况很少时,使用`if`语句。而`switch`语句更适用于条件较复杂、有更多排列组合的时候。并且`switch`在需要用到模式匹配pattern-matching的情况下会更有用。
Swift 提供两种类型的条件语句:`if` 语句和 `switch` 语句。通常,当条件较为简单且可能的情况很少时,使用 `if` 语句。而 `switch` 语句更适用于条件较复杂、有更多排列组合的时候。并且 `switch` 在需要用到模式匹配pattern-matching的情况下会更有用。
<a name="if"></a>
### If
`if`语句最简单的形式就是只包含一个条件,只有该条件为`true`时,才执行相关代码:
`if` 语句最简单的形式就是只包含一个条件,只有该条件为 `true` 时,才执行相关代码:
```swift
var temperatureInFahrenheit = 30
@ -284,9 +284,9 @@ if temperatureInFahrenheit <= 32 {
// 输出 "It's very cold. Consider wearing a scarf."
```
上面的例子会判断温度是否小于等于 32 华氏度(水的冰点)。如果是,则打印一条消息;否则,不打印任何消息,继续执行`if`块后面的代码。
上面的例子会判断温度是否小于等于 32 华氏度(水的冰点)。如果是,则打印一条消息;否则,不打印任何消息,继续执行 `if` 块后面的代码。
当然,`if`语句允许二选一执行,叫做`else`从句。也就是当条件为`false`时,执行 *else 语句*
当然,`if` 语句允许二选一执行,叫做 `else` 从句。也就是当条件为 `false` 时,执行 *else 语句*
```swift
temperatureInFahrenheit = 40
@ -298,9 +298,9 @@ if temperatureInFahrenheit <= 32 {
// 输出 "It's not that cold. Wear a t-shirt."
```
显然,这两条分支中总有一条会被执行。由于温度已升至 40 华氏度,不算太冷,没必要再围围巾。因此,`else`分支就被触发了。
显然,这两条分支中总有一条会被执行。由于温度已升至 40 华氏度,不算太冷,没必要再围围巾。因此,`else` 分支就被触发了。
你可以把多个`if`语句链接在一起,来实现更多分支:
你可以把多个 `if` 语句链接在一起,来实现更多分支:
```swift
temperatureInFahrenheit = 90
@ -314,9 +314,9 @@ if temperatureInFahrenheit <= 32 {
// 输出 "It's really warm. Don't forget to wear sunscreen."
```
在上面的例子中,额外的`if`语句用于判断是不是特别热。而最后的`else`语句被保留了下来,用于打印既不冷也不热时的消息。
在上面的例子中,额外的 `if` 语句用于判断是不是特别热。而最后的 `else` 语句被保留了下来,用于打印既不冷也不热时的消息。
实际上,当不需要完整判断情况的时候,最后的`else`语句是可选的:
实际上,当不需要完整判断情况的时候,最后的 `else` 语句是可选的:
```swift
temperatureInFahrenheit = 72
@ -327,14 +327,14 @@ if temperatureInFahrenheit <= 32 {
}
```
在这个例子中,由于既不冷也不热,所以不会触发`if``else if`分支,也就不会打印任何消息。
在这个例子中,由于既不冷也不热,所以不会触发 `if``else if` 分支,也就不会打印任何消息。
<a name="switch"></a>
### Switch
`switch`语句会尝试把某个值与若干个模式pattern进行匹配。根据第一个匹配成功的模式`switch`语句会执行对应的代码。当有可能的情况较多时,通常用`switch`语句替换`if`语句。
`switch` 语句会尝试把某个值与若干个模式pattern进行匹配。根据第一个匹配成功的模式`switch` 语句会执行对应的代码。当有可能的情况较多时,通常用 `switch` 语句替换 `if` 语句。
`switch`语句最简单的形式就是把某个值与一个或若干个相同类型的值作比较:
`switch` 语句最简单的形式就是把某个值与一个或若干个相同类型的值作比较:
```swift
switch some value to consider {
@ -348,13 +348,13 @@ default:
}
```
`switch`语句由*多个 case* 构成,每个由`case`关键字开始。为了匹配某些更特定的值Swift 提供了几种方法来进行更复杂的模式匹配,这些模式将在本节的稍后部分提到。
`switch` 语句由*多个 case* 构成,每个由 `case` 关键字开始。为了匹配某些更特定的值Swift 提供了几种方法来进行更复杂的模式匹配,这些模式将在本节的稍后部分提到。
与`if`语句类似,每一个 case 都是代码执行的一条分支。`switch`语句会决定哪一条分支应该被执行,这个流程被称作根据给定的值*切换(switching)*。
`if` 语句类似,每一个 case 都是代码执行的一条分支。`switch` 语句会决定哪一条分支应该被执行,这个流程被称作根据给定的值*切换(switching)*。
`switch`语句必须是完备的。这就是说,每一个可能的值都必须至少有一个 case 分支与之对应。在某些不可能涵盖所有值的情况下,你可以使用默认(`default`)分支来涵盖其它所有没有对应的值,这个默认分支必须在`switch`语句的最后面。
`switch` 语句必须是完备的。这就是说,每一个可能的值都必须至少有一个 case 分支与之对应。在某些不可能涵盖所有值的情况下,你可以使用默认(`default`)分支来涵盖其它所有没有对应的值,这个默认分支必须在 `switch` 语句的最后面。
下面的例子使用`switch`语句来匹配一个名为`someCharacter`的小写字符:
下面的例子使用 `switch` 语句来匹配一个名为 `someCharacter` 的小写字符:
```swift
let someCharacter: Character = "z"
@ -369,17 +369,17 @@ default:
// 输出 "The last letter of the alphabet"
```
在这个例子中,第一个 case 分支用于匹配第一个英文字母`a`,第二个 case 分支用于匹配最后一个字母`z`。
因为`switch`语句必须有一个case分支用于覆盖所有可能的字符而不仅仅是所有的英文字母所以switch语句使用`default`分支来匹配除了`a``z`外的所有值这个分支保证了swith语句的完备性。
在这个例子中,第一个 case 分支用于匹配第一个英文字母 `a`,第二个 case 分支用于匹配最后一个字母 `z`。
因为 `switch` 语句必须有一个 case 分支用于覆盖所有可能的字符,而不仅仅是所有的英文字母,所以 switch 语句使用 `default` 分支来匹配除了 `a``z` 外的所有值,这个分支保证了 swith 语句的完备性。
<a name="no_implicit_fallthrough"></a>
#### 不存在隐式的贯穿
与 C 和 Objective-C 中的`switch`语句不同,在 Swift 中,当匹配的 case 分支中的代码执行完毕后,程序会终止`switch`语句,而不会继续执行下一个 case 分支。这也就是说,不需要在 case 分支中显式地使用`break`语句。这使得`switch`语句更安全、更易用,也避免了因忘记写`break`语句而产生的错误。
与 C 和 Objective-C 中的 `switch` 语句不同,在 Swift 中,当匹配的 case 分支中的代码执行完毕后,程序会终止 `switch` 语句,而不会继续执行下一个 case 分支。这也就是说,不需要在 case 分支中显式地使用 `break` 语句。这使得 `switch` 语句更安全、更易用,也避免了因忘记写 `break` 语句而产生的错误。
> 注意
>
虽然在Swift`break`不是必须的,但你依然可以在 case 分支中的代码执行完毕前使用`break`跳出,详情请参见[Switch 语句中的 break](#break_in_a_switch_statement)。
虽然在 Swift`break` 不是必须的,但你依然可以在 case 分支中的代码执行完毕前使用 `break` 跳出,详情请参见[Switch 语句中的 break](#break_in_a_switch_statement)。
每一个 case 分支都*必须*包含至少一条语句。像下面这样书写代码是无效的,因为第一个 case 分支是空的:
@ -395,9 +395,9 @@ default:
// 这段代码会报编译错误
```
不像 C 语言里的`switch`语句,在 Swift 中,`switch`语句不会一起匹配`"a"``"A"`。相反的,上面的代码会引起编译期错误:`case "a": 不包含任何可执行语句`——这就避免了意外地从一个 case 分支贯穿到另外一个,使得代码更安全、也更直观。
不像 C 语言里的 `switch` 语句,在 Swift 中,`switch` 语句不会一起匹配 `"a"``"A"`。相反的,上面的代码会引起编译期错误:`case "a": 不包含任何可执行语句 `——这就避免了意外地从一个 case 分支贯穿到另外一个,使得代码更安全、也更直观。
为了让单个case同时匹配`a``A`,可以将这个两个值组合成一个复合匹配,并且用逗号分开:
为了让单个 case 同时匹配 `a``A`,可以将这个两个值组合成一个复合匹配,并且用逗号分开:
```swift
let anotherCharacter: Character = "a"
switch anotherCharacter {
@ -412,7 +412,7 @@ default:
> 注意
>
> 如果想要显式贯穿case分支请使用`fallthrough`语句,详情请参考[贯穿](#fallthrough)。
> 如果想要显式贯穿 case 分支,请使用 `fallthrough` 语句,详情请参考[贯穿](#fallthrough)。
<a name="interval_matching"></a>
#### 区间匹配
@ -441,14 +441,14 @@ print("There are \(naturalCount) \(countedThings).")
// 输出 "There are dozens of moons orbiting Saturn."
```
在上例中,`approximateCount`在一个`switch`声明中被评估。每一个`case`都与之进行比较。因为`approximateCount`落在了 12 到 100 的区间,所以`naturalCount`等于`"dozens of"`值,并且此后的执行跳出了`switch`语句。
在上例中,`approximateCount` 在一个 `switch` 声明中被评估。每一个 `case` 都与之进行比较。因为 `approximateCount` 落在了 12 到 100 的区间,所以 `naturalCount` 等于 `"dozens of"` 值,并且此后的执行跳出了 `switch` 语句。
<a name="tuples"></a>
#### 元组
我们可以使用元组在同一个`switch`语句中测试多个值。元组中的元素可以是值,也可以是区间。另外,使用下划线(`_`)来匹配所有可能的值。
我们可以使用元组在同一个 `switch` 语句中测试多个值。元组中的元素可以是值,也可以是区间。另外,使用下划线(`_`)来匹配所有可能的值。
下面的例子展示了如何使用一个`(Int, Int)`类型的元组来分类下图中的点(x, y)
下面的例子展示了如何使用一个 `(Int, Int)` 类型的元组来分类下图中的点(x, y)
```swift
let somePoint = (1, 1)
@ -469,16 +469,16 @@ default:
![image](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/coordinateGraphSimple_2x.png)
在上面的例子中,`switch`语句会判断某个点是否是原点(0, 0),是否在红色的x轴上,是否在橘黄色的y轴上是否在一个以原点为中心的4x4的蓝色矩形里或者在这个矩形外面。
在上面的例子中,`switch` 语句会判断某个点是否是原点(0, 0),是否在红色的 x 轴上,是否在橘黄色的 y 轴上是否在一个以原点为中心的4x4的蓝色矩形里或者在这个矩形外面。
不像 C 语言Swift 允许多个 case 匹配同一个值。实际上,在这个例子中,点(0, 0)可以匹配所有_四个 case_。但是如果存在多个匹配那么只会执行第一个被匹配到的 case 分支。考虑点(0, 0)会首先匹配`case (0, 0)`,因此剩下的能够匹配的分支都会被忽视掉。
不像 C 语言Swift 允许多个 case 匹配同一个值。实际上,在这个例子中,点(0, 0)可以匹配所有_四个 case_。但是如果存在多个匹配那么只会执行第一个被匹配到的 case 分支。考虑点(0, 0)会首先匹配 `case (0, 0)`,因此剩下的能够匹配的分支都会被忽视掉。
<a name="value_bindings"></a>
#### 值绑定Value Bindings
case 分支允许将匹配的值声明为临时常量或变量并且在case分支体内使用 —— 这种行为被称为*值绑定*value binding因为匹配的值在case分支体内与临时的常量或变量绑定。
case 分支允许将匹配的值声明为临时常量或变量,并且在 case 分支体内使用 —— 这种行为被称为*值绑定*value binding因为匹配的值在 case 分支体内,与临时的常量或变量绑定。
下面的例子将下图中的点(x, y),使用`(Int, Int)`类型的元组表示,然后分类表示:
下面的例子将下图中的点(x, y),使用 `(Int, Int)` 类型的元组表示,然后分类表示:
```swift
let anotherPoint = (2, 0)
@ -495,18 +495,18 @@ case let (x, y):
![image](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/coordinateGraphMedium_2x.png)
在上面的例子中,`switch`语句会判断某个点是否在红色的x轴上,是否在橘黄色的y轴上,或者不在坐标轴上。
在上面的例子中,`switch` 语句会判断某个点是否在红色的 x 轴上,是否在橘黄色的 y 轴上,或者不在坐标轴上。
这三个 case 都声明了常量`x``y`的占位符,用于临时获取元组`anotherPoint`的一个或两个值。第一个 case ——`case (let x, 0)`将匹配一个纵坐标为`0`的点,并把这个点的横坐标赋给临时的常量`x`。类似的,第二个 case ——`case (0, let y)`将匹配一个横坐标为`0`的点,并把这个点的纵坐标赋给临时的常量`y`。
这三个 case 都声明了常量 `x``y` 的占位符,用于临时获取元组 `anotherPoint` 的一个或两个值。第一个 case ——`case (let x, 0)` 将匹配一个纵坐标为 `0` 的点,并把这个点的横坐标赋给临时的常量 `x`。类似的,第二个 case ——`case (0, let y)` 将匹配一个横坐标为 `0` 的点,并把这个点的纵坐标赋给临时的常量 `y`。
一旦声明了这些临时的常量,它们就可以在其对应的 case 分支里使用。在这个例子中,它们用于打印给定点的类型。
请注意,这个`switch`语句不包含默认分支。这是因为最后一个 case ——`case let(x, y)`声明了一个可以匹配余下所有值的元组。这使得`switch`语句已经完备了,因此不需要再书写默认分支。
请注意,这个 `switch` 语句不包含默认分支。这是因为最后一个 case ——`case let(x, y)` 声明了一个可以匹配余下所有值的元组。这使得 `switch` 语句已经完备了,因此不需要再书写默认分支。
<a name="where"></a>
#### Where
case 分支的模式可以使用`where`语句来判断额外的条件。
case 分支的模式可以使用 `where` 语句来判断额外的条件。
下面的例子把下图中的点(x, y)进行了分类:
@ -525,16 +525,16 @@ case let (x, y):
![image](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/coordinateGraphComplex_2x.png)
在上面的例子中,`switch`语句会判断某个点是否在绿色的对角线`x == y`上,是否在紫色的对角线`x == -y`上,或者不在对角线上。
在上面的例子中,`switch` 语句会判断某个点是否在绿色的对角线 `x == y` 上,是否在紫色的对角线 `x == -y` 上,或者不在对角线上。
这三个 case 都声明了常量`x``y`的占位符,用于临时获取元组`yetAnotherPoint`的两个值。这两个常量被用作`where`语句的一部分,从而创建一个动态的过滤器(filter)。当且仅当`where`语句的条件为`true`时,匹配到的 case 分支才会被执行。
这三个 case 都声明了常量 `x``y` 的占位符,用于临时获取元组 `yetAnotherPoint` 的两个值。这两个常量被用作 `where` 语句的一部分,从而创建一个动态的过滤器(filter)。当且仅当 `where` 语句的条件为 `true` 时,匹配到的 case 分支才会被执行。
就像是值绑定中的例子,由于最后一个 case 分支匹配了余下所有可能的值,`switch`语句就已经完备了,因此不需要再书写默认分支。
就像是值绑定中的例子,由于最后一个 case 分支匹配了余下所有可能的值,`switch` 语句就已经完备了,因此不需要再书写默认分支。
<a name="compound_cases"></a>
#### 复合型 Cases
当多个条件可以使用同一种方法来处理时,可以将这几种可能放在同一个`case`后面并且用逗号隔开。当case后面的任意一种模式匹配的时候这条分支就会被匹配。并且如果匹配列表过长还可以分行书写
当多个条件可以使用同一种方法来处理时,可以将这几种可能放在同一个 `case` 后面,并且用逗号隔开。当 case 后面的任意一种模式匹配的时候,这条分支就会被匹配。并且,如果匹配列表过长,还可以分行书写:
```swift
let someCharacter: Character = "e"
@ -550,7 +550,7 @@ default:
// 输出 "e is a vowel"
```
这个`switch`语句中的第一个case匹配了英语中的五个小写元音字母。相似的第二个case匹配了英语中所有的小写辅音字母。最终`default`分支匹配了其它所有字符。
这个 `switch` 语句中的第一个 case匹配了英语中的五个小写元音字母。相似的第二个 case 匹配了英语中所有的小写辅音字母。最终,`default` 分支匹配了其它所有字符。
复合匹配同样可以包含值绑定。复合匹配里所有的匹配模式,都必须包含相同的值绑定。并且每一个绑定都必须获取到相同类型的值。这保证了,无论复合匹配中的哪个模式发生了匹配,分支体内的代码,都能获取到绑定的值,并且绑定的值都有一样的类型。
```swift
@ -566,7 +566,7 @@ default:
```
上面的case有两个模式`(let distance, 0)`匹配了在x轴上的值,`(0, let distance)`匹配了在y轴上的值。两个模式都绑定了`distance`,并且`distance`在两种模式下都是整型——这意味着分支体内的代码只要case匹配都可以获取到`distance`值
上面的 case 有两个模式:`(let distance, 0)` 匹配了在 x 轴上的值,`(0, let distance)` 匹配了在 y 轴上的值。两个模式都绑定了 `distance`,并且 `distance` 在两种模式下,都是整型——这意味着分支体内的代码,只要 case 匹配,都可以获取到 `distance`
<a name="control_transfer_statements"></a>
## 控制转移语句
@ -579,12 +579,12 @@ default:
- `return`
- `throw`
我们将会在下面讨论`continue`、`break``fallthrough`语句。`return`语句将会在[函数](./06_Functions.html)章节讨论,`throw`语句会在[错误抛出](./18_Error_Handling.html#throwing_errors)章节讨论。
我们将会在下面讨论 `continue`、`break``fallthrough` 语句。`return` 语句将会在[函数](./06_Functions.html)章节讨论,`throw` 语句会在[错误抛出](./18_Error_Handling.html#throwing_errors)章节讨论。
<a name="continue"></a>
### Continue
`continue`语句告诉一个循环体立刻停止本次循环,重新开始下次循环。就好像在说“本次循环我已经执行完了”,但是并不会离开整个循环体。
`continue` 语句告诉一个循环体立刻停止本次循环,重新开始下次循环。就好像在说“本次循环我已经执行完了”,但是并不会离开整个循环体。
下面的例子把一个小写字符串中的元音字母和空格字符移除,生成了一个含义模糊的短句:
@ -603,30 +603,30 @@ print(puzzleOutput)
// 输出 "grtmndsthnklk"
```
在上面的代码中,只要匹配到元音字母或者空格字符,就调用`continue`语句,使本次循环结束,重新开始下次循环。这种行为使`switch`匹配到元音字母和空格字符时不做处理,而不是让每一个匹配到的字符都被打印。
在上面的代码中,只要匹配到元音字母或者空格字符,就调用 `continue` 语句,使本次循环结束,重新开始下次循环。这种行为使 `switch` 匹配到元音字母和空格字符时不做处理,而不是让每一个匹配到的字符都被打印。
<a name="break"></a>
### Break
`break`语句会立刻结束整个控制流的执行。`break` 可以在 `switch` 或循环语句中使用,用来提前结束`switch`或循环语句。
`break` 语句会立刻结束整个控制流的执行。`break` 可以在 `switch` 或循环语句中使用,用来提前结束 `switch` 或循环语句。
<a name="break_in_a_loop_statement"></a>
#### 循环语句中的 break
当在一个循环体中使用`break`时,会立刻中断该循环体的执行,然后跳转到表示循环体结束的大括号(`}`)后的第一行代码。不会再有本次循环的代码被执行,也不会再有下次的循环产生。
当在一个循环体中使用 `break` 时,会立刻中断该循环体的执行,然后跳转到表示循环体结束的大括号(`}`)后的第一行代码。不会再有本次循环的代码被执行,也不会再有下次的循环产生。
<a name="break_in_a_switch_statement"></a>
#### Switch 语句中的 break
当在一个`switch`代码块中使用`break`时,会立即中断该`switch`代码块的执行,并且跳转到表示`switch`代码块结束的大括号(`}`)后的第一行代码。
当在一个 `switch` 代码块中使用 `break` 时,会立即中断该 `switch` 代码块的执行,并且跳转到表示 `switch` 代码块结束的大括号(`}`)后的第一行代码。
这种特性可以被用来匹配或者忽略一个或多个分支。因为 Swift 的`switch`需要包含所有的分支而且不允许有为空的分支,有时为了使你的意图更明显,需要特意匹配或者忽略某个分支。那么当你想忽略某个分支时,可以在该分支内写上`break`语句。当那个分支被匹配到时,分支内的`break`语句立即结束`switch`代码块。
这种特性可以被用来匹配或者忽略一个或多个分支。因为 Swift 的 `switch` 需要包含所有的分支而且不允许有为空的分支,有时为了使你的意图更明显,需要特意匹配或者忽略某个分支。那么当你想忽略某个分支时,可以在该分支内写上 `break` 语句。当那个分支被匹配到时,分支内的 `break` 语句立即结束 `switch` 代码块。
>注意
>
>当一个`switch`分支仅仅包含注释时,会被报编译时错误。注释不是代码语句而且也不能让`switch`分支达到被忽略的效果。你应该使用`break`来忽略某个分支。
> 注意
>
> 当一个 `switch` 分支仅仅包含注释时,会被报编译时错误。注释不是代码语句而且也不能让 `switch` 分支达到被忽略的效果。你应该使用 `break` 来忽略某个分支。
下面的例子通过`switch`来判断一个`Character`值是否代表下面四种语言之一。为了简洁,多个值被包含在了同一个分支情况中。
下面的例子通过 `switch` 来判断一个 `Character` 值是否代表下面四种语言之一。为了简洁,多个值被包含在了同一个分支情况中。
```swift
let numberSymbol: Character = "三" // 简体中文里的数字 3
@ -651,18 +651,18 @@ if let integerValue = possibleIntegerValue {
// 输出 "The integer value of 三 is 3."
```
这个例子检查`numberSymbol`是否是拉丁,阿拉伯,中文或者泰语中的`1``4`之一。如果被匹配到,该`switch`分支语句给`Int?`类型变量`possibleIntegerValue`设置一个整数值。
这个例子检查 `numberSymbol` 是否是拉丁,阿拉伯,中文或者泰语中的 `1``4` 之一。如果被匹配到,该 `switch` 分支语句给 `Int?` 类型变量 `possibleIntegerValue` 设置一个整数值。
当`switch`代码块执行完后,接下来的代码通过使用可选绑定来判断`possibleIntegerValue`是否曾经被设置过值。因为是可选类型的缘故,`possibleIntegerValue`有一个隐式的初始值`nil`,所以仅仅当`possibleIntegerValue`曾被`switch`代码块的前四个分支中的某个设置过一个值时,可选的绑定才会被判定为成功。
`switch` 代码块执行完后,接下来的代码通过使用可选绑定来判断 `possibleIntegerValue` 是否曾经被设置过值。因为是可选类型的缘故,`possibleIntegerValue` 有一个隐式的初始值 `nil`,所以仅仅当 `possibleIntegerValue` 曾被 `switch` 代码块的前四个分支中的某个设置过一个值时,可选的绑定才会被判定为成功。
在上面的例子中,想要把`Character`所有的的可能性都枚举出来是不现实的,所以使用`default`分支来包含所有上面没有匹配到字符的情况。由于这个`default`分支不需要执行任何动作,所以它只写了一条`break`语句。一旦落入到`default`分支中后,`break`语句就完成了该分支的所有代码操作,代码继续向下,开始执行`if let`语句。
在上面的例子中,想要把 `Character` 所有的的可能性都枚举出来是不现实的,所以使用 `default` 分支来包含所有上面没有匹配到字符的情况。由于这个 `default` 分支不需要执行任何动作,所以它只写了一条 `break` 语句。一旦落入到 `default` 分支中后,`break` 语句就完成了该分支的所有代码操作,代码继续向下,开始执行 `if let` 语句。
<a name="fallthrough"></a>
### 贯穿 (Fallthrough)
在 Swift 里,`switch`语句不会从上一个 case 分支跳转到下一个 case 分支中。相反,只要第一个匹配到的 case 分支完成了它需要执行的语句,整个`switch`代码块完成了它的执行。相比之下C 语言要求你显式地插入`break`语句到每个 case 分支的末尾来阻止自动落入到下一个 case 分支中。Swift 的这种避免默认落入到下一个分支中的特性意味着它的`switch` 功能要比 C 语言的更加清晰和可预测,可以避免无意识地执行多个 case 分支从而引发的错误。
在 Swift 里,`switch` 语句不会从上一个 case 分支跳转到下一个 case 分支中。相反,只要第一个匹配到的 case 分支完成了它需要执行的语句,整个 `switch` 代码块完成了它的执行。相比之下C 语言要求你显式地插入 `break` 语句到每个 case 分支的末尾来阻止自动落入到下一个 case 分支中。Swift 的这种避免默认落入到下一个分支中的特性意味着它的 `switch` 功能要比 C 语言的更加清晰和可预测,可以避免无意识地执行多个 case 分支从而引发的错误。
如果你确实需要 C 风格的贯穿的特性,你可以在每个需要该特性的 case 分支中使用`fallthrough`关键字。下面的例子使用`fallthrough`来创建一个数字的描述语句。
如果你确实需要 C 风格的贯穿的特性,你可以在每个需要该特性的 case 分支中使用 `fallthrough` 关键字。下面的例子使用 `fallthrough` 来创建一个数字的描述语句。
```swift
let integerToDescribe = 5
@ -678,24 +678,24 @@ print(description)
// 输出 "The number 5 is a prime number, and also an integer."
```
这个例子定义了一个`String`类型的变量`description`并且给它设置了一个初始值。函数使用`switch`逻辑来判断`integerToDescribe`变量的值。当`integerToDescribe`的值属于列表中的质数之一时,该函数在`description`后添加一段文字,来表明这个数字是一个质数。然后它使用`fallthrough`关键字来“贯穿”到`default`分支中。`default`分支在`description`的最后添加一段额外的文字,至此`switch`代码块执行完了。
这个例子定义了一个 `String` 类型的变量 `description` 并且给它设置了一个初始值。函数使用 `switch` 逻辑来判断 `integerToDescribe` 变量的值。当 `integerToDescribe` 的值属于列表中的质数之一时,该函数在 `description` 后添加一段文字,来表明这个数字是一个质数。然后它使用 `fallthrough` 关键字来“贯穿”到 `default` 分支中。`default` 分支在 `description` 的最后添加一段额外的文字,至此 `switch` 代码块执行完了。
如果`integerToDescribe`的值不属于列表中的任何质数,那么它不会匹配到第一个`switch`分支。而这里没有其他特别的分支情况,所以`integerToDescribe`匹配到`default`分支中。
如果 `integerToDescribe` 的值不属于列表中的任何质数,那么它不会匹配到第一个 `switch` 分支。而这里没有其他特别的分支情况,所以 `integerToDescribe` 匹配到 `default` 分支中。
当`switch`代码块执行完后,使用`print(_:separator:terminator:)`函数打印该数字的描述。在这个例子中,数字`5`被准确的识别为了一个质数。
`switch` 代码块执行完后,使用 `print(_:separator:terminator:)` 函数打印该数字的描述。在这个例子中,数字 `5` 被准确的识别为了一个质数。
> 注意
>
> `fallthrough`关键字不会检查它下一个将会落入执行的 case 中的匹配条件。`fallthrough`简单地使代码继续连接到下一个 case 中的代码,这和 C 语言标准中的`switch`语句特性是一样的。
> `fallthrough` 关键字不会检查它下一个将会落入执行的 case 中的匹配条件。`fallthrough` 简单地使代码继续连接到下一个 case 中的代码,这和 C 语言标准中的 `switch` 语句特性是一样的。
<a name="labeled_statements"></a>
### 带标签的语句
在 Swift 中,你可以在循环体和条件语句中嵌套循环体和条件语句来创造复杂的控制流结构。并且,循环体和条件语句都可以使用`break`语句来提前结束整个代码块。因此,显式地指明`break`语句想要终止的是哪个循环体或者条件语句,会很有用。类似地,如果你有许多嵌套的循环体,显式指明`continue`语句想要影响哪一个循环体也会非常有用。
在 Swift 中,你可以在循环体和条件语句中嵌套循环体和条件语句来创造复杂的控制流结构。并且,循环体和条件语句都可以使用 `break` 语句来提前结束整个代码块。因此,显式地指明 `break` 语句想要终止的是哪个循环体或者条件语句,会很有用。类似地,如果你有许多嵌套的循环体,显式指明 `continue` 语句想要影响哪一个循环体也会非常有用。
为了实现这个目的,你可以使用标签(*statement label*)来标记一个循环体或者条件语句,对于一个条件语句,你可以使用`break`加标签的方式,来结束这个被标记的语句。对于一个循环语句,你可以使用`break`或者`continue`加标签,来结束或者继续这条被标记语句的执行。
为了实现这个目的,你可以使用标签(*statement label*)来标记一个循环体或者条件语句,对于一个条件语句,你可以使用 `break` 加标签的方式,来结束这个被标记的语句。对于一个循环语句,你可以使用 `break` 或者 `continue` 加标签,来结束或者继续这条被标记语句的执行。
声明一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签,作为这个语句的前导关键字(introducor keyword),并且该标签后面跟随一个冒号。下面是一个针对`while`循环体的标签语法,同样的规则适用于所有的循环体和条件语句。
声明一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签,作为这个语句的前导关键字(introducor keyword),并且该标签后面跟随一个冒号。下面是一个针对 `while` 循环体的标签语法,同样的规则适用于所有的循环体和条件语句。
```swift
label name: while condition {
@ -703,7 +703,7 @@ print(description)
}
```
下面的例子是前面章节中*蛇和梯子*的适配版本,在此版本中,我们将使用一个带有标签的`while`循环体中调用`break``continue`语句。这次,游戏增加了一条额外的规则:
下面的例子是前面章节中*蛇和梯子*的适配版本,在此版本中,我们将使用一个带有标签的 `while` 循环体中调用 `break``continue` 语句。这次,游戏增加了一条额外的规则:
- 为了获胜,你必须*刚好*落在第 25 个方块中。
@ -713,7 +713,7 @@ print(description)
![image](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/snakesAndLadders_2x.png)
`finalSquare`、`board`、`square``diceRoll`值被和之前一样的方式初始化:
`finalSquare`、`board`、`square``diceRoll` 值被和之前一样的方式初始化:
```swift
let finalSquare = 25
@ -724,9 +724,9 @@ var square = 0
var diceRoll = 0
```
这个版本的游戏使用`while`循环和`switch`语句来实现游戏的逻辑。`while`循环有一个标签名`gameLoop`,来表明它是游戏的主循环。
这个版本的游戏使用 `while` 循环和 `switch` 语句来实现游戏的逻辑。`while` 循环有一个标签名 `gameLoop`,来表明它是游戏的主循环。
该`while`循环体的条件判断语句是`while square !=finalSquare`这表明你必须刚好落在方格25中。
`while` 循环体的条件判断语句是 `while square !=finalSquare`这表明你必须刚好落在方格25中。
```swift
gameLoop: while square != finalSquare {
@ -748,22 +748,22 @@ gameLoop: while square != finalSquare {
print("Game over!")
```
每次循环迭代开始时掷骰子。与之前玩家掷完骰子就立即移动不同,这里使用了`switch`语句来考虑每次移动可能产生的结果,从而决定玩家本次是否能够移动。
每次循环迭代开始时掷骰子。与之前玩家掷完骰子就立即移动不同,这里使用了 `switch` 语句来考虑每次移动可能产生的结果,从而决定玩家本次是否能够移动。
- 如果骰子数刚好使玩家移动到最终的方格里,游戏结束。`break gameLoop`语句跳转控制去执行`while`循环体后的第一行代码,意味着游戏结束。
- 如果骰子数将会使玩家的移动超出最后的方格,那么这种移动是不合法的,玩家需要重新掷骰子。`continue gameLoop`语句结束本次`while`循环,开始下一次循环。
- 在剩余的所有情况中,骰子数产生的都是合法的移动。玩家向前移动 `diceRoll` 个方格,然后游戏逻辑再处理玩家当前是否处于蛇头或者梯子的底部。接着本次循环结束,控制跳转到`while`循环体的条件判断语句处,再决定是否需要继续执行下次循环。
- 如果骰子数刚好使玩家移动到最终的方格里,游戏结束。`break gameLoop` 语句跳转控制去执行 `while` 循环体后的第一行代码,意味着游戏结束。
- 如果骰子数将会使玩家的移动超出最后的方格,那么这种移动是不合法的,玩家需要重新掷骰子。`continue gameLoop` 语句结束本次 `while` 循环,开始下一次循环。
- 在剩余的所有情况中,骰子数产生的都是合法的移动。玩家向前移动 `diceRoll` 个方格,然后游戏逻辑再处理玩家当前是否处于蛇头或者梯子的底部。接着本次循环结束,控制跳转到 `while` 循环体的条件判断语句处,再决定是否需要继续执行下次循环。
>注意
>
>如果上述的`break`语句没有使用`gameLoop`标签,那么它将会中断`switch`语句而不是`while`循环。使用`gameLoop`标签清晰的表明了`break`想要中断的是哪个代码块。
>
>同时请注意,当调用`continue gameLoop`去跳转到下一次循环迭代时,这里使用`gameLoop`标签并不是严格必须的。因为在这个游戏中,只有一个循环体,所以`continue`语句会影响到哪个循环体是没有歧义的。然而,`continue`语句使用`gameLoop`标签也是没有危害的。这样做符合标签的使用规则,同时参照旁边的`break gameLoop`,能够使游戏的逻辑更加清晰和易于理解。
> 注意
>
> 如果上述的 `break` 语句没有使用 `gameLoop` 标签,那么它将会中断 `switch` 语句而不是 `while` 循环。使用 `gameLoop` 标签清晰的表明了 `break` 想要中断的是哪个代码块。
>
> 同时请注意,当调用 `continue gameLoop` 去跳转到下一次循环迭代时,这里使用 `gameLoop` 标签并不是严格必须的。因为在这个游戏中,只有一个循环体,所以 `continue` 语句会影响到哪个循环体是没有歧义的。然而,`continue` 语句使用 `gameLoop` 标签也是没有危害的。这样做符合标签的使用规则,同时参照旁边的 `break gameLoop`,能够使游戏的逻辑更加清晰和易于理解。
<a name="early_exit"></a>
## 提前退出
像`if`语句一样,`guard`的执行取决于一个表达式的布尔值。我们可以使用`guard`语句来要求条件必须为真时,以执行`guard`语句后的代码。不同于`if`语句,一个`guard`语句总是有一个`else`从句,如果条件不为真则执行`else`从句中的代码。
`if` 语句一样,`guard` 的执行取决于一个表达式的布尔值。我们可以使用 `guard` 语句来要求条件必须为真时,以执行 `guard` 语句后的代码。不同于 `if` 语句,一个 `guard` 语句总是有一个 `else` 从句,如果条件不为真则执行 `else` 从句中的代码。
```swift
func greet(person: [String: String]) {
@ -785,20 +785,20 @@ greet(["name": "Jane", "location": "Cupertino"])
// 输出 "I hope the weather is nice in Cupertino."
```
如果`guard`语句的条件被满足,则继续执行`guard`语句大括号后的代码。将变量或者常量的可选绑定作为`guard`语句的条件,都可以保护`guard`语句后面的代码。
如果 `guard` 语句的条件被满足,则继续执行 `guard` 语句大括号后的代码。将变量或者常量的可选绑定作为 `guard` 语句的条件,都可以保护 `guard` 语句后面的代码。
如果条件不被满足,在`else`分支上的代码就会被执行。这个分支必须转移控制以退出`guard`语句出现的代码段。它可以用控制转移语句如`return`,`break`,`continue`或者`throw`做这件事,或者调用一个不返回的方法或函数,例如`fatalError()`。
如果条件不被满足,在 `else` 分支上的代码就会被执行。这个分支必须转移控制以退出 `guard` 语句出现的代码段。它可以用控制转移语句如 `return`,`break`,`continue` 或者 `throw` 做这件事,或者调用一个不返回的方法或函数,例如 `fatalError()`。
相比于可以实现同样功能的`if`语句,按需使用`guard`语句会提升我们代码的可读性。它可以使你的代码连贯的被执行而不需要将它包在`else`块中,它可以使你在紧邻条件判断的地方,处理违规的情况。
相比于可以实现同样功能的 `if` 语句,按需使用 `guard` 语句会提升我们代码的可读性。它可以使你的代码连贯的被执行而不需要将它包在 `else` 块中,它可以使你在紧邻条件判断的地方,处理违规的情况。
<a name="checking_api_availability"></a>
## 检测 API 可用性
Swift内置支持检查 API 可用性这可以确保我们不会在当前部署机器上不小心地使用了不可用的API。
Swift 内置支持检查 API 可用性,这可以确保我们不会在当前部署机器上,不小心地使用了不可用的 API。
编译器使用 SDK 中的可用信息来验证我们的代码中使用的所有 API 在项目指定的部署目标上是否可用。如果我们尝试使用一个不可用的 APISwift 会在编译时报错。
我们在`if``guard`语句中使用`可用性条件availability condition)`去有条件的执行一段代码来在运行时判断调用的API是否可用。编译器使用从可用性条件语句中获取的信息去验证在这个代码块中调用的 API 是否可用。
我们在 `if``guard` 语句中使用 `可用性条件availability condition)`去有条件的执行一段代码,来在运行时判断调用的 API 是否可用。编译器使用从可用性条件语句中获取的信息去验证,在这个代码块中调用的 API 是否可用。
```swift
if #available(iOS 10, macOS 10.12, *) {
@ -808,9 +808,9 @@ if #available(iOS 10, macOS 10.12, *) {
}
```
以上可用性条件指定,`if`语句的代码块仅仅在 iOS 10 或 macOS 10.12 及更高版本才运行。最后一个参数,`*`是必须的用于指定在所有其它平台中如果版本号高于你的设备指定的最低版本if语句的代码块将会运行。
以上可用性条件指定,`if` 语句的代码块仅仅在 iOS 10 或 macOS 10.12 及更高版本才运行。最后一个参数,`*`是必须的用于指定在所有其它平台中如果版本号高于你的设备指定的最低版本if 语句的代码块将会运行。
在它一般的形式中,可用性条件使用了一个平台名字和版本的列表。平台名字可以是`iOS``macOS``watchOS``tvOS`——请访问[声明属性](../chapter3/06_Attributes.html)来获取完整列表。除了指定像 iOS 8 或 macOS 10.10 的大版本号,也可以指定像 iOS 11.2.6 以及 macOS 10.13.3 的小版本号。
在它一般的形式中,可用性条件使用了一个平台名字和版本的列表。平台名字可以是 `iOS``macOS``watchOS``tvOS`——请访问[声明属性](../chapter3/06_Attributes.html)来获取完整列表。除了指定像 iOS 8 或 macOS 10.10 的大版本号,也可以指定像 iOS 11.2.6 以及 macOS 10.13.3 的小版本号。
```swift
if #available(platform name version, ..., *) {

View File

@ -18,19 +18,20 @@
> 3.0
> 翻译: [crayygy](https://github.com/crayygy) 2016-09-12
> 校对: [shanks](http://codebuild.me) 2016-09-27
> 3.0.1shanks2016-11-12
> 4.0
> 校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
本页包含内容:
- [函数定义与调用](#Defining_and_Calling_Functions)
- [函数参数与返回值](#Function_Parameters_and_Return_Values)
- [函数参数标签和参数名称](#Function_Argument_Labels_and_Parameter_Names)
- [函数类型](#Function_Types)
本页包含内容:
- [函数定义与调用](#Defining_and_Calling_Functions)
- [函数参数与返回值](#Function_Parameters_and_Return_Values)
- [函数参数标签和参数名称](#Function_Argument_Labels_and_Parameter_Names)
- [函数类型](#Function_Types)
- [嵌套函数](#Nested_Functions)
*函数*是一段完成特定任务的独立代码片段。你可以通过给函数命名来标识某个函数的功能,这个名字可以被用来在需要的时候"调用"这个函数来完成它的任务。
@ -46,7 +47,7 @@ Swift 统一的函数语法非常的灵活,可以用来表示任何函数,
每个函数有个*函数名*,用来描述函数执行的任务。要使用一个函数时,用函数名来“调用”这个函数,并传给它匹配的输入值(称作 *实参* )。函数的实参必须与函数参数表里参数的顺序一致。
下面例子中的函数的名字是`greet(person:)`,之所以叫这个名字,是因为这个函数用一个人的名字当做输入,并返回向这个人问候的语句。为了完成这个任务,你需要定义一个输入参数——一个叫做 `person``String` 值,和一个包含给这个人问候语的 `String` 类型的返回值:
下面例子中的函数的名字是 `greet(person:)`,之所以叫这个名字,是因为这个函数用一个人的名字当做输入,并返回向这个人问候的语句。为了完成这个任务,你需要定义一个输入参数——一个叫做 `person``String` 值,和一个包含给这个人问候语的 `String` 类型的返回值:
```swift
func greet(person: String) -> String {
@ -66,15 +67,15 @@ print(greet(person: "Brian"))
// 打印 "Hello, Brian!"
```
调用 `greet(person:)` 函数时,在圆括号中传给它一个 `String` 类型的实参,例如 `greet(person: "Anna")`。正如上面所示,因为这个函数返回一个 `String` 类型的值,所以`greet ` 可以被包含在 `print(_:separator:terminator:)` 的调用中,用来输出这个函数的返回值。
调用 `greet(person:)` 函数时,在圆括号中传给它一个 `String` 类型的实参,例如 `greet(person: "Anna")`。正如上面所示,因为这个函数返回一个 `String` 类型的值,所以 `greet` 可以被包含在 `print(_:separator:terminator:)` 的调用中,用来输出这个函数的返回值。
>注意
>
>`print(_:separator:terminator:)` 函数的第一个参数并没有设置一个标签,而其他的参数因为已经有了默认值,因此是可选的。关于这些函数语法上的变化详见下方关于 函数参数标签和参数名 以及 默认参数值。
> 注意
>
> `print(_:separator:terminator:)` 函数的第一个参数并没有设置一个标签,而其他的参数因为已经有了默认值,因此是可选的。关于这些函数语法上的变化详见下方关于 函数参数标签和参数名 以及 默认参数值。
`greet(person:)` 的函数体中,先定义了一个新的名为 `greeting``String` 常量,同时,把对 `personName` 的问候消息赋值给了 `greeting` 。然后用 `return` 关键字把这个问候返回出去。一旦 `return greeting` 被调用,该函数结束它的执行并返回 `greeting` 的当前值。
你可以用不同的输入值多次调用 `greet(person:)`。上面的例子展示的是用`"Anna"``"Brian"`调用的结果,该函数分别返回了不同的结果。
你可以用不同的输入值多次调用 `greet(person:)`。上面的例子展示的是用 `"Anna"``"Brian"` 调用的结果,该函数分别返回了不同的结果。
为了简化这个函数的定义,可以将问候消息的创建和返回写成一句:
@ -125,12 +126,12 @@ print(greet(person: "Tim", alreadyGreeted: true))
// 打印 "Hello again, Tim!"
```
你可以通过在括号内使用逗号分隔来传递一个`String`参数值和一个标识为`alreadyGreeted``Bool`值,来调用`greet(person:alreadyGreeted:)`函数。注意这个函数和上面`greet(person:)`是不同的。虽然它们都有着同样的名字`greet`,但是`greet(person:alreadyGreeted:)`函数需要两个参数,而`greet(person:)`只需要一个参数。
你可以通过在括号内使用逗号分隔来传递一个 `String` 参数值和一个标识为 `alreadyGreeted``Bool` 值,来调用 `greet(person:alreadyGreeted:)` 函数。注意这个函数和上面 `greet(person:)` 是不同的。虽然它们都有着同样的名字 `greet`,但是 `greet(person:alreadyGreeted:)` 函数需要两个参数,而 `greet(person:)` 只需要一个参数。
<a name="functions_without_return_values"></a>
### 无返回值函数
函数可以没有返回值。下面是 `greet(person:)` 函数的另一个版本,这个函数直接打印一个`String`值,而不是返回它:
函数可以没有返回值。下面是 `greet(person:)` 函数的另一个版本,这个函数直接打印一个 `String` 值,而不是返回它:
```swift
func greet(person: String) {
@ -142,9 +143,9 @@ greet(person: "Dave")
因为这个函数不需要返回值,所以这个函数的定义中没有返回箭头(->)和返回类型。
>注意
>
>严格上来说,虽然没有返回值被定义,`greet(person:)` 函数依然返回了值。没有定义返回类型的函数会返回一个特殊的`Void`值。它其实是一个空的元组,没有任何元素,可以写成()。
> 注意
>
> 严格上来说,虽然没有返回值被定义,`greet(person:)` 函数依然返回了值。没有定义返回类型的函数会返回一个特殊的 `Void` 值。它其实是一个空的元组,没有任何元素,可以写成()。
被调用时,一个函数的返回值可以被忽略:
@ -162,10 +163,11 @@ printWithoutCounting(string: "hello, world")
// 打印 "hello, world" 但是没有返回任何值
```
第一个函数 `printAndCount(string:)`,输出一个字符串并返回 `Int` 类型的字符数。第二个函数 `printWithoutCounting(string:)`调用了第一个函数,但是忽略了它的返回值。当第二个函数被调用时,消息依然会由第一个函数输出,但是返回值不会被用到。
第一个函数 `printAndCount(string:)`,输出一个字符串并返回 `Int` 类型的字符数。第二个函数 `printWithoutCounting(string:)` 调用了第一个函数,但是忽略了它的返回值。当第二个函数被调用时,消息依然会由第一个函数输出,但是返回值不会被用到。
>注意:
返回值可以被忽略,但定义了有返回值的函数必须返回一个值,如果在函数定义底部没有返回任何值,将导致编译时错误。
> 注意
>
> 返回值可以被忽略,但定义了有返回值的函数必须返回一个值,如果在函数定义底部没有返回任何值,将导致编译时错误。
<a name="functions_with_multiple_return_values"></a>
### 多重返回值函数
@ -206,11 +208,11 @@ print("min is \(bounds.min) and max is \(bounds.max)")
<a name="optional_tuple_return_types"></a>
### 可选元组返回类型
如果函数返回的元组类型有可能整个元组都“没有值”,你可以使用*可选的* 元组返回类型反映整个元组可以是`nil`的事实。你可以通过在元组类型的右括号后放置一个问号来定义一个可选元组,例如 `(Int, Int)?``(String, Int, Bool)?`
如果函数返回的元组类型有可能整个元组都“没有值”,你可以使用*可选的* 元组返回类型反映整个元组可以是 `nil` 的事实。你可以通过在元组类型的右括号后放置一个问号来定义一个可选元组,例如 `(Int, Int)?``(String, Int, Bool)?`
>注意
>
>可选元组类型如 `(Int, Int)?` 与元组包含可选类型如 `(Int?, Int?)` 是不同的。可选的元组类型,整个元组是可选的,而不只是元组中的每个元素值。
> 注意
>
> 可选元组类型如 `(Int, Int)?` 与元组包含可选类型如 `(Int?, Int?)` 是不同的。可选的元组类型,整个元组是可选的,而不只是元组中的每个元素值。
前面的 `minMax(array:)` 函数返回了一个包含两个 `Int` 值的元组。但是函数不会对传入的数组执行任何安全检查,如果 `array` 参数是一个空数组,如上定义的 `minMax(array:)` 在试图访问 `array[0]` 时会触发一个运行时错误。
@ -330,22 +332,22 @@ arithmeticMean(3, 8.25, 18.75)
// 返回 10.0, 是这 3 个数的平均数。
```
>注意
>
>一个函数最多只能拥有一个可变参数。
> 注意
>
> 一个函数最多只能拥有一个可变参数。
<a name="in_out_parameters"></a>
### 输入输出参数
函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误。这意味着你不能错误地更改参数值。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为*输入输出参数In-Out Parameters*。
定义一个输入输出参数时,在参数定义前加 `inout` 关键字。一个`输入输出参数`有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。想获取更多的关于输入输出参数的细节和相关的编译器优化,请查看[输入输出参数](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID545)一节。
定义一个输入输出参数时,在参数定义前加 `inout` 关键字。一个 `输入输出参数`有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。想获取更多的关于输入输出参数的细节和相关的编译器优化,请查看[输入输出参数](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID545)一节。
你只能传递变量给输入输出参数。你不能传入常量或者字面量,因为这些量是不能被修改的。当传入的参数作为输入输出参数时,需要在参数名前加 `&` 符,表示这个值可以被函数修改。
>注意
>
>输入输出参数不能有默认值,而且可变参数不能用 `inout` 标记。
> 注意
>
> 输入输出参数不能有默认值,而且可变参数不能用 `inout` 标记。
下例中,`swapTwoInts(_:_:)` 函数有两个分别叫做 `a``b` 的输入输出参数:
@ -371,9 +373,9 @@ print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
从上面这个例子中,我们可以看到 `someInt``anotherInt` 的原始值在 `swapTwoInts(_:_:)` 函数中被修改,尽管它们的定义在函数体外。
>注意
>
>输入输出参数和返回值是不一样的。上面的 `swapTwoInts` 函数并没有定义任何返回值,但仍然修改了 `someInt` 和 `anotherInt` 的值。输入输出参数是函数对函数体外产生影响的另一种方式。
> 注意
>
> 输入输出参数和返回值是不一样的。上面的 `swapTwoInts` 函数并没有定义任何返回值,但仍然修改了 `someInt` 和 `anotherInt` 的值。输入输出参数是函数对函数体外产生影响的另一种方式。
<a name="Function_Types"></a>
## 函数类型
@ -393,7 +395,7 @@ func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
这个例子中定义了两个简单的数学函数:`addTwoInts``multiplyTwoInts`。这两个函数都接受两个 `Int` 值, 返回一个 `Int` 值。
这两个函数的类型是 `(Int, Int) -> Int`,可以解读为:
这两个函数的类型是 `(Int, Int) -> Int`,可以解读为:
“这个函数类型有两个 `Int` 型的参数并返回一个 `Int` 型的值”。
@ -470,7 +472,7 @@ printMathResult(addTwoInts, 3, 5)
你可以用函数类型作为另一个函数的返回类型。你需要做的是在返回箭头(->)后写一个完整的函数类型。
下面的这个例子中定义了两个简单函数,分别是 `stepForward(_:)``stepBackward(_:)``stepForward(_:)`函数返回一个比输入值大 `1` 的值。`stepBackward(_:)` 函数返回一个比输入值小 `1` 的值。这两个函数的类型都是 `(Int) -> Int`
下面的这个例子中定义了两个简单函数,分别是 `stepForward(_:)``stepBackward(_:)``stepForward(_:)` 函数返回一个比输入值大 `1` 的值。`stepBackward(_:)` 函数返回一个比输入值小 `1` 的值。这两个函数的类型都是 `(Int) -> Int`
```swift
func stepForward(_ input: Int) -> Int {
@ -499,7 +501,7 @@ let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
上面这个例子中计算出从 `currentValue` 逐渐接近到0是需要向正数走还是向负数走。`currentValue` 的初始值是 `3`,这意味着 `currentValue > 0` 为真true这将使得 `chooseStepFunction(_:)` 返回 `stepBackward(_:)` 函数。一个指向返回的函数的引用保存在了 `moveNearerToZero` 常量中。
现在,`moveNearerToZero`指向了正确的函数,它可以被用来数到零:
现在,`moveNearerToZero` 指向了正确的函数,它可以被用来数到零:
```swift
print("Counting to zero:")

View File

@ -11,12 +11,13 @@
> 2.1
> 翻译:[100mango](https://github.com/100mango), [magicdict](https://github.com/magicdict)
> 校对:[shanks](http://codebuild.me)
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-12
>
> 3.0
> 翻译:[Lanford](https://github.com/LanfordCai) 2016-09-19
> 3.0.1shanks2016-11-12
> 4.0
@ -39,6 +40,7 @@
闭包可以捕获和存储其所在上下文中任意常量和变量的引用。被称为*包裹*常量和变量。 Swift 会为你管理在捕获过程中涉及到的所有内存操作。
> 注意
>
> 如果你不熟悉捕获capturing这个概念也不用担心你可以在[值捕获](#capturing_values)章节对其进行详细了解。
在[函数](./06_Functions.md)章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:
@ -72,7 +74,7 @@ Swift 标准库提供了名为 `sorted(by:)` 的方法,它会根据你所提
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
```
`sorted(by:)` 方法接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值*前面*,排序闭包函数需要返回`true`,反之返回`false`
`sorted(by:)` 方法接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值*前面*,排序闭包函数需要返回 `true`,反之返回 `false`
该例子对一个 `String` 类型的数组进行排序,因此排序闭包函数类型需为 `(String, String) -> Bool`
@ -113,7 +115,7 @@ reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
需要注意的是内联闭包参数和返回值类型声明与 `backward(_:_:)` 函数类型声明相同。在这两种方式中,都写成了 `(s1: String, s2: String) -> Bool`。然而在内联闭包表达式中,函数和返回值类型都写在*大括号内*,而不是大括号外。
闭包的函数体部分由关键字`in`引入。该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。
闭包的函数体部分由关键字 `in` 引入。该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。
由于这个闭包的函数体部分如此短,以至于可以将其改写成一行代码:
@ -151,13 +153,13 @@ reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
Swift 自动为内联闭包提供了参数名称缩写功能,你可以直接通过 `$0``$1``$2` 来顺序调用闭包的参数,以此类推。
如果你在闭包表达式中使用参数名称缩写,你可以在闭包定义中省略参数列表,并且对应参数名称缩写的类型会通过函数类型进行推断。`in`关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
如果你在闭包表达式中使用参数名称缩写,你可以在闭包定义中省略参数列表,并且对应参数名称缩写的类型会通过函数类型进行推断。`in` 关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
```swift
reversedNames = names.sorted(by: { $0 > $1 } )
```
在这个例子中,`$0``$1`表示闭包中第一个和第二个 `String` 类型的参数。
在这个例子中,`$0``$1` 表示闭包中第一个和第二个 `String` 类型的参数。
<a name="operator_methods"></a>
### 运算符方法
@ -207,7 +209,7 @@ reversedNames = names.sorted { $0 > $1 }
当提供给数组的闭包应用于每个数组元素后,`map(_:)` 方法将返回一个新的数组,数组中包含了与原数组中的元素一一对应的映射后的值。
下例介绍了如何在 `map(_:)` 方法中使用尾随闭包将 `Int` 类型数组 `[16, 58, 510]` 转换为包含对应 `String` 类型的值的数组`["OneSix", "FiveEight", "FiveOneZero"]`
下例介绍了如何在 `map(_:)` 方法中使用尾随闭包将 `Int` 类型数组 `[16, 58, 510]` 转换为包含对应 `String` 类型的值的数组 `["OneSix", "FiveEight", "FiveOneZero"]`
```swift
let digitNames = [
@ -244,7 +246,7 @@ let strings = numbers.map {
> 注意
>
> 字典 `digitNames` 下标后跟着一个叹号(`!`因为字典下标返回一个可选值optional value表明该键不存在时会查找失败。在上例中由于可以确定 `number % 10` 总是 `digitNames` 字典的有效下标,因此叹号可以用于强制解包 (force-unwrap) 存储在下标的可选类型的返回值中的`String`类型的值。
> 字典 `digitNames` 下标后跟着一个叹号(`!`因为字典下标返回一个可选值optional value表明该键不存在时会查找失败。在上例中由于可以确定 `number % 10` 总是 `digitNames` 字典的有效下标,因此叹号可以用于强制解包 (force-unwrap) 存储在下标的可选类型的返回值中的 `String` 类型的值。
`digitNames` 字典中获取的字符串被添加到 `output` 的*前部*,逆序建立了一个字符串版本的数字。(在表达式 `number % 10` 中,如果 `number``16`,则返回 `6``58` 返回 `8``510` 返回 `0`。)

View File

@ -46,7 +46,7 @@
<a name="enumeration_syntax"></a>
## 枚举语法
使用`enum`关键词来创建枚举并且把它们的整个定义放在一对大括号内:
使用 `enum` 关键词来创建枚举并且把它们的整个定义放在一对大括号内:
```swift
enum SomeEnumeration {
@ -65,11 +65,11 @@ enum CompassPoint {
}
```
枚举中定义的值(如 `north ``south``east``west`)是这个枚举的*成员值*(或*成员*)。你可以使用`case`关键字来定义一个新的枚举成员值。
枚举中定义的值(如 `north``south``east``west`)是这个枚举的*成员值*(或*成员*)。你可以使用 `case` 关键字来定义一个新的枚举成员值。
> 注意
>
> 与 C 和 Objective-C 不同Swift 的枚举成员在被创建时不会被赋予一个默认的整型值。在上面的`CompassPoint`例子中,`north``south``east``west`不会被隐式地赋值为`0``1``2``3`。相反,这些枚举成员本身就是完备的值,这些值的类型是已经明确定义好的`CompassPoint`类型。
> 与 C 和 Objective-C 不同Swift 的枚举成员在被创建时不会被赋予一个默认的整型值。在上面的 `CompassPoint` 例子中,`north``south``east``west` 不会被隐式地赋值为 `0``1``2``3`。相反,这些枚举成员本身就是完备的值,这些值的类型是已经明确定义好的 `CompassPoint` 类型。
多个成员值可以出现在同一行上,用逗号隔开:
@ -79,24 +79,24 @@ enum Planet {
}
```
每个枚举定义了一个全新的类型。像 Swift 中其他类型一样,它们的名字(例如`CompassPoint``Planet`)应该以一个大写字母开头。给枚举类型起一个单数名字而不是复数名字,以便于:
每个枚举定义了一个全新的类型。像 Swift 中其他类型一样,它们的名字(例如 `CompassPoint``Planet`)应该以一个大写字母开头。给枚举类型起一个单数名字而不是复数名字,以便于:
```swift
var directionToHead = CompassPoint.west
```
`directionToHead`的类型可以在它被`CompassPoint`的某个值初始化时推断出来。一旦`directionToHead`被声明为`CompassPoint`类型,你可以使用更简短的点语法将其设置为另一个`CompassPoint`的值:
`directionToHead` 的类型可以在它被 `CompassPoint` 的某个值初始化时推断出来。一旦 `directionToHead` 被声明为 `CompassPoint` 类型,你可以使用更简短的点语法将其设置为另一个 `CompassPoint` 的值:
```swift
directionToHead = .east
```
`directionToHead`的类型已知时,再次为其赋值可以省略枚举类型名。在使用具有显式类型的枚举值时,这种写法让代码具有更好的可读性。
`directionToHead` 的类型已知时,再次为其赋值可以省略枚举类型名。在使用具有显式类型的枚举值时,这种写法让代码具有更好的可读性。
<a name="matching_enumeration_values_with_a_switch_statement"></a>
## 使用 Switch 语句匹配枚举值
你可以使用`switch`语句匹配单个枚举值:
你可以使用 `switch` 语句匹配单个枚举值:
```swift
directionToHead = .south
@ -115,13 +115,13 @@ switch directionToHead {
你可以这样理解这段代码:
“判断`directionToHead`的值。当它等于`.north`,打印`“Lots of planets have a north”`。当它等于`.south`,打印`“Watch out for penguins”`。”
“判断 `directionToHead` 的值。当它等于 `.north`,打印 `“Lots of planets have a north”`。当它等于 `.south`,打印 `“Watch out for penguins”`。”
……以此类推。
正如在[控制流](./05_Control_Flow.html)中介绍的那样,在判断一个枚举类型的值时,`switch`语句必须穷举所有情况。如果忽略了`.west`这种情况,上面那段代码将无法通过编译,因为它没有考虑到`CompassPoint`的全部成员。强制穷举确保了枚举成员不会被意外遗漏。
正如在[控制流](./05_Control_Flow.html)中介绍的那样,在判断一个枚举类型的值时,`switch` 语句必须穷举所有情况。如果忽略了 `.west` 这种情况,上面那段代码将无法通过编译,因为它没有考虑到 `CompassPoint` 的全部成员。强制穷举确保了枚举成员不会被意外遗漏。
当不需要匹配每个枚举成员的时候,你可以提供一个`default`分支来涵盖所有未明确处理的枚举成员:
当不需要匹配每个枚举成员的时候,你可以提供一个 `default` 分支来涵盖所有未明确处理的枚举成员:
```swift
let somePlanet = Planet.earth
@ -137,11 +137,11 @@ default:
<a name="associated_values"></a>
## 关联值
上一小节的例子演示了如何定义和分类枚举的成员。你可以为`Planet.earth`设置一个常量或者变量,并在赋值之后查看这个值。然而,有时候能够把其他类型的*关联值*和成员值一起存储起来会很有用。这能让你连同成员值一起存储额外的自定义信息,并且你每次在代码中使用该枚举成员时,还可以修改这个关联值。
上一小节的例子演示了如何定义和分类枚举的成员。你可以为 `Planet.earth` 设置一个常量或者变量,并在赋值之后查看这个值。然而,有时候能够把其他类型的*关联值*和成员值一起存储起来会很有用。这能让你连同成员值一起存储额外的自定义信息,并且你每次在代码中使用该枚举成员时,还可以修改这个关联值。
你可以定义 Swift 枚举来存储任意类型的关联值如果需要的话每个枚举成员的关联值类型可以各不相同。枚举的这种特性跟其他语言中的可识别联合discriminated unions标签联合tagged unions或者变体variants相似。
例如,假设一个库存跟踪系统需要利用两种不同类型的条形码来跟踪商品。有些商品上标有使用`0``9`的数字的 UPC 格式的一维条形码。每一个条形码都有一个代表“数字系统”的数字,该数字后接五位代表“厂商代码”的数字,接下来是五位代表“产品代码”的数字。最后一个数字是“检查”位,用来验证代码是否被正确扫描:
例如,假设一个库存跟踪系统需要利用两种不同类型的条形码来跟踪商品。有些商品上标有使用 `0``9` 的数字的 UPC 格式的一维条形码。每一个条形码都有一个代表“数字系统”的数字,该数字后接五位代表“厂商代码”的数字,接下来是五位代表“产品代码”的数字。最后一个数字是“检查”位,用来验证代码是否被正确扫描:
<img width="252" height="120" alt="" src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/barcode_UPC_2x.png">
@ -162,9 +162,9 @@ enum Barcode {
以上代码可以这么理解:
“定义一个名为`Barcode`的枚举类型,它的一个成员值是具有`(IntIntIntInt)`类型关联值的`upc`,另一个成员值是具有`String`类型关联值的`qrCode`。”
“定义一个名为 `Barcode` 的枚举类型,它的一个成员值是具有 `(IntIntIntInt)` 类型关联值的 `upc`,另一个成员值是具有 `String` 类型关联值的 `qrCode`。”
这个定义不提供任何`Int``String`类型的关联值,它只是定义了,当`Barcode`常量和变量等于`Barcode.upc``Barcode.qrCode`时,可以存储的关联值的类型。
这个定义不提供任何 `Int``String` 类型的关联值,它只是定义了,当 `Barcode` 常量和变量等于 `Barcode.upc``Barcode.qrCode` 时,可以存储的关联值的类型。
然后可以使用任意一种条形码类型创建新的条形码,例如:
@ -172,7 +172,7 @@ enum Barcode {
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
```
上面的例子创建了一个名为`productBarcode`的变量,并将`Barcode.upc`赋值给它,关联的元组值为`(8, 85909, 51226, 3)`
上面的例子创建了一个名为 `productBarcode` 的变量,并将 `Barcode.upc` 赋值给它,关联的元组值为 `(8, 85909, 51226, 3)`
同一个商品可以被分配一个不同类型的条形码,例如:
@ -180,9 +180,9 @@ var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
```
这时,原始的`Barcode.upc`和其整数关联值被新的`Barcode.qrCode`和其字符串关联值所替代。`Barcode`类型的常量和变量可以存储一个`.upc`或者一个`.qrCode`(连同它们的关联值),但是在同一时间只能存储这两个值中的一个。
这时,原始的 `Barcode.upc` 和其整数关联值被新的 `Barcode.qrCode` 和其字符串关联值所替代。`Barcode` 类型的常量和变量可以存储一个 `.upc` 或者一个 `.qrCode`(连同它们的关联值),但是在同一时间只能存储这两个值中的一个。
像先前那样,可以使用一个 switch 语句来检查不同的条形码类型。然而,这一次,关联值可以被提取出来作为 switch 语句的一部分。你可以在`switch`的 case 分支代码中提取每个关联值作为一个常量(用`let`前缀)或者作为一个变量(用`var`前缀)来使用:
像先前那样,可以使用一个 switch 语句来检查不同的条形码类型。然而,这一次,关联值可以被提取出来作为 switch 语句的一部分。你可以在 `switch` 的 case 分支代码中提取每个关联值作为一个常量(用 `let` 前缀)或者作为一个变量(用 `var` 前缀)来使用:
```swift
switch productBarcode {
@ -194,7 +194,7 @@ case .qrCode(let productCode):
// 打印 "QR code: ABCDEFGHIJKLMNOP."
```
如果一个枚举成员的所有关联值都被提取为常量,或者都被提取为变量,为了简洁,你可以只在成员名称前标注一个`let`或者`var`
如果一个枚举成员的所有关联值都被提取为常量,或者都被提取为变量,为了简洁,你可以只在成员名称前标注一个 `let` 或者 `var`
```swift
switch productBarcode {
@ -221,7 +221,7 @@ enum ASCIIControlCharacter: Character {
}
```
枚举类型`ASCIIControlCharacter`的原始值类型被定义为`Character`,并设置了一些比较常见的 ASCII 控制字符。`Character`的描述详见[字符串和字符](./03_Strings_and_Characters.html)部分。
枚举类型 `ASCIIControlCharacter` 的原始值类型被定义为 `Character`,并设置了一些比较常见的 ASCII 控制字符。`Character` 的描述详见[字符串和字符](./03_Strings_and_Characters.html)部分。
原始值可以是字符串,字符,或者任意整型值或浮点型值。每个原始值在枚举声明中必须是唯一的。
@ -234,9 +234,9 @@ enum ASCIIControlCharacter: Character {
在使用原始值为整数或者字符串类型的枚举时不需要显式地为每一个枚举成员设置原始值Swift 将会自动为你赋值。
例如,当使用整数作为原始值时,隐式赋值的值依次递增`1`。如果第一个枚举成员没有设置原始值,其原始值将为`0`
例如,当使用整数作为原始值时,隐式赋值的值依次递增 `1`。如果第一个枚举成员没有设置原始值,其原始值将为 `0`
下面的枚举是对之前`Planet`这个枚举的一个细化,利用整型的原始值来表示每个行星在太阳系中的顺序:
下面的枚举是对之前 `Planet` 这个枚举的一个细化,利用整型的原始值来表示每个行星在太阳系中的顺序:
```swift
enum Planet: Int {
@ -244,11 +244,11 @@ enum Planet: Int {
}
```
在上面的例子中,`Plant.mercury`的显式原始值为`1``Planet.venus`的隐式原始值为`2`,依次类推。
在上面的例子中,`Plant.mercury` 的显式原始值为 `1``Planet.venus` 的隐式原始值为 `2`,依次类推。
当使用字符串作为枚举类型的原始值时,每个枚举成员的隐式原始值为该枚举成员的名称。
下面的例子是`CompassPoint`枚举的细化,使用字符串类型的原始值来表示各个方向的名称:
下面的例子是 `CompassPoint` 枚举的细化,使用字符串类型的原始值来表示各个方向的名称:
```swift
enum CompassPoint: String {
@ -256,9 +256,9 @@ enum CompassPoint: String {
}
```
上面例子中,`CompassPoint.south`拥有隐式原始值`south`,依次类推。
上面例子中,`CompassPoint.south` 拥有隐式原始值 `south`,依次类推。
使用枚举成员的`rawValue`属性可以访问该枚举成员的原始值:
使用枚举成员的 `rawValue` 属性可以访问该枚举成员的原始值:
```swift
let earthsOrder = Planet.earth.rawValue
@ -271,22 +271,22 @@ let sunsetDirection = CompassPoint.west.rawValue
<a name="initializing_from_a_raw_value"></a>
### 使用原始值初始化枚举实例
如果在定义枚举类型的时候使用了原始值,那么将会自动获得一个初始化方法,这个方法接收一个叫做`rawValue`的参数,参数类型即为原始值类型,返回值则是枚举成员或`nil`。你可以使用这个初始化方法来创建一个新的枚举实例。
如果在定义枚举类型的时候使用了原始值,那么将会自动获得一个初始化方法,这个方法接收一个叫做 `rawValue` 的参数,参数类型即为原始值类型,返回值则是枚举成员或 `nil`。你可以使用这个初始化方法来创建一个新的枚举实例。
这个例子利用原始值`7`创建了枚举成员`uranus`
这个例子利用原始值 `7` 创建了枚举成员 `uranus`
```swift
let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet 类型为 Planet? 值为 Planet.uranus
```
然而,并非所有`Int`值都可以找到一个匹配的行星。因此,原始值构造器总是返回一个*可选*的枚举成员。在上面的例子中,`possiblePlanet``Planet?`类型,或者说“可选的`Planet`”。
然而,并非所有 `Int` 值都可以找到一个匹配的行星。因此,原始值构造器总是返回一个*可选*的枚举成员。在上面的例子中,`possiblePlanet``Planet?` 类型,或者说“可选的 `Planet`”。
> 注意
>
> 原始值构造器是一个可失败构造器,因为并不是每一个原始值都有与之对应的枚举成员。更多信息请参见[可失败构造器](../chapter3/05_Declarations.html#failable_initializers)
如果你试图寻找一个位置为`11`的行星,通过原始值构造器返回的可选`Planet`值将是`nil`
如果你试图寻找一个位置为 `11` 的行星,通过原始值构造器返回的可选 `Planet` 值将是 `nil`
```swift
let positionToFind = 11
@ -303,12 +303,12 @@ if let somePlanet = Planet(rawValue: positionToFind) {
// 输出 "There isn't a planet at position 11
```
这个例子使用了可选绑定optional binding试图通过原始值`11`来访问一个行星。`if let somePlanet = Planet(rawValue: 11)`语句创建了一个可选`Planet`,如果可选`Planet`的值存在,就会赋值给`somePlanet`。在这个例子中,无法检索到位置为`11`的行星,所以`else`分支被执行。
这个例子使用了可选绑定optional binding试图通过原始值 `11` 来访问一个行星。`if let somePlanet = Planet(rawValue: 11)` 语句创建了一个可选 `Planet`,如果可选 `Planet` 的值存在,就会赋值给 `somePlanet`。在这个例子中,无法检索到位置为 `11` 的行星,所以 `else` 分支被执行。
<a name="recursive_enumerations"></a>
## 递归枚举
*递归枚举*是一种枚举类型,它有一个或多个枚举成员使用该枚举类型的实例作为关联值。使用递归枚举时,编译器会插入一个间接层。你可以在枚举成员前加上`indirect`来表示该成员可递归。
*递归枚举*是一种枚举类型,它有一个或多个枚举成员使用该枚举类型的实例作为关联值。使用递归枚举时,编译器会插入一个间接层。你可以在枚举成员前加上 `indirect` 来表示该成员可递归。
例如,下面的例子中,枚举类型存储了简单的算术表达式:
@ -320,7 +320,7 @@ enum ArithmeticExpression {
}
```
你也可以在枚举类型开头加上`indirect`关键字来表明它的所有成员都是可递归的:
你也可以在枚举类型开头加上 `indirect` 关键字来表明它的所有成员都是可递归的:
```swift
indirect enum ArithmeticExpression {
@ -330,7 +330,7 @@ indirect enum ArithmeticExpression {
}
```
上面定义的枚举类型可以存储三种算术表达式:纯数字、两个表达式相加、两个表达式相乘。枚举成员`addition``multiplication`的关联值也是算术表达式——这些关联值使得嵌套表达式成为可能。例如,表达式`(5 + 4) * 2`,乘号右边是一个数字,左边则是另一个表达式。因为数据是嵌套的,因而用来存储数据的枚举类型也需要支持这种嵌套——这意味着枚举类型需要支持递归。下面的代码展示了使用`ArithmeticExpression `这个递归枚举创建表达式`(5 + 4) * 2`
上面定义的枚举类型可以存储三种算术表达式:纯数字、两个表达式相加、两个表达式相乘。枚举成员 `addition``multiplication` 的关联值也是算术表达式——这些关联值使得嵌套表达式成为可能。例如,表达式 `(5 + 4) * 2`,乘号右边是一个数字,左边则是另一个表达式。因为数据是嵌套的,因而用来存储数据的枚举类型也需要支持这种嵌套——这意味着枚举类型需要支持递归。下面的代码展示了使用 `ArithmeticExpression` 这个递归枚举创建表达式 `(5 + 4) * 2`
```swift
let five = ArithmeticExpression.number(5)

View File

@ -67,7 +67,7 @@ Swift 中类和结构体有很多共同点。共同处在于:
<a name="definition_syntax"></a>
### 定义语法
类和结构体有着类似的定义方式。我们通过关键字`class``struct`来分别表示类和结构体,并在一对大括号中定义它们的具体内容:
类和结构体有着类似的定义方式。我们通过关键字 `class``struct` 来分别表示类和结构体,并在一对大括号中定义它们的具体内容:
```swift
class SomeClass {
@ -80,7 +80,7 @@ struct SomeStructure {
> 注意
>
> 在你每次定义一个新类或者结构体的时候,实际上你是定义了一个新的 Swift 类型。因此请使用`UpperCamelCase`这种方式来命名(如`SomeClass``SomeStructure`等),以便符合标准 Swift 类型的大写命名风格(如`String``Int``Bool`)。相反的,请使用`lowerCamelCase`这种方式为属性和方法命名(如`framerate``incrementCount`),以便和类型名区分。
> 在你每次定义一个新类或者结构体的时候,实际上你是定义了一个新的 Swift 类型。因此请使用 `UpperCamelCase` 这种方式来命名(如 `SomeClass``SomeStructure` 等),以便符合标准 Swift 类型的大写命名风格(如 `String``Int``Bool`)。相反的,请使用 `lowerCamelCase` 这种方式为属性和方法命名(如 `framerate``incrementCount`),以便和类型名区分。
以下是定义结构体和定义类的示例:
@ -97,14 +97,14 @@ class VideoMode {
}
```
在上面的示例中我们定义了一个名为`Resolution`的结构体,用来描述一个显示器的像素分辨率。这个结构体包含了两个名为`width``height`的存储属性。存储属性是被捆绑和存储在类或结构体中的常量或变量。当这两个属性被初始化为整数`0`的时候,它们会被推断为`Int`类型。
在上面的示例中我们定义了一个名为 `Resolution` 的结构体,用来描述一个显示器的像素分辨率。这个结构体包含了两个名为 `width``height` 的存储属性。存储属性是被捆绑和存储在类或结构体中的常量或变量。当这两个属性被初始化为整数 `0` 的时候,它们会被推断为 `Int` 类型。
在上面的示例中我们还定义了一个名为`VideoMode`的类,用来描述一个视频显示器的特定模式。这个类包含了四个变量存储属性。第一个是`分辨率`,它被初始化为一个新的`Resolution`结构体的实例,属性类型被推断为`Resolution`。新`VideoMode`实例同时还会初始化其它三个属性,它们分别是,初始值为`false``interlaced`,初始值为`0.0``frameRate`,以及值为可选`String``name``name`属性会被自动赋予一个默认值`nil`,意为“没有`name`值”,因为它是一个可选类型。
在上面的示例中我们还定义了一个名为 `VideoMode` 的类,用来描述一个视频显示器的特定模式。这个类包含了四个变量存储属性。第一个是 `分辨率`,它被初始化为一个新的 `Resolution` 结构体的实例,属性类型被推断为 `Resolution`。新 `VideoMode` 实例同时还会初始化其它三个属性,它们分别是,初始值为 `false``interlaced`,初始值为 `0.0``frameRate`,以及值为可选 `String``name``name` 属性会被自动赋予一个默认值 `nil`,意为“没有 `name` 值”,因为它是一个可选类型。
<a name="class_and_structure_instances"></a>
### 类和结构体实例
`Resolution`结构体和`VideoMode`类的定义仅描述了什么是`Resolution``VideoMode`。它们并没有描述一个特定的分辨率resolution或者视频模式video mode。为了描述一个特定的分辨率或者视频模式我们需要生成一个它们的实例。
`Resolution` 结构体和 `VideoMode` 类的定义仅描述了什么是 `Resolution``VideoMode`。它们并没有描述一个特定的分辨率resolution或者视频模式video mode。为了描述一个特定的分辨率或者视频模式我们需要生成一个它们的实例。
生成结构体和类实例的语法非常相似:
@ -113,7 +113,7 @@ let someResolution = Resolution()
let someVideoMode = VideoMode()
```
结构体和类都使用构造器语法来生成新的实例。构造器语法的最简单形式是在结构体或者类的类型名称后跟随一对空括号,如`Resolution()``VideoMode()`。通过这种方式所创建的类或者结构体实例,其属性均会被初始化为默认值。[构造过程](./14_Initialization.html)章节会对类和结构体的初始化进行更详细的讨论。
结构体和类都使用构造器语法来生成新的实例。构造器语法的最简单形式是在结构体或者类的类型名称后跟随一对空括号,如 `Resolution()``VideoMode()`。通过这种方式所创建的类或者结构体实例,其属性均会被初始化为默认值。[构造过程](./14_Initialization.html)章节会对类和结构体的初始化进行更详细的讨论。
<a name="accessing_properties"></a>
### 属性访问
@ -125,9 +125,9 @@ print("The width of someResolution is \(someResolution.width)")
// 打印 "The width of someResolution is 0"
```
在上面的例子中,`someResolution.width`引用`someResolution``width`属性,返回`width`的初始值`0`
在上面的例子中,`someResolution.width` 引用 `someResolution``width` 属性,返回 `width` 的初始值 `0`
你也可以访问子属性,如`VideoMode``Resolution`属性的`width`属性:
你也可以访问子属性,如 `VideoMode``Resolution` 属性的 `width` 属性:
```swift
print("The width of someVideoMode is \(someVideoMode.resolution.width)")
@ -144,7 +144,7 @@ print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
> 注意
>
> 与 Objective-C 语言不同的是Swift 允许直接设置结构体属性的子属性。上面的最后一个例子,就是直接设置了`someVideoMode``resolution`属性的`width`这个子属性,以上操作并不需要重新为整个`resolution`属性设置新值。
> 与 Objective-C 语言不同的是Swift 允许直接设置结构体属性的子属性。上面的最后一个例子,就是直接设置了 `someVideoMode``resolution` 属性的 `width` 这个子属性,以上操作并不需要重新为整个 `resolution` 属性设置新值。
<a name="memberwise_initializers_for_structure_types"></a>
### 结构体类型的成员逐一构造器
@ -166,38 +166,38 @@ let vga = Resolution(width: 640, height: 480)
在 Swift 中,所有的结构体和枚举类型都是值类型。这意味着它们的实例,以及实例中所包含的任何值类型属性,在代码中传递的时候都会被复制。
请看下面这个示例,其使用了前一个示例中的`Resolution`结构体:
请看下面这个示例,其使用了前一个示例中的 `Resolution` 结构体:
```swift
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
```
在以上示例中,声明了一个名为`hd`的常量,其值为一个初始化为全高清视频分辨率(`1920` 像素宽,`1080` 像素高)的`Resolution`实例。
在以上示例中,声明了一个名为 `hd` 的常量,其值为一个初始化为全高清视频分辨率(`1920` 像素宽,`1080` 像素高)的 `Resolution` 实例。
然后示例中又声明了一个名为`cinema`的变量,并将`hd`赋值给它。因为`Resolution`是一个结构体,所以`cinema`的值其实是`hd`的一个拷贝副本,而不是`hd`本身。尽管`hd``cinema`有着相同的宽width和高height但是在幕后它们是两个完全不同的实例。
然后示例中又声明了一个名为 `cinema` 的变量,并将 `hd` 赋值给它。因为 `Resolution` 是一个结构体,所以 `cinema` 的值其实是 `hd` 的一个拷贝副本,而不是 `hd` 本身。尽管 `hd``cinema` 有着相同的宽width和高height但是在幕后它们是两个完全不同的实例。
下面,为了符合数码影院放映的需求(`2048` 像素宽,`1080` 像素高),`cinema``width`属性需要作如下修改:
下面,为了符合数码影院放映的需求(`2048` 像素宽,`1080` 像素高),`cinema``width` 属性需要作如下修改:
```swift
cinema.width = 2048
```
这里,将会显示`cinema``width`属性确已改为了`2048`
这里,将会显示 `cinema``width` 属性确已改为了 `2048`
```swift
print("cinema is now \(cinema.width) pixels wide")
// 打印 "cinema is now 2048 pixels wide"
```
然而,初始的`hd`实例中`width`属性还是`1920`
然而,初始的 `hd` 实例中 `width` 属性还是 `1920`
```swift
print("hd is still \(hd.width) pixels wide")
// 打印 "hd is still 1920 pixels wide"
```
在将`hd`赋予给`cinema`的时候,实际上是将`hd`中所存储的值进行拷贝,然后将拷贝的数据存储到新的`cinema`实例中。结果就是两个完全独立的实例碰巧包含有相同的数值。由于两者相互独立,因此将`cinema``width`修改为`2048`并不会影响`hd`中的`width`的值。
在将 `hd` 赋予给 `cinema` 的时候,实际上是将 `hd` 中所存储的值进行拷贝,然后将拷贝的数据存储到新的 `cinema` 实例中。结果就是两个完全独立的实例碰巧包含有相同的数值。由于两者相互独立,因此将 `cinema``width` 修改为 `2048` 并不会影响 `hd` 中的 `width` 的值。
枚举也遵循相同的行为准则:
@ -214,14 +214,14 @@ if rememberedDirection == .West {
// 打印 "The remembered direction is still .West"
```
上例中`rememberedDirection`被赋予了`currentDirection`的值,实际上它被赋予的是值的一个拷贝。赋值过程结束后再修改`currentDirection`的值并不影响`rememberedDirection`所储存的原始值的拷贝。
上例中 `rememberedDirection` 被赋予了 `currentDirection` 的值,实际上它被赋予的是值的一个拷贝。赋值过程结束后再修改 `currentDirection` 的值并不影响 `rememberedDirection` 所储存的原始值的拷贝。
<a name="classes_are_reference_types"></a>
## 类是引用类型
与值类型不同,*引用类型*在被赋予到一个变量、常量或者被传递到一个函数时,其值不会被拷贝。因此,引用的是已存在的实例本身而不是其拷贝。
请看下面这个示例,其使用了之前定义的`VideoMode`类:
请看下面这个示例,其使用了之前定义的 `VideoMode` 类:
```swift
let tenEighty = VideoMode()
@ -231,25 +231,25 @@ tenEighty.name = "1080i"
tenEighty.frameRate = 25.0
```
以上示例中,声明了一个名为`tenEighty`的常量,其引用了一个`VideoMode`类的新实例。在之前的示例中这个视频模式video mode被赋予了HD分辨率`1920`*`1080`)的一个拷贝(即`hd`实例)。同时设置为`interlaced`,命名为`“1080i”`。最后,其帧率是`25.0`帧每秒。
以上示例中,声明了一个名为 `tenEighty` 的常量,其引用了一个 `VideoMode` 类的新实例。在之前的示例中这个视频模式video mode被赋予了 HD 分辨率(`1920`*`1080`)的一个拷贝(即 `hd` 实例)。同时设置为 `interlaced`,命名为 `“1080i”`。最后,其帧率是 `25.0` 帧每秒。
然后,`tenEighty`被赋予名为`alsoTenEighty`的新常量,同时对`alsoTenEighty`的帧率进行修改:
然后,`tenEighty` 被赋予名为 `alsoTenEighty` 的新常量,同时对 `alsoTenEighty` 的帧率进行修改:
```swift
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0
```
因为类是引用类型,所以`tenEight``alsoTenEight`实际上引用的是相同的`VideoMode`实例。换句话说,它们是同一个实例的两种叫法。
因为类是引用类型,所以 `tenEight``alsoTenEight` 实际上引用的是相同的 `VideoMode` 实例。换句话说,它们是同一个实例的两种叫法。
下面,通过查看`tenEighty``frameRate`属性,我们会发现它正确的显示了所引用的`VideoMode`实例的新帧率,其值为`30.0`
下面,通过查看 `tenEighty``frameRate` 属性,我们会发现它正确的显示了所引用的 `VideoMode` 实例的新帧率,其值为 `30.0`
```swift
print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// 打印 "The frameRate property of theEighty is now 30.0"
```
需要注意的是`tenEighty``alsoTenEighty`被声明为常量而不是变量。然而你依然可以改变`tenEighty.frameRate``alsoTenEighty.frameRate`,因为`tenEighty``alsoTenEighty`这两个常量的值并未改变。它们并不“存储”这个`VideoMode`实例,而仅仅是对`VideoMode`实例的引用。所以,改变的是被引用的`VideoMode``frameRate`属性,而不是引用`VideoMode`的常量的值。
需要注意的是 `tenEighty``alsoTenEighty` 被声明为常量而不是变量。然而你依然可以改变 `tenEighty.frameRate``alsoTenEighty.frameRate`,因为 `tenEighty``alsoTenEighty` 这两个常量的值并未改变。它们并不“存储”这个 `VideoMode` 实例,而仅仅是对 `VideoMode` 实例的引用。所以,改变的是被引用的 `VideoMode``frameRate` 属性,而不是引用 `VideoMode` 的常量的值。
<a name="identity_operators"></a>
### 恒等运算符
@ -298,18 +298,18 @@ if tenEighty === alsoTenEighty {
举例来说,以下情境中适合使用结构体:
* 几何形状的大小,封装一个`width`属性和`height`属性,两者均为`Double`类型。
* 一定范围内的路径,封装一个`start`属性和`length`属性,两者均为`Int`类型。
* 三维坐标系内一点,封装`x``y``z`属性,三者均为`Double`类型。
* 几何形状的大小,封装一个 `width` 属性和 `height` 属性,两者均为 `Double` 类型。
* 一定范围内的路径,封装一个 `start` 属性和 `length` 属性,两者均为 `Int` 类型。
* 三维坐标系内一点,封装 `x``y``z` 属性,三者均为 `Double` 类型。
在所有其它案例中,定义一个类,生成一个它的实例,并通过引用来管理和传递。实际中,这意味着绝大部分的自定义数据构造都应该是类,而非结构体。
<a name="assignment_and_copy_behavior_for_strings_arrays_and_dictionaries"></a>
## 字符串、数组、和字典类型的赋值与复制行为
Swift 中,许多基本类型,诸如`String``Array``Dictionary`类型均以结构体的形式实现。这意味着被赋值给新的常量或变量,或者被传入函数或方法中时,它们的值会被拷贝。
Swift 中,许多基本类型,诸如 `String``Array``Dictionary` 类型均以结构体的形式实现。这意味着被赋值给新的常量或变量,或者被传入函数或方法中时,它们的值会被拷贝。
Objective-C 中`NSString``NSArray``NSDictionary`类型均以类的形式实现,而并非结构体。它们在被赋值或者被传入函数或方法时,不会发生值拷贝,而是传递现有实例的引用。
Objective-C 中 `NSString``NSArray``NSDictionary` 类型均以类的形式实现,而并非结构体。它们在被赋值或者被传入函数或方法时,不会发生值拷贝,而是传递现有实例的引用。
> 注意
>

View File

@ -118,7 +118,7 @@ manager.data.append("Some more data")
`DataManager` 管理数据时也可能不从文件中导入数据。所以当 `DataManager` 的实例被创建时,没必要创建一个 `DataImporter` 的实例,更明智的做法是第一次用到 `DataImporter` 的时候才去创建它。
由于使用了 `lazy` `importer` 属性只有在第一次被访问的时候才被创建。比如访问它的属性 `fileName` 时:
由于使用了 `lazy``importer` 属性只有在第一次被访问的时候才被创建。比如访问它的属性 `fileName` 时:
```swift
print(manager.importer.fileName)
@ -178,13 +178,13 @@ print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
- `Size` 封装了一个 `width` 和一个 `height`
- `Rect` 表示一个有原点和尺寸的矩形
`Rect`也提供了一个名为`center` 的计算属性。一个矩形的中心点可以从原点(`origin`)和大小(`size`)算出,所以不需要将它以显式声明的 `Point` 来保存。`Rect` 的计算属性 `center` 提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。
`Rect` 也提供了一个名为 `center` 的计算属性。一个矩形的中心点可以从原点(`origin`)和大小(`size`)算出,所以不需要将它以显式声明的 `Point` 来保存。`Rect` 的计算属性 `center` 提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。
上述例子中创建了一个名为 `square``Rect` 实例,初始值原点是 `(0, 0)`,宽度高度都是 `10`。如下图中蓝色正方形所示。
`square``center` 属性可以通过点运算符(`square.center`)来访问,这会调用该属性的 getter 来获取它的值。跟直接返回已经存在的值不同getter 实际上通过计算然后返回一个新的 `Point` 来表示 `square` 的中心点。如代码所示,它正确返回了中心点 `(5, 5)`
`center` 属性之后被设置了一个新的值 `(15, 15)`,表示向右上方移动正方形到如下图橙色正方形所示的位置。设置属性`center`的值会调用它的 setter 来修改属性 `origin``x``y` 的值,从而实现移动正方形到新的位置。
`center` 属性之后被设置了一个新的值 `(15, 15)`,表示向右上方移动正方形到如下图橙色正方形所示的位置。设置属性 `center` 的值会调用它的 setter 来修改属性 `origin``x``y` 的值,从而实现移动正方形到新的位置。
<img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/computedProperties_2x.png" alt="Computed Properties sample" width="388" height="387" />
@ -308,7 +308,7 @@ stepCounter.totalSteps = 896
> 注意
>
> 全局的常量或变量都是延迟计算的,跟[延迟存储属性](#lazy_stored_properties)相似,不同的地方在于,全局的常量或变量不需要标记`lazy`修饰符。
> 全局的常量或变量都是延迟计算的,跟[延迟存储属性](#lazy_stored_properties)相似,不同的地方在于,全局的常量或变量不需要标记 `lazy` 修饰符。
>
> 局部范围的常量或变量从不延迟计算。
@ -410,7 +410,7 @@ struct AudioChannel {
结构 `AudioChannel` 定义了 2 个存储型类型属性来实现上述功能。第一个是 `thresholdLevel`,表示音量的最大上限阈值,它是一个值为 `10` 的常量,对所有实例都可见,如果音量高于 `10`,则取最大上限值 `10`(见后面描述)。
第二个类型属性是变量存储型属性 `maxInputLevelForAllChannels`,它用来表示所有 `AudioChannel` 实例的最大音量,初始值是`0`
第二个类型属性是变量存储型属性 `maxInputLevelForAllChannels`,它用来表示所有 `AudioChannel` 实例的最大音量,初始值是 `0`
`AudioChannel` 也定义了一个名为 `currentLevel` 的存储型实例属性,表示当前声道现在的音量,取值为 `0``10`

View File

@ -10,16 +10,17 @@
> 2.1
> 翻译:[DianQK](https://github.com/DianQK)[Realank](https://github.com/Realank) 校对:[shanks](http://codebuild.me)2016-01-18
>
> 2.2
> 校对:[SketchK](https://github.com/SketchK) 2016-05-13
> 3.0.1shanks2016-11-13
> 4.0
> 校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
本页包含内容:
@ -37,7 +38,7 @@
实例方法要写在它所属的类型的前后大括号之间。实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的某个特定实例调用。实例方法不能脱离于现存的实例而被调用。
下面的例子,定义一个很简单的`Counter`类,`Counter`能被用来对一个动作发生的次数进行计数:
下面的例子,定义一个很简单的 `Counter` 类,`Counter` 能被用来对一个动作发生的次数进行计数:
```swift
class Counter {
@ -54,12 +55,12 @@ class Counter {
}
```
`Counter`类定义了三个实例方法:
- `increment`让计数器按一递增;
- `increment(by: Int)`让计数器按一个指定的整数值递增;
- `reset`将计数器重置为0。
`Counter` 类定义了三个实例方法:
- `increment` 让计数器按一递增;
- `increment(by: Int)` 让计数器按一个指定的整数值递增;
- `reset` 将计数器重置为0。
`Counter`这个类还声明了一个可变属性`count`,用它来保持对当前计数器值的追踪。
`Counter` 这个类还声明了一个可变属性 `count`,用它来保持对当前计数器值的追踪。
和调用属性一样用点语法dot syntax调用实例方法
@ -79,9 +80,9 @@ counter.reset()
<a name="the_self_property"></a>
### self 属性
类型的每一个实例都有一个隐含属性叫做`self``self`完全等同于该实例本身。你可以在一个实例的实例方法中使用这个隐含的`self`属性来引用当前实例。
类型的每一个实例都有一个隐含属性叫做 `self``self` 完全等同于该实例本身。你可以在一个实例的实例方法中使用这个隐含的 `self` 属性来引用当前实例。
上面例子中的`increment`方法还可以这样写:
上面例子中的 `increment` 方法还可以这样写:
```swift
func increment() {
@ -89,11 +90,11 @@ func increment() {
}
```
实际上,你不必在你的代码里面经常写`self`。不论何时,只要在一个方法中使用一个已知的属性或者方法名称,如果你没有明确地写`self`Swift 假定你是指当前实例的属性或者方法。这种假定在上面的`Counter`中已经示范了:`Counter`中的三个实例方法中都使用的是`count`(而不是`self.count`)。
实际上,你不必在你的代码里面经常写 `self`。不论何时,只要在一个方法中使用一个已知的属性或者方法名称,如果你没有明确地写 `self`Swift 假定你是指当前实例的属性或者方法。这种假定在上面的 `Counter` 中已经示范了:`Counter` 中的三个实例方法中都使用的是 `count`(而不是 `self.count`)。
使用这条规则的主要场景是实例方法的某个参数名称与实例的某个属性名称相同的时候。在这种情况下,参数名称享有优先权,并且在引用属性时必须使用一种更严格的方式。这时你可以使用`self`属性来区分参数名称和属性名称。
使用这条规则的主要场景是实例方法的某个参数名称与实例的某个属性名称相同的时候。在这种情况下,参数名称享有优先权,并且在引用属性时必须使用一种更严格的方式。这时你可以使用 `self` 属性来区分参数名称和属性名称。
下面的例子中,`self`消除方法参数`x`和实例属性`x`之间的歧义:
下面的例子中,`self` 消除方法参数 `x` 和实例属性 `x` 之间的歧义:
```swift
struct Point {
@ -109,16 +110,16 @@ if somePoint.isToTheRightOfX(1.0) {
// 打印 "This point is to the right of the line where x == 1.0"
```
如果不使用`self`前缀Swift 就认为两次使用的`x`都指的是名称为`x`的函数参数。
如果不使用 `self` 前缀Swift 就认为两次使用的 `x` 都指的是名称为 `x` 的函数参数。
<a name="modifying_value_types_from_within_instance_methods"></a>
### 在实例方法中修改值类型
结构体和枚举是*值类型*。默认情况下,值类型的属性不能在它的实例方法中被修改。
但是,如果你确实需要在某个特定的方法中修改结构体或者枚举的属性,你可以为这个方法选择`可变(mutating)`行为,然后就可以从其方法内部改变它的属性;并且这个方法做的任何改变都会在方法执行结束时写回到原始结构中。方法还可以给它隐含的`self`属性赋予一个全新的实例,这个新实例在方法结束时会替换现存实例。
但是,如果你确实需要在某个特定的方法中修改结构体或者枚举的属性,你可以为这个方法选择 `可变(mutating)`行为,然后就可以从其方法内部改变它的属性;并且这个方法做的任何改变都会在方法执行结束时写回到原始结构中。方法还可以给它隐含的 `self` 属性赋予一个全新的实例,这个新实例在方法结束时会替换现存实例。
要使用`可变`方法,将关键字`mutating` 放到方法的`func`关键字之前就可以了:
要使用 `可变`方法,将关键字 `mutating` 放到方法的 `func` 关键字之前就可以了:
```swift
struct Point {
@ -134,7 +135,7 @@ print("The point is now at (\(somePoint.x), \(somePoint.y))")
// 打印 "The point is now at (3.0, 4.0)"
```
上面的`Point`结构体定义了一个可变方法 `moveByX(_:y:)` 来移动`Point`实例到给定的位置。该方法被调用时修改了这个点,而不是返回一个新的点。方法定义时加上了`mutating`关键字,从而允许修改属性。
上面的 `Point` 结构体定义了一个可变方法 `moveByX(_:y:)` 来移动 `Point` 实例到给定的位置。该方法被调用时修改了这个点,而不是返回一个新的点。方法定义时加上了 `mutating` 关键字,从而允许修改属性。
注意不能在结构体类型的常量a constant of structure type上调用可变方法因为其属性不能被改变即使属性是变量属性详情参见[常量结构体的存储属性](./10_Properties.html#stored_properties_of_constant_structure_instances)
@ -147,7 +148,7 @@ fixedPoint.moveByX(2.0, y: 3.0)
<a name="assigning_to_self_within_a_mutating_method"></a>
### 在可变方法中给 self 赋值
可变方法能够赋给隐含属性`self`一个全新的实例。上面`Point`的例子可以用下面的方式改写:
可变方法能够赋给隐含属性 `self` 一个全新的实例。上面 `Point` 的例子可以用下面的方式改写:
```swift
struct Point {
@ -158,9 +159,9 @@ struct Point {
}
```
新版的可变方法` moveBy(x:y:)`创建了一个新的结构体实例,它的 x 和 y 的值都被设定为目标值。调用这个版本的方法和调用上个版本的最终结果是一样的。
新版的可变方法 `moveBy(x:y:)` 创建了一个新的结构体实例,它的 x 和 y 的值都被设定为目标值。调用这个版本的方法和调用上个版本的最终结果是一样的。
枚举的可变方法可以把`self`设置为同一枚举类型中不同的成员:
枚举的可变方法可以把 `self` 设置为同一枚举类型中不同的成员:
```swift
enum TriStateSwitch {
@ -183,18 +184,18 @@ ovenLight.next()
// ovenLight 现在等于 .Off
```
上面的例子中定义了一个三态开关的枚举。每次调用`next()`方法时,开关在不同的电源状态(`Off``Low``High`)之间循环切换。
上面的例子中定义了一个三态开关的枚举。每次调用 `next()` 方法时,开关在不同的电源状态(`Off``Low``High`)之间循环切换。
<a name="type_methods"></a>
## 类型方法
实例方法是被某个类型的实例调用的方法。你也可以定义在类型本身上调用的方法,这种方法就叫做*类型方法*。在方法的`func`关键字之前加上关键字`static`,来指定类型方法。类还可以用关键字`class`来允许子类重写父类的方法实现。
实例方法是被某个类型的实例调用的方法。你也可以定义在类型本身上调用的方法,这种方法就叫做*类型方法*。在方法的 `func` 关键字之前加上关键字 `static`,来指定类型方法。类还可以用关键字 `class` 来允许子类重写父类的方法实现。
> 注意
> 注意
>
> 在 Objective-C 中,你只能为 Objective-C 的类类型classes定义类型方法type-level methods。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法。每一个类型方法都被它所支持的类型显式包含。
类型方法和实例方法一样用点语法调用。但是,你是在类型上调用这个方法,而不是在实例上调用。下面是如何在`SomeClass`类上调用类型方法的例子:
类型方法和实例方法一样用点语法调用。但是,你是在类型上调用这个方法,而不是在实例上调用。下面是如何在 `SomeClass` 类上调用类型方法的例子:
```swift
class SomeClass {
@ -205,13 +206,13 @@ class SomeClass {
SomeClass.someTypeMethod()
```
在类型方法的方法体body`self`指向这个类型本身,而不是类型的某个实例。这意味着你可以用`self`来消除类型属性和类型方法参数之间的歧义(类似于我们在前面处理实例属性和实例方法参数时做的那样)。
在类型方法的方法体body`self` 指向这个类型本身,而不是类型的某个实例。这意味着你可以用 `self` 来消除类型属性和类型方法参数之间的歧义(类似于我们在前面处理实例属性和实例方法参数时做的那样)。
一般来说,在类型方法的方法体中,任何未限定的方法和属性名称,可以被本类中其他的类型方法和类型属性引用。一个类型方法可以直接通过类型方法的名称调用本类中的其它类型方法,而无需在方法名称前面加上类型名称。类似地,在结构体和枚举中,也能够直接通过类型属性的名称访问本类中的类型属性,而不需要前面加上类型名称。
下面的例子定义了一个名为`LevelTracker`结构体。它监测玩家的游戏发展情况(游戏的不同层次或阶段)。这是一个单人游戏,但也可以存储多个玩家在同一设备上的游戏信息。
下面的例子定义了一个名为 `LevelTracker` 结构体。它监测玩家的游戏发展情况(游戏的不同层次或阶段)。这是一个单人游戏,但也可以存储多个玩家在同一设备上的游戏信息。
游戏初始时,所有的游戏等级(除了等级 1都被锁定。每次有玩家完成一个等级这个等级就对这个设备上的所有玩家解锁。`LevelTracker`结构体用类型属性和方法监测游戏的哪个等级已经被解锁。它还监测每个玩家的当前等级。
游戏初始时,所有的游戏等级(除了等级 1都被锁定。每次有玩家完成一个等级这个等级就对这个设备上的所有玩家解锁。`LevelTracker` 结构体用类型属性和方法监测游戏的哪个等级已经被解锁。它还监测每个玩家的当前等级。
```swift
struct LevelTracker {
@ -238,15 +239,15 @@ struct LevelTracker {
}
```
`LevelTracker`监测玩家已解锁的最高等级。这个值被存储在类型属性`highestUnlockedLevel`中。
`LevelTracker` 监测玩家已解锁的最高等级。这个值被存储在类型属性 `highestUnlockedLevel` 中。
`LevelTracker`还定义了两个类型方法与`highestUnlockedLevel`配合工作。第一个类型方法是`unlock(_:)`,一旦新等级被解锁,它会更新`highestUnlockedLevel`的值。第二个类型方法是`isUnlocked(_:)`,如果某个给定的等级已经被解锁,它将返回`true`。(注意,尽管我们没有使用类似`LevelTracker.highestUnlockedLevel`的写法,这个类型方法还是能够访问类型属性`highestUnlockedLevel`
`LevelTracker` 还定义了两个类型方法与 `highestUnlockedLevel` 配合工作。第一个类型方法是 `unlock(_:)`,一旦新等级被解锁,它会更新 `highestUnlockedLevel` 的值。第二个类型方法是 `isUnlocked(_:)`,如果某个给定的等级已经被解锁,它将返回 `true`。(注意,尽管我们没有使用类似 `LevelTracker.highestUnlockedLevel` 的写法,这个类型方法还是能够访问类型属性 `highestUnlockedLevel`
除了类型属性和类型方法,`LevelTracker`还监测每个玩家的进度。它用实例属性`currentLevel`来监测每个玩家当前的等级。
除了类型属性和类型方法,`LevelTracker` 还监测每个玩家的进度。它用实例属性 `currentLevel` 来监测每个玩家当前的等级。
为了便于管理`currentLevel`属性,`LevelTracker`定义了实例方法`advance(to:)`。这个方法会在更新`currentLevel`之前检查所请求的新等级是否已经解锁。`advance(to:)`方法返回布尔值以指示是否能够设置`currentLevel`。因为允许在调用`advance(to:)`时候忽略返回值,不会产生编译警告,所以函数被标注为`@ discardableResult`属性,更多关于属性信息,请参考[属性](../chapter3/07_Attributes.html)章节。
为了便于管理 `currentLevel` 属性,`LevelTracker` 定义了实例方法 `advance(to:)`。这个方法会在更新 `currentLevel` 之前检查所请求的新等级是否已经解锁。`advance(to:)` 方法返回布尔值以指示是否能够设置 `currentLevel`。因为允许在调用 `advance(to:)` 时候忽略返回值,不会产生编译警告,所以函数被标注为 `@ discardableResult` 属性,更多关于属性信息,请参考[属性](../chapter3/07_Attributes.html)章节。
下面,`Player`类使用`LevelTracker`来监测和更新每个玩家的发展进度:
下面,`Player` 类使用 `LevelTracker` 来监测和更新每个玩家的发展进度:
```swift
class Player {
@ -262,9 +263,9 @@ class Player {
}
```
`Player`类创建一个新的`LevelTracker`实例来监测这个用户的进度。它提供了`complete(level:)`方法,一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了`advance(to:)`返回的布尔值,因为之前调用`LevelTracker.unlock(_:)`时就知道了这个等级已经被解锁了)。
`Player` 类创建一个新的 `LevelTracker` 实例来监测这个用户的进度。它提供了 `complete(level:)` 方法,一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了 `advance(to:)` 返回的布尔值,因为之前调用 `LevelTracker.unlock(_:)` 时就知道了这个等级已经被解锁了)。
你还可以为一个新的玩家创建一个`Player`的实例,然后看这个玩家完成等级一时发生了什么:
你还可以为一个新的玩家创建一个 `Player` 的实例,然后看这个玩家完成等级一时发生了什么:
```swift
var player = Player(name: "Argyrios")

View File

@ -16,10 +16,10 @@
> 3.0.1shanks2016-11-13
> 4.0
> 校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
> 校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
本页包含内容:
@ -27,14 +27,14 @@
- [下标用法](#subscript_usage)
- [下标选项](#subscript_options)
*下标*可以定义在类、结构体和枚举中,是访问集合、列表或序列中元素的快捷方式。可以使用下标的索引,设置和获取值,而不需要再调用对应的存取方法。举例来说,用下标访问一个`Array`实例中的元素可以写作`someArray[index]`,访问`Dictionary`实例中的元素可以写作`someDictionary[key]`
*下标*可以定义在类、结构体和枚举中,是访问集合、列表或序列中元素的快捷方式。可以使用下标的索引,设置和获取值,而不需要再调用对应的存取方法。举例来说,用下标访问一个 `Array` 实例中的元素可以写作 `someArray[index]`,访问 `Dictionary` 实例中的元素可以写作 `someDictionary[key]`
一个类型可以定义多个下标,通过不同索引类型进行重载。下标不限于一维,你可以定义具有多个入参的下标满足自定义类型的需求。
<a name="subscript_syntax"></a>
## 下标语法
下标允许你通过在实例名称后面的方括号中传入一个或者多个索引值来对实例进行存取。语法类似于实例方法语法和计算型属性语法的混合。与定义实例方法类似,定义下标使用`subscript`关键字,指定一个或多个输入参数和返回类型;与实例方法不同的是,下标可以设定为读写或只读。这种行为由 getter 和 setter 实现,有点类似计算型属性:
下标允许你通过在实例名称后面的方括号中传入一个或者多个索引值来对实例进行存取。语法类似于实例方法语法和计算型属性语法的混合。与定义实例方法类似,定义下标使用 `subscript` 关键字,指定一个或多个输入参数和返回类型;与实例方法不同的是,下标可以设定为读写或只读。这种行为由 getter 和 setter 实现,有点类似计算型属性:
```swift
subscript(index: Int) -> Int {
@ -48,9 +48,9 @@ subscript(index: Int) -> Int {
}
```
`newValue`的类型和下标的返回类型相同。如同计算型属性,可以不指定 setter 的参数(`newValue`。如果不指定参数setter 会提供一个名为`newValue`的默认参数。
`newValue` 的类型和下标的返回类型相同。如同计算型属性,可以不指定 setter 的参数(`newValue`。如果不指定参数setter 会提供一个名为 `newValue` 的默认参数。
如同只读计算型属性,可以省略只读下标的`get`关键字:
如同只读计算型属性,可以省略只读下标的 `get` 关键字:
```swift
subscript(index: Int) -> Int {
@ -58,7 +58,7 @@ subscript(index: Int) -> Int {
}
```
下面代码演示了只读下标的实现,这里定义了一个`TimesTable`结构体,用来表示传入整数的乘法表:
下面代码演示了只读下标的实现,这里定义了一个 `TimesTable` 结构体,用来表示传入整数的乘法表:
```swift
struct TimesTable {
@ -72,33 +72,33 @@ print("six times three is \(threeTimesTable[6])")
// 打印 "six times three is 18"
```
在上例中,创建了一个`TimesTable`实例,用来表示整数`3`的乘法表。数值`3`被传递给结构体的构造函数,作为实例成员`multiplier`的值。
在上例中,创建了一个 `TimesTable` 实例,用来表示整数 `3` 的乘法表。数值 `3` 被传递给结构体的构造函数,作为实例成员 `multiplier` 的值。
你可以通过下标访问`threeTimesTable`实例,例如上面演示的`threeTimesTable[6]`。这条语句查询了`3`的乘法表中的第六个元素,返回`3``6`倍即`18`
你可以通过下标访问 `threeTimesTable` 实例,例如上面演示的 `threeTimesTable[6]`。这条语句查询了 `3` 的乘法表中的第六个元素,返回 `3``6` 倍即 `18`
> 注意
> 注意
>
> `TimesTable`例子基于一个固定的数学公式,对`threeTimesTable[someIndex]`进行赋值操作并不合适,因此下标定义为只读的。
> `TimesTable` 例子基于一个固定的数学公式,对 `threeTimesTable[someIndex]` 进行赋值操作并不合适,因此下标定义为只读的。
<a name="subscript_usage"></a>
## 下标用法
下标的确切含义取决于使用场景。下标通常作为访问集合,列表或序列中元素的快捷方式。你可以针对自己特定的类或结构体的功能来自由地以最恰当的方式实现下标。
例如Swift 的`Dictionary`类型实现下标用于对其实例中储存的值进行存取操作。为字典设值时,在下标中使用和字典的键类型相同的键,并把一个和字典的值类型相同的值赋给这个下标:
例如Swift 的 `Dictionary` 类型实现下标用于对其实例中储存的值进行存取操作。为字典设值时,在下标中使用和字典的键类型相同的键,并把一个和字典的值类型相同的值赋给这个下标:
```swift
var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
numberOfLegs["bird"] = 2
```
上例定义一个名为`numberOfLegs`的变量,并用一个包含三对键值的字典字面量初始化它。`numberOfLegs`字典的类型被推断为`[String: Int]`。字典创建完成后,该例子通过下标将`String`类型的键`bird``Int`类型的值`2`添加到字典中。
上例定义一个名为 `numberOfLegs` 的变量,并用一个包含三对键值的字典字面量初始化它。`numberOfLegs` 字典的类型被推断为 `[String: Int]`。字典创建完成后,该例子通过下标将 `String` 类型的键 `bird``Int` 类型的值 `2` 添加到字典中。
更多关于`Dictionary`下标的信息请参考[读取和修改字典](./04_Collection_Types.html#accessing_and_modifying_a_dictionary)
更多关于 `Dictionary` 下标的信息请参考[读取和修改字典](./04_Collection_Types.html#accessing_and_modifying_a_dictionary)
> 注意
> 注意
>
> Swift 的`Dictionary`类型的下标接受并返回可选类型的值。上例中的`numberOfLegs`字典通过下标返回的是一个`Int?`或者说“可选的int”。`Dictionary`类型之所以如此实现下标,是因为不是每个键都有个对应的值,同时这也提供了一种通过键删除对应值的方式,只需将键对应的值赋值为`nil`即可。
> Swift 的 `Dictionary` 类型的下标接受并返回可选类型的值。上例中的 `numberOfLegs` 字典通过下标返回的是一个 `Int?` 或者说“可选的 int”。`Dictionary` 类型之所以如此实现下标,是因为不是每个键都有个对应的值,同时这也提供了一种通过键删除对应值的方式,只需将键对应的值赋值为 `nil` 即可。
<a name="subscript_options"></a>
## 下标选项
@ -107,7 +107,7 @@ numberOfLegs["bird"] = 2
一个类或结构体可以根据自身需要提供多个下标实现,使用下标时将通过入参的数量和类型进行区分,自动匹配合适的下标,这就是*下标的重载*。
虽然接受单一入参的下标是最常见的,但也可以根据情况定义接受多个入参的下标。例如下例定义了一个`Matrix`结构体,用于表示一个`Double`类型的二维矩阵。`Matrix`结构体的下标接受两个整型参数:
虽然接受单一入参的下标是最常见的,但也可以根据情况定义接受多个入参的下标。例如下例定义了一个 `Matrix` 结构体,用于表示一个 `Double` 类型的二维矩阵。`Matrix` 结构体的下标接受两个整型参数:
```swift
struct Matrix {
@ -134,30 +134,30 @@ struct Matrix {
}
```
`Matrix`提供了一个接受两个入参的构造方法,入参分别是`rows``columns`,创建了一个足够容纳`rows * columns``Double`类型的值的数组。通过传入数组长度和初始值`0.0`到数组的构造器,将矩阵中每个位置的值初始化为`0.0`。关于数组的这种构造方法请参考[创建一个带有默认值的数组](./04_Collection_Types.html#creating_an_array_with_a_default_value)。
`Matrix` 提供了一个接受两个入参的构造方法,入参分别是 `rows``columns`,创建了一个足够容纳 `rows * columns``Double` 类型的值的数组。通过传入数组长度和初始值 `0.0` 到数组的构造器,将矩阵中每个位置的值初始化为 `0.0`。关于数组的这种构造方法请参考[创建一个带有默认值的数组](./04_Collection_Types.html#creating_an_array_with_a_default_value)。
你可以通过传入合适的`row``column`的数量来构造一个新的`Matrix`实例:
你可以通过传入合适的 `row``column` 的数量来构造一个新的 `Matrix` 实例:
```swift
var matrix = Matrix(rows: 2, columns: 2)
```
上例中创建了一个`Matrix`实例来表示两行两列的矩阵。该`Matrix`实例的`grid`数组按照从左上到右下的阅读顺序将矩阵扁平化存储:
上例中创建了一个 `Matrix` 实例来表示两行两列的矩阵。该 `Matrix` 实例的 `grid` 数组按照从左上到右下的阅读顺序将矩阵扁平化存储:
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/subscriptMatrix01_2x.png)
`row``column`的值传入下标来为矩阵设值,下标的入参使用逗号分隔:
`row``column` 的值传入下标来为矩阵设值,下标的入参使用逗号分隔:
```swift
matrix[0, 1] = 1.5
matrix[1, 0] = 3.2
```
上面两条语句分别调用下标的 setter 将矩阵右上角位置(即`row``0``column``1`的位置)的值设置为`1.5`,将矩阵左下角位置(即`row``1``column``0`的位置)的值设置为`3.2`
上面两条语句分别调用下标的 setter 将矩阵右上角位置(即 `row``0``column``1` 的位置)的值设置为 `1.5`,将矩阵左下角位置(即 `row``1``column``0` 的位置)的值设置为 `3.2`
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/subscriptMatrix02_2x.png)
`Matrix`下标的 getter 和 setter 中都含有断言,用来检查下标入参`row``column`的值是否有效。为了方便进行断言,`Matrix`包含了一个名为`indexIsValid(row:column:)`的便利方法,用来检查入参`row``column`的值是否在矩阵范围内:
`Matrix` 下标的 getter 和 setter 中都含有断言,用来检查下标入参 `row``column` 的值是否有效。为了方便进行断言,`Matrix` 包含了一个名为 `indexIsValid(row:column:)` 的便利方法,用来检查入参 `row``column` 的值是否在矩阵范围内:
```swift
func indexIsValid(row: Int, column: Int) -> Bool {

View File

@ -7,15 +7,16 @@
> 2.02.1
> 翻译+校对:[shanks](http://codebuild.me)
>
> 2.2
> 校对:[SketchK](https://github.com/SketchK) 2016-05-13
> 3.0.1shanks2016-11-13
> 4.0
> 校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
> 4.1
> 校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
本页包含内容:
@ -36,13 +37,13 @@
不继承于其它类的类,称之为*基类*。
> 注意
>
> 注意
>
> Swift 中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。
下面的例子定义了一个叫`Vehicle`的基类。这个基类声明了一个名为`currentSpeed `,默认值是`0.0`的存储属性(属性类型推断为`Double`)。`currentSpeed`属性的值被一个`String`类型的只读计算型属性`description`使用,用来创建车辆的描述。
下面的例子定义了一个叫 `Vehicle` 的基类。这个基类声明了一个名为 `currentSpeed`,默认值是 `0.0` 的存储属性(属性类型推断为 `Double`)。`currentSpeed` 属性的值被一个 `String` 类型的只读计算型属性 `description` 使用,用来创建车辆的描述。
`Vehicle`基类也定义了一个名为`makeNoise`的方法。这个方法实际上不为`Vehicle`实例做任何事,但之后将会被`Vehicle`的子类定制:
`Vehicle` 基类也定义了一个名为 `makeNoise` 的方法。这个方法实际上不为 `Vehicle` 实例做任何事,但之后将会被 `Vehicle` 的子类定制:
```swift
class Vehicle {
@ -56,20 +57,20 @@ class Vehicle {
}
```
您可以用初始化语法创建一个`Vehicle`的新实例,即类名后面跟一个空括号:
您可以用初始化语法创建一个 `Vehicle` 的新实例,即类名后面跟一个空括号:
```swift
let someVehicle = Vehicle()
```
现在已经创建了一个`Vehicle`的新实例,你可以访问它的`description`属性来打印车辆的当前速度:
现在已经创建了一个 `Vehicle` 的新实例,你可以访问它的 `description` 属性来打印车辆的当前速度:
```swift
print("Vehicle: \(someVehicle.description)")
// 打印 "Vehicle: traveling at 0.0 miles per hour"
```
`Vehicle`类定义了一个通用特性的车辆类,实际上没什么用处。为了让它变得更加有用,需要完善它从而能够描述一个更加具体类型的车辆。
`Vehicle` 类定义了一个通用特性的车辆类,实际上没什么用处。为了让它变得更加有用,需要完善它从而能够描述一个更加具体类型的车辆。
<a name="subclassing"></a>
## 子类生成
@ -84,7 +85,7 @@ class SomeClass: SomeSuperclass {
}
```
下一个例子,定义一个叫`Bicycle`的子类,继承成父类`Vehicle`
下一个例子,定义一个叫 `Bicycle` 的子类,继承成父类 `Vehicle`
```swift
class Bicycle: Vehicle {
@ -92,18 +93,18 @@ class Bicycle: Vehicle {
}
```
新的`Bicycle`类自动获得`Vehicle`类的所有特性,比如`currentSpeed``description`属性,还有它的`makeNoise()`方法。
新的 `Bicycle` 类自动获得 `Vehicle` 类的所有特性,比如 `currentSpeed``description` 属性,还有它的 `makeNoise()` 方法。
除了它所继承的特性,`Bicycle`类还定义了一个默认值为`false`的存储型属性`hasBasket`(属性推断为`Bool`)。
除了它所继承的特性,`Bicycle` 类还定义了一个默认值为 `false` 的存储型属性 `hasBasket`(属性推断为 `Bool`)。
默认情况下,你创建任何新的`Bicycle`实例将不会有一个篮子(即`hasBasket`属性默认为`false`),创建该实例之后,你可以为特定的`Bicycle`实例设置`hasBasket`属性为`ture`
默认情况下,你创建任何新的 `Bicycle` 实例将不会有一个篮子(即 `hasBasket` 属性默认为 `false`),创建该实例之后,你可以为特定的 `Bicycle` 实例设置 `hasBasket` 属性为 `ture`
```swift
let bicycle = Bicycle()
bicycle.hasBasket = true
```
你还可以修改`Bicycle`实例所继承的`currentSpeed`属性,和查询实例所继承的`description`属性:
你还可以修改 `Bicycle` 实例所继承的 `currentSpeed` 属性,和查询实例所继承的 `description` 属性:
```swift
bicycle.currentSpeed = 15.0
@ -111,7 +112,7 @@ print("Bicycle: \(bicycle.description)")
// 打印 "Bicycle: traveling at 15.0 miles per hour"
```
子类还可以继续被其它类继承,下面的示例为`Bicycle`创建了一个名为`Tandem`(双人自行车)的子类:
子类还可以继续被其它类继承,下面的示例为 `Bicycle` 创建了一个名为 `Tandem`(双人自行车)的子类:
```swift
class Tandem: Bicycle {
@ -119,9 +120,9 @@ class Tandem: Bicycle {
}
```
`Tandem``Bicycle`继承了所有的属性与方法,这又使它同时继承了`Vehicle`的所有属性与方法。`Tandem`也增加了一个新的叫做`currentNumberOfPassengers`的存储型属性,默认值为`0`
`Tandem``Bicycle` 继承了所有的属性与方法,这又使它同时继承了 `Vehicle` 的所有属性与方法。`Tandem` 也增加了一个新的叫做 `currentNumberOfPassengers` 的存储型属性,默认值为 `0`
如果你创建了一个`Tandem`的实例,你可以使用它所有的新属性和继承的属性,还能查询从`Vehicle`继承来的只读属性`description`
如果你创建了一个 `Tandem` 的实例,你可以使用它所有的新属性和继承的属性,还能查询从 `Vehicle` 继承来的只读属性 `description`
```swift
let tandem = Tandem()
@ -137,25 +138,25 @@ print("Tandem: \(tandem.description)")
子类可以为继承来的实例方法,类方法,实例属性,或下标提供自己定制的实现。我们把这种行为叫*重写*。
如果要重写某个特性,你需要在重写定义的前面加上`override`关键字。这么做,你就表明了你是想提供一个重写版本,而非错误地提供了一个相同的定义。意外的重写行为可能会导致不可预知的错误,任何缺少`override`关键字的重写都会在编译时被诊断为错误。
如果要重写某个特性,你需要在重写定义的前面加上 `override` 关键字。这么做,你就表明了你是想提供一个重写版本,而非错误地提供了一个相同的定义。意外的重写行为可能会导致不可预知的错误,任何缺少 `override` 关键字的重写都会在编译时被诊断为错误。
`override`关键字会提醒 Swift 编译器去检查该类的超类(或其中一个父类)是否有匹配重写版本的声明。这个检查可以确保你的重写定义是正确的。
`override` 关键字会提醒 Swift 编译器去检查该类的超类(或其中一个父类)是否有匹配重写版本的声明。这个检查可以确保你的重写定义是正确的。
### 访问超类的方法,属性及下标
当你在子类中重写超类的方法,属性或下标时,有时在你的重写版本中使用已经存在的超类实现会大有裨益。比如,你可以完善已有实现的行为,或在一个继承来的变量中存储一个修改过的值。
在合适的地方,你可以通过使用`super`前缀来访问超类版本的方法,属性或下标:
在合适的地方,你可以通过使用 `super` 前缀来访问超类版本的方法,属性或下标:
* 在方法`someMethod()`的重写实现中,可以通过`super.someMethod()`来调用超类版本的`someMethod()`方法。
* 在属性`someProperty`的 getter 或 setter 的重写实现中,可以通过`super.someProperty`来访问超类版本的`someProperty`属性。
* 在下标的重写实现中,可以通过`super[someIndex]`来访问超类版本中的相同下标。
* 在方法 `someMethod()` 的重写实现中,可以通过 `super.someMethod()` 来调用超类版本的 `someMethod()` 方法。
* 在属性 `someProperty` 的 getter 或 setter 的重写实现中,可以通过 `super.someProperty` 来访问超类版本的 `someProperty` 属性。
* 在下标的重写实现中,可以通过 `super[someIndex]` 来访问超类版本中的相同下标。
### 重写方法
在子类中,你可以重写继承来的实例方法或类方法,提供一个定制或替代的方法实现。
下面的例子定义了`Vehicle`的一个新的子类,叫`Train`,它重写了从`Vehicle`类继承来的`makeNoise()`方法:
下面的例子定义了 `Vehicle` 的一个新的子类,叫 `Train`,它重写了从 `Vehicle` 类继承来的 `makeNoise()` 方法:
```swift
class Train: Vehicle {
@ -165,7 +166,7 @@ class Train: Vehicle {
}
```
如果你创建一个`Train`的新实例,并调用了它的`makeNoise()`方法,你就会发现`Train`版本的方法被调用:
如果你创建一个 `Train` 的新实例,并调用了它的 `makeNoise()` 方法,你就会发现 `Train` 版本的方法被调用:
```swift
let train = Train()
@ -183,11 +184,11 @@ train.makeNoise()
你可以将一个继承来的只读属性重写为一个读写属性,只需要在重写版本的属性里提供 getter 和 setter 即可。但是,你不可以将一个继承来的读写属性重写为一个只读属性。
> 注意
>
> 如果你在重写属性中提供了 setter那么你也一定要提供 getter。如果你不想在重写版本中的 getter 里修改继承来的属性值,你可以直接通过`super.someProperty`来返回继承来的值,其中`someProperty`是你要重写的属性的名字。
> 注意
>
> 如果你在重写属性中提供了 setter那么你也一定要提供 getter。如果你不想在重写版本中的 getter 里修改继承来的属性值,你可以直接通过 `super.someProperty` 来返回继承来的值,其中 `someProperty` 是你要重写的属性的名字。
以下的例子定义了一个新类,叫`Car`,它是`Vehicle`的子类。这个类引入了一个新的存储型属性叫做`gear`,默认值为整数`1``Car`类重写了继承自`Vehicle``description`属性,提供包含当前档位的自定义描述:
以下的例子定义了一个新类,叫 `Car`,它是 `Vehicle` 的子类。这个类引入了一个新的存储型属性叫做 `gear`,默认值为整数 `1``Car` 类重写了继承自 `Vehicle``description` 属性,提供包含当前档位的自定义描述:
```swift
class Car: Vehicle {
@ -198,9 +199,9 @@ class Car: Vehicle {
}
```
重写的`description`属性首先要调用`super.description`返回`Vehicle`类的`description`属性。之后,`Car`类版本的`description`在末尾增加了一些额外的文本来提供关于当前档位的信息。
重写的 `description` 属性首先要调用 `super.description` 返回 `Vehicle` 类的 `description` 属性。之后,`Car` 类版本的 `description` 在末尾增加了一些额外的文本来提供关于当前档位的信息。
如果你创建了`Car`的实例并且设置了它的`gear``currentSpeed`属性,你可以看到它的`description`返回了`Car`中的自定义描述:
如果你创建了 `Car` 的实例并且设置了它的 `gear``currentSpeed` 属性,你可以看到它的 `description` 返回了 `Car` 中的自定义描述:
```swift
let car = Car()
@ -215,12 +216,12 @@ print("Car: \(car.description)")
你可以通过重写属性为一个继承来的属性添加属性观察器。这样一来,当继承来的属性值发生改变时,你就会被通知到,无论那个属性原本是如何实现的。关于属性观察器的更多内容,请看[属性观察器](../chapter2/10_Properties.html#property_observers)。
> 注意
>
> 你不可以为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。这些属性的值是不可以被设置的,所以,为它们提供`willSet``didSet`实现是不恰当。
> 注意
>
> 你不可以为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。这些属性的值是不可以被设置的,所以,为它们提供 `willSet``didSet` 实现是不恰当。
此外还要注意,你不可以同时提供重写的 setter 和重写的属性观察器。如果你想观察属性值的变化,并且你已经为那个属性提供了定制的 setter那么你在 setter 中就可以观察到任何值变化了。
下面的例子定义了一个新类叫`AutomaticCar`,它是`Car`的子类。`AutomaticCar`表示自动挡汽车,它可以根据当前的速度自动选择合适的挡位:
下面的例子定义了一个新类叫 `AutomaticCar`,它是 `Car` 的子类。`AutomaticCar` 表示自动挡汽车,它可以根据当前的速度自动选择合适的挡位:
```swift
class AutomaticCar: Car {
@ -232,7 +233,7 @@ class AutomaticCar: Car {
}
```
无论何时当你设置`AutomaticCar``currentSpeed`属性,属性的`didSet`观察器就会自动地设置`gear`属性,为新的速度选择一个合适的挡位。具体来说就是,属性观察器将新的速度值除以`10`,然后向下取得最接近的整数值,最后加`1`来得到档位`gear`的值。例如,速度为`35.0`时,挡位为`4`
无论何时当你设置 `AutomaticCar``currentSpeed` 属性,属性的 `didSet` 观察器就会自动地设置 `gear` 属性,为新的速度选择一个合适的挡位。具体来说就是,属性观察器将新的速度值除以 `10`,然后向下取得最接近的整数值,最后加 `1` 来得到档位 `gear` 的值。例如,速度为 `35.0` 时,挡位为 `4`
```swift
let automatic = AutomaticCar()
@ -244,8 +245,8 @@ print("AutomaticCar: \(automatic.description)")
<a name="preventing_overrides"></a>
## 防止重写
你可以通过把方法,属性或下标标记为*`final`*来防止它们被重写,只需要在声明关键字前加上`final`修饰符即可(例如:`final var``final func``final class func`,以及`final subscript`)。
你可以通过把方法,属性或下标标记为*`final`*来防止它们被重写,只需要在声明关键字前加上 `final` 修饰符即可(例如:`final var``final func``final class func`,以及 `final subscript`)。
任何试图对带有`final`标记的方法、属性或下标进行重写,都会在编译时会报错。在类扩展中的方法,属性或下标也可以在扩展的定义里标记为 final 的。
任何试图对带有 `final` 标记的方法、属性或下标进行重写,都会在编译时会报错。在类扩展中的方法,属性或下标也可以在扩展的定义里标记为 final 的。
你可以通过在关键字`class`前添加`final`修饰符(`final class`)来将整个类标记为 final 的。这样的类是不可被继承的,试图继承这样的类会导致编译报错。
你可以通过在关键字 `class` 前添加 `final` 修饰符(`final class`)来将整个类标记为 final 的。这样的类是不可被继承的,试图继承这样的类会导致编译报错。

View File

@ -41,7 +41,7 @@
通过定义*构造器*来实现构造过程,就像用来创建特定类型新实例的特殊方法。与 Objective-C 中的构造器不同Swift 的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化。
类的实例也可以通过定义*析构器*在实例释放之前执行特定的清除工作。想了解更多关于析构器的内容,请参考[析构过程](./15_Deinitialization.html)。
类的实例也可以通过定义*析构器*在实例释放之前执行特定的清除工作。想了解更多关于析构器的内容,请参考[析构过程](./15_Deinitialization.html)。
<a name="setting_initial_values_for_stored_properties"></a>
## 存储属性的初始赋值
@ -108,7 +108,7 @@ struct Fahrenheit {
自定义构造过程时,可以在定义中提供*构造参数*,指定参数值的类型和名字。构造参数的功能和语法跟函数和方法的参数相同。
下面例子中定义了一个包含摄氏度温度的结构体 `Celsius`。它定义了两个不同的构造器:`init(fromFahrenheit:)``init(fromKelvin:)`,二者分别通过接受不同温标下的温度值来创建新的实例:
下面例子中定义了一个包含摄氏度温度的结构体 `Celsius`。它定义了两个不同的构造器:`init(fromFahrenheit:)``init(fromKelvin:)`,二者分别通过接受不同温标下的温度值来创建新的实例:
```swift
struct Celsius {
@ -127,7 +127,7 @@ let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius 是 0.0
```
第一个构造器拥有一个构造参数,其外部名字为`fromFahrenheit`,内部名字为`fahrenheit`;第二个构造器也拥有一个构造参数,其外部名字为`fromKelvin`,内部名字为`kelvin`。这两个构造器都将唯一的参数值转换成摄氏温度值,并保存在属性 `temperatureInCelsius` 中。
第一个构造器拥有一个构造参数,其外部名字为 `fromFahrenheit`,内部名字为 `fahrenheit`;第二个构造器也拥有一个构造参数,其外部名字为 `fromKelvin`,内部名字为 `kelvin`。这两个构造器都将唯一的参数值转换成摄氏温度值,并保存在属性 `temperatureInCelsius` 中。
<a name="parameter_names_and_argument_labels"></a>
### 参数名和参数标签
@ -138,7 +138,7 @@ let freezingPointOfWater = Celsius(fromKelvin: 273.15)
以下例子中定义了一个结构体 `Color`,它包含了三个常量:`red``green``blue`。这些属性可以存储 `0.0``1.0` 之间的值,用来指示颜色中红、绿、蓝成分的含量。
`Color` 提供了一个构造器,其中包含三个`Double`类型的构造参数。`Color` 也提供了第二个构造器,它只包含名为`white``Double` 类型的参数,它被用于给上述三个构造参数赋予同样的值。
`Color` 提供了一个构造器,其中包含三个 `Double` 类型的构造参数。`Color` 也提供了第二个构造器,它只包含名为 `white``Double` 类型的参数,它被用于给上述三个构造参数赋予同样的值。
```swift
struct Color {
@ -156,7 +156,7 @@ struct Color {
}
```
两种构造器都能通过提供的初始参数值来创建一个新的`Color`实例:
两种构造器都能通过提供的初始参数值来创建一个新的 `Color` 实例:
```swift
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
@ -195,12 +195,12 @@ let bodyTemperature = Celsius(37.0)
// bodyTemperature.temperatureInCelsius 为 37.0
```
调用 `Celsius(37.0)` 意图明确,不需要参数标签。因此适合使用 `init(_ celsius: Double)` 这样的构造器,从而可以通过提供未命名的`Double`值调用构造器,而不需要加上参数标签。
调用 `Celsius(37.0)` 意图明确,不需要参数标签。因此适合使用 `init(_ celsius: Double)` 这样的构造器,从而可以通过提供未命名的 `Double` 值调用构造器,而不需要加上参数标签。
<a name="optional_property_types"></a>
### 可选属性类型
如果你定制的类型包含一个逻辑上允许取值为空的存储型属性——无论是因为它无法在初始化时赋值,还是因为它在之后某个时间点可以赋值为空——你都需要将它定义为`可选类型`。可选类型的属性将自动初始化为 `nil`,表示这个属性是有意在初始化时设置为空的。
如果你定制的类型包含一个逻辑上允许取值为空的存储型属性——无论是因为它无法在初始化时赋值,还是因为它在之后某个时间点可以赋值为空——你都需要将它定义为 `可选类型`。可选类型的属性将自动初始化为 `nil`,表示这个属性是有意在初始化时设置为空的。
下面例子中定义了类 `SurveyQuestion`,它包含一个可选字符串属性 `response`
@ -222,7 +222,7 @@ cheeseQuestion.ask()
cheeseQuestion.response = "Yes, I do like cheese."
```
调查问题的答案在回答前是无法确定的,因此我们将属性 `response` 声明为 `String?` 类型,或者说是`可选字符串类型`。当 `SurveyQuestion` 实例化时,它将自动赋值为`nil`,表明此字符串暂时还没有值。
调查问题的答案在回答前是无法确定的,因此我们将属性 `response` 声明为 `String?` 类型,或者说是 `可选字符串类型`。当 `SurveyQuestion` 实例化时,它将自动赋值为 `nil`,表明此字符串暂时还没有值。
<a name="assigning_constant_properties_during_initialization"></a>
### 构造过程中常量属性的赋值
@ -233,7 +233,7 @@ cheeseQuestion.response = "Yes, I do like cheese."
>
> 对于类的实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。
你可以修改上面的 `SurveyQuestion` 示例,用常量属性替代变量属性 `text`,表示问题内容 `text``SurveyQuestion`的实例被创建之后不会再被修改。尽管 `text` 属性现在是常量,我们仍然可以在类的构造器中设置它的值:
你可以修改上面的 `SurveyQuestion` 示例,用常量属性替代变量属性 `text`,表示问题内容 `text` `SurveyQuestion` 的实例被创建之后不会再被修改。尽管 `text` 属性现在是常量,我们仍然可以在类的构造器中设置它的值:
```swift
class SurveyQuestion {
@ -268,7 +268,7 @@ class ShoppingListItem {
var item = ShoppingListItem()
```
由于 `ShoppingListItem` 类中的所有属性都有默认值,且它是没有父类的基类,它将自动获得一个可以为所有属性设置默认值的默认构造器(尽管代码中没有显式为`name`属性设置默认值,但由于`name`是可选字符串类型,它将默认设置为`nil`)。上面例子中使用默认构造器创造了一个 `ShoppingListItem` 类的实例(使用 `ShoppingListItem()` 形式的构造器语法),并将其赋值给变量 `item`
由于 `ShoppingListItem` 类中的所有属性都有默认值,且它是没有父类的基类,它将自动获得一个可以为所有属性设置默认值的默认构造器(尽管代码中没有显式为 `name` 属性设置默认值,但由于 `name` 是可选字符串类型,它将默认设置为 `nil`)。上面例子中使用默认构造器创造了一个 `ShoppingListItem` 类的实例(使用 `ShoppingListItem()` 形式的构造器语法),并将其赋值给变量 `item`
<a name="memberwise_initializers_for_structure_types"></a>
### 结构体的逐一成员构造器
@ -277,7 +277,7 @@ var item = ShoppingListItem()
逐一成员构造器是用来初始化结构体新实例里成员属性的快捷方法。我们在调用逐一成员构造器时,通过与成员属性名相同的参数名进行传值来完成对成员属性的初始赋值。
下面例子中定义了一个结构体 `Size`,它包含两个属性 `width``height`。Swift 可以根据这两个属性的初始赋值`0.0` 自动推导出它们的类型为 `Double`
下面例子中定义了一个结构体 `Size`,它包含两个属性 `width``height`。Swift 可以根据这两个属性的初始赋值 `0.0` 自动推导出它们的类型为 `Double`
结构体 `Size` 自动获得了一个逐一成员构造器 `init(width:height:)`。你可以用它来创建新的 Size 实例:
@ -315,7 +315,7 @@ struct Point {
}
```
你可以通过以下三种方式为 `Rect` 创建实例——使用含有默认值的 `origin``size` 属性来初始化;提供指定的`origin``size` 实例来初始化;提供指定的 `center``size` 来初始化。在下面 `Rect` 结构体定义中,我们为这三种方式提供了三个自定义的构造器:
你可以通过以下三种方式为 `Rect` 创建实例——使用含有默认值的 `origin``size` 属性来初始化;提供指定的 `origin``size` 实例来初始化;提供指定的 `center``size` 来初始化。在下面 `Rect` 结构体定义中,我们为这三种方式提供了三个自定义的构造器:
```swift
struct Rect {
@ -363,7 +363,7 @@ let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
> 注意
>
> 如果你想用另外一种不需要自己定义`init()``init(origin:size:)`的方式来实现这个例子,请参考[扩展](./21_Extensions.html)。
> 如果你想用另外一种不需要自己定义 `init()``init(origin:size:)` 的方式来实现这个例子,请参考[扩展](./21_Extensions.html)。
<a name="class_inheritance_and_initialization"></a>
## 类的继承和构造过程
@ -451,7 +451,7 @@ Swift 中类的构造过程包含两个阶段。第一个阶段,类中的每
> 注意
>
> Swift 的两段式构造过程跟 Objective-C 中的构造过程类似。最主要的区别在于阶段 1Objective-C 给每一个属性赋值 `0` 或空值(比如说`0``nil`。Swift 的构造流程则更加灵活,它允许你设置定制的初始值,并自如应对某些属性不能以 `0` 或 `nil` 作为合法默认值的情况。
> Swift 的两段式构造过程跟 Objective-C 中的构造过程类似。最主要的区别在于阶段 1Objective-C 给每一个属性赋值 `0` 或空值(比如说 `0``nil`。Swift 的构造流程则更加灵活,它允许你设置定制的初始值,并自如应对某些属性不能以 `0` 或 `nil` 作为合法默认值的情况。
Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程不出错地完成:
@ -471,7 +471,7 @@ Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造
##### 安全检查 4
&nbsp;&nbsp;&nbsp;&nbsp;构造器在第一阶段构造完成之前,不能调用任何实例方法,不能读取任何实例属性的值,不能引用`self`作为一个值。
&nbsp;&nbsp;&nbsp;&nbsp;构造器在第一阶段构造完成之前,不能调用任何实例方法,不能读取任何实例属性的值,不能引用 `self` 作为一个值。
类实例在第一阶段结束以前并不是完全有效的。只有第一阶段完成后,该实例才会成为有效实例,才能访问属性和调用方法。
@ -530,7 +530,7 @@ Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造
> 注意
>
> 当你重写一个父类的指定构造器时,你总是需要写`override`修饰符,即使是为了实现子类的便利构造器。
> 当你重写一个父类的指定构造器时,你总是需要写 `override` 修饰符,即使是为了实现子类的便利构造器。
相反,如果你编写了一个和父类便利构造器相匹配的子类构造器,由于子类不能直接调用父类的便利构造器(每个规则都在上文[类的构造器代理规则](#initializer_delegation_for_class_types)有所描述),因此,严格意义上来讲,你的子类并未对一个父类构造器提供重写。最后的结果就是,你在子类中“重写”一个父类便利构造器时,不需要加 `override` 修饰符。
@ -545,7 +545,7 @@ class Vehicle {
}
```
`Vehicle` 类只为存储型属性提供默认值,也没有提供自定义构造器。因此,它会自动获得一个默认构造器,具体内容请参考[默认构造器](#default_initializers)。自动获得的默认构造器总是类中的指定构造器,它可以用于创建`numberOfWheels``0``Vehicle`
`Vehicle` 类只为存储型属性提供默认值,也没有提供自定义构造器。因此,它会自动获得一个默认构造器,具体内容请参考[默认构造器](#default_initializers)。自动获得的默认构造器总是类中的指定构造器,它可以用于创建 `numberOfWheels``0``Vehicle`
实例:
```swift
@ -567,7 +567,7 @@ class Bicycle: Vehicle {
子类 `Bicycle` 定义了一个自定义指定构造器 `init()`。这个指定构造器和父类的指定构造器相匹配,所以 `Bicycle` 中的指定构造器需要带上 `override` 修饰符。
`Bicycle` 的构造器 `init()` 以调用 `super.init()` 方法开始,这个方法的作用是调用 `Bicycle` 的父类`Vehicle` 的默认构造器。这样可以确保 `Bicycle` 在修改属性之前,它所继承的属性 `numberOfWheels` 能被 `Vehicle` 类初始化。在调用 `super.init()` 之后,属性 `numberOfWheels` 的原值被新值 `2` 替换。
`Bicycle` 的构造器 `init()` 以调用 `super.init()` 方法开始,这个方法的作用是调用 `Bicycle` 的父类 `Vehicle` 的默认构造器。这样可以确保 `Bicycle` 在修改属性之前,它所继承的属性 `numberOfWheels` 能被 `Vehicle` 类初始化。在调用 `super.init()` 之后,属性 `numberOfWheels` 的原值被新值 `2` 替换。
如果你创建一个 `Bicycle` 实例,你可以调用继承的 `description` 计算型属性去查看属性 `numberOfWheels` 是否有改变:
@ -607,7 +607,7 @@ print("Bicycle: \(bicycle.description)")
接下来的例子将在实践中展示指定构造器、便利构造器以及构造器的自动继承。这个例子定义了包含三个类 `Food``RecipeIngredient` 以及 `ShoppingListItem` 的类层次结构,并将演示它们的构造器是如何相互作用的。
类层次中的基类是 `Food`,它是一个简单的用来封装食物名字的类。`Food` 类引入了一个叫做 `name``String` 类型的属性,并且提供了两个构造器来创建`Food`实例:
类层次中的基类是 `Food`,它是一个简单的用来封装食物名字的类。`Food` 类引入了一个叫做 `name``String` 类型的属性,并且提供了两个构造器来创建 `Food` 实例:
```swift
class Food {
@ -624,7 +624,7 @@ class Food {
下图中展示了 `Food` 的构造器链:
![Food构造器链](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/initializersExample01_2x.png)
![Food 构造器链](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/initializersExample01_2x.png)
类类型没有默认的逐一成员构造器,所以 `Food` 类提供了一个接受单一参数 `name` 的指定构造器。这个构造器可以使用一个特定的名字来创建新的 `Food` 实例:
@ -642,7 +642,7 @@ let mysteryMeat = Food()
// mysteryMeat 的名字是 [Unnamed]
```
类层级中的第二个类是 `Food` 的子类 `RecipeIngredient``RecipeIngredient` 类用来表示食谱中的一项原料。它引入了 `Int` 类型的属性 `quantity`(以及从 `Food` 继承过来的 `name` 属性),并且定义了两个构造器来创建`RecipeIngredient` 实例:
类层级中的第二个类是 `Food` 的子类 `RecipeIngredient``RecipeIngredient` 类用来表示食谱中的一项原料。它引入了 `Int` 类型的属性 `quantity`(以及从 `Food` 继承过来的 `name` 属性),并且定义了两个构造器来创建 `RecipeIngredient` 实例:
```swift
class RecipeIngredient: Food {
@ -659,19 +659,19 @@ class RecipeIngredient: Food {
下图中展示了 `RecipeIngredient` 类的构造器链:
![RecipeIngredient构造器](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/initializersExample02_2x.png)
![RecipeIngredient 构造器](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/initializersExample02_2x.png)
`RecipeIngredient` 类拥有一个指定构造器 `init(name: String, quantity: Int)`,它可以用来填充`RecipeIngredient` 实例的所有属性值。这个构造器一开始先将传入的 `quantity` 参数赋值给 `quantity` 属性,这个属性也是唯一在 `RecipeIngredient` 中新引入的属性。随后,构造器向上代理到父类 `Food``init(name: String)`。这个过程满足[两段式构造过程](#two_phase_initialization)中的安全检查 1。
`RecipeIngredient` 类拥有一个指定构造器 `init(name: String, quantity: Int)`,它可以用来填充 `RecipeIngredient` 实例的所有属性值。这个构造器一开始先将传入的 `quantity` 参数赋值给 `quantity` 属性,这个属性也是唯一在 `RecipeIngredient` 中新引入的属性。随后,构造器向上代理到父类 `Food` `init(name: String)`。这个过程满足[两段式构造过程](#two_phase_initialization)中的安全检查 1。
`RecipeIngredient` 也定义了一个便利构造器 `init(name: String)`,它只通过 `name` 来创建 `RecipeIngredient` 的实例。这个便利构造器假设任意 `RecipeIngredient` 实例的 `quantity``1`,所以不需要显式指明数量即可创建出实例。这个便利构造器的定义可以更加方便和快捷地创建实例,并且避免了创建多个`quantity``1``RecipeIngredient` 实例时的代码重复。这个便利构造器只是简单地横向代理到类中的指定构造器,并为 `quantity` 参数传递 `1`
`RecipeIngredient` 也定义了一个便利构造器 `init(name: String)`,它只通过 `name` 来创建 `RecipeIngredient` 的实例。这个便利构造器假设任意 `RecipeIngredient` 实例的 `quantity``1`,所以不需要显式指明数量即可创建出实例。这个便利构造器的定义可以更加方便和快捷地创建实例,并且避免了创建多个 `quantity``1``RecipeIngredient` 实例时的代码重复。这个便利构造器只是简单地横向代理到类中的指定构造器,并为 `quantity` 参数传递 `1`
注意,`RecipeIngredient` 的便利构造器 `init(name: String)` 使用了跟 `Food` 中指定构造器 `init(name: String)` 相同的参数。由于这个便利构造器重写了父类的指定构造器 `init(name: String)`,因此必须在前面使用 `override` 修饰符(参见[构造器的继承和重写](#initializer_inheritance_and_overriding))。
尽管 `RecipeIngredient` 将父类的指定构造器重写为了便利构造器,但是它依然提供了父类的所有指定构造器的实现。因此,`RecipeIngredient` 会自动继承父类的所有便利构造器。
在这个例子中,`RecipeIngredient` 的父类是 `Food`,它有一个便利构造器 `init()`。这个便利构造器会被`RecipeIngredient` 继承。这个继承版本的 `init()` 在功能上跟 `Food` 提供的版本是一样的,只是它会代理到`RecipeIngredient` 版本的 `init(name: String)` 而不是 `Food` 提供的版本。
在这个例子中,`RecipeIngredient` 的父类是 `Food`,它有一个便利构造器 `init()`。这个便利构造器会被 `RecipeIngredient` 继承。这个继承版本的 `init()` 在功能上跟 `Food` 提供的版本是一样的,只是它会代理到 `RecipeIngredient` 版本的 `init(name: String)` 而不是 `Food` 提供的版本。
所有的这三种构造器都可以用来创建新的 `RecipeIngredient `实例:
所有的这三种构造器都可以用来创建新的 `RecipeIngredient` 实例:
```swift
let oneMysteryItem = RecipeIngredient()
@ -739,7 +739,7 @@ for item in breakfastList {
> 注意
>
> 严格来说,构造器都不支持返回值。因为构造器本身的作用,只是为了确保对象能被正确构造。因此你只是用`return nil`表明可失败构造器构造失败,而不要用关键字`return`来表明构造成功。
> 严格来说,构造器都不支持返回值。因为构造器本身的作用,只是为了确保对象能被正确构造。因此你只是用 `return nil` 表明可失败构造器构造失败,而不要用关键字 `return` 来表明构造成功。
例如,实现针对数字类型转换的可失败构造器。确保数字类型之间的转换能保持精确的值,使用这个 `init(exactly:)` 构造器。如果类型转换不能保持值不变,则这个构造器构造失败。
@ -761,7 +761,7 @@ if valueChanged == nil {
// 打印 "3.14159 conversion to Int does not maintain value"
```
下例中,定义了一个名为 `Animal` 的结构体,其中有一个名为 `species``String` 类型的常量属性。同时该结构体还定义了一个接受一个名为 `species``String` 类型参数的可失败构造器。这个可失败构造器检查传入的参数是否为一个空字符串。如果为空字符串,则构造失败。否则,`species`属性被赋值,构造成功。
下例中,定义了一个名为 `Animal` 的结构体,其中有一个名为 `species``String` 类型的常量属性。同时该结构体还定义了一个接受一个名为 `species``String` 类型参数的可失败构造器。这个可失败构造器检查传入的参数是否为一个空字符串。如果为空字符串,则构造失败。否则,`species` 属性被赋值,构造成功。
```swift
struct Animal {
@ -808,7 +808,7 @@ if anonymousCreature == nil {
你可以通过一个带一个或多个参数的可失败构造器来获取枚举类型中特定的枚举成员。如果提供的参数无法匹配任何枚举成员,则构造失败。
下例中,定义了一个名为 `TemperatureUnit` 的枚举类型。其中包含了三个可能的枚举成员(`Kelvin``Celsius`,和`Fahrenheit`),以及一个根据 `Character` 值找出所对应的枚举成员的可失败构造器:
下例中,定义了一个名为 `TemperatureUnit` 的枚举类型。其中包含了三个可能的枚举成员(`Kelvin``Celsius`,和 `Fahrenheit`),以及一个根据 `Character` 值找出所对应的枚举成员的可失败构造器:
```swift
enum TemperatureUnit {
@ -880,7 +880,7 @@ if unknownUnit == nil {
>
> 可失败构造器也可以代理到其它的非可失败构造器。通过这种方式,你可以增加一个可能的失败状态到现有的构造过程中。
下面这个例子,定义了一个名为`CartItem``Product`类的子类。这个类建立了一个在线购物车中的物品的模型,它有一个名为`quantity`的常量存储型属性,并确保该属性的值至少为`1`
下面这个例子,定义了一个名为 `CartItem``Product` 类的子类。这个类建立了一个在线购物车中的物品的模型,它有一个名为 `quantity` 的常量存储型属性,并确保该属性的值至少为 `1`
```swift
class Product {
@ -960,7 +960,7 @@ class Document {
}
```
下面这个例子,定义了一个 `Document` 类的子类 `AutomaticallyNamedDocument`。这个子类重写了父类的两个指定构造器,确保了无论是使用 `init()` 构造器,还是使用 `init(name:)` 构造器并为参数传递空字符串,生成的实例中的 `name` 属性总有初始`"[Untitled]"`
下面这个例子,定义了一个 `Document` 类的子类 `AutomaticallyNamedDocument`。这个子类重写了父类的两个指定构造器,确保了无论是使用 `init()` 构造器,还是使用 `init(name:)` 构造器并为参数传递空字符串,生成的实例中的 `name` 属性总有初始 `"[Untitled]"`
```swift
class AutomaticallyNamedDocument: Document {
@ -996,9 +996,9 @@ class UntitledDocument: Document {
<a name="the_init!_failable_initializer"></a>
### init!可失败构造器
通常来说我们通过在`init`关键字后添加问号的方式(`init?`)来定义一个可失败构造器,但你也可以通过在`init`后面添加惊叹号的方式来定义一个可失败构造器(`init!`),该可失败构造器将会构建一个对应类型的隐式解包可选类型的对象。
通常来说我们通过在 `init` 关键字后添加问号的方式(`init?`)来定义一个可失败构造器,但你也可以通过在 `init` 后面添加惊叹号的方式来定义一个可失败构造器(`init!`),该可失败构造器将会构建一个对应类型的隐式解包可选类型的对象。
你可以在 `init?` 中代理到 `init!`,反之亦然。你也可以用 `init?` 重写 `init!`,反之亦然。你还可以用`init` 代理到 `init!`,不过,一旦 `init!` 构造失败,则会触发一个断言。
你可以在 `init?` 中代理到 `init!`,反之亦然。你也可以用 `init?` 重写 `init!`,反之亦然。你还可以用 `init` 代理到 `init!`,不过,一旦 `init!` 构造失败,则会触发一个断言。
<a name="required_initializers"></a>
## 必要构造器
@ -1056,7 +1056,7 @@ class SomeClass {
![西洋跳棋棋盘](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/chessBoard_2x.png)
为了呈现这副游戏棋盘,`Chessboard `结构体定义了一个属性 `boardColors`,它是一个包含 `64``Bool`值的数组。在数组中,值为 `true` 的元素表示一个黑格,值为 `false` 的元素表示一个白格。数组中第一个元素代表棋盘上左上角的格子,最后一个元素代表棋盘上右下角的格子。
为了呈现这副游戏棋盘,`Chessboard` 结构体定义了一个属性 `boardColors`,它是一个包含 `64``Bool` 值的数组。在数组中,值为 `true` 的元素表示一个黑格,值为 `false` 的元素表示一个白格。数组中第一个元素代表棋盘上左上角的格子,最后一个元素代表棋盘上右下角的格子。
`boardColors` 数组是通过一个闭包来初始化并设置颜色值的:
@ -1080,7 +1080,7 @@ struct Chessboard {
}
```
每当一个新的 `Chessboard` 实例被创建时,赋值闭包则会被执行,`boardColors` 的默认值会被计算出来并返回。上面例子中描述的闭包将计算出棋盘中每个格子对应的颜色,并将这些值保存到一个临时数组 `temporaryBoard` 中,最后在构建完成时将此数组作为闭包返回值返回。这个返回的数组会保存到 `boardColors` 中,并可以通过工具函数`squareIsBlackAtRow`来查询:
每当一个新的 `Chessboard` 实例被创建时,赋值闭包则会被执行,`boardColors` 的默认值会被计算出来并返回。上面例子中描述的闭包将计算出棋盘中每个格子对应的颜色,并将这些值保存到一个临时数组 `temporaryBoard` 中,最后在构建完成时将此数组作为闭包返回值返回。这个返回的数组会保存到 `boardColors` 中,并可以通过工具函数 `squareIsBlackAtRow` 来查询:
```swift
let board = Chessboard()

View File

@ -10,9 +10,10 @@
> 2.1
> 校对:[shanks](http://codebuild.me)2015-10-31
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-14
> 3.0.1shanks2016-11-13
> 4.0
@ -26,7 +27,7 @@
- [析构过程原理](#how_deinitialization_works)
- [析构器实践](#deinitializers_in_action)
*析构器*只适用于类类型,当一个类的实例被释放之前,析构器会被立即调用。析构器用关键字`deinit`来标示,类似于构造器要用`init`来标示。
*析构器*只适用于类类型,当一个类的实例被释放之前,析构器会被立即调用。析构器用关键字 `deinit` 来标示,类似于构造器要用 `init` 来标示。
<a name="how_deinitialization_works"></a>
## 析构过程原理
@ -48,7 +49,7 @@ deinit {
<a name="deinitializers_in_action"></a>
## 析构器实践
这是一个析构器实践的例子。这个例子描述了一个简单的游戏,这里定义了两种新类型,分别是`Bank``Player``Bank`类管理一种虚拟硬币,确保流通的硬币数量永远不可能超过 10,000。在游戏中有且只能有一个`Bank`存在,因此`Bank`用类来实现,并使用类型属性和类型方法来存储和管理其当前状态。
这是一个析构器实践的例子。这个例子描述了一个简单的游戏,这里定义了两种新类型,分别是 `Bank``Player``Bank` 类管理一种虚拟硬币,确保流通的硬币数量永远不可能超过 10,000。在游戏中有且只能有一个 `Bank` 存在,因此 `Bank` 用类来实现,并使用类型属性和类型方法来存储和管理其当前状态。
```swift
class Bank {
@ -64,13 +65,13 @@ class Bank {
}
```
`Bank`使用`coinsInBank`属性来跟踪它当前拥有的硬币数量。`Bank`还提供了两个方法,`distribute(coins:)``receive(coins:)`,分别用来处理硬币的分发和收集。
`Bank` 使用 `coinsInBank` 属性来跟踪它当前拥有的硬币数量。`Bank` 还提供了两个方法,`distribute(coins:)``receive(coins:)`,分别用来处理硬币的分发和收集。
`distribute(coins:)`方法在`Bank`对象分发硬币之前检查是否有足够的硬币。如果硬币不足,`Bank`对象会返回一个比请求时小的数字(如果`Bank`对象中没有硬币了就返回`0`)。此方法返回一个整型值,表示提供的硬币的实际数量。
`distribute(coins:)` 方法在 `Bank` 对象分发硬币之前检查是否有足够的硬币。如果硬币不足,`Bank` 对象会返回一个比请求时小的数字(如果 `Bank` 对象中没有硬币了就返回 `0`)。此方法返回一个整型值,表示提供的硬币的实际数量。
`receive(coins:)`方法只是将`Bank`实例接收到的硬币数目加回硬币存储中。
`receive(coins:)` 方法只是将 `Bank` 实例接收到的硬币数目加回硬币存储中。
`Player`类描述了游戏中的一个玩家。每一个玩家在任意时间都有一定数量的硬币存储在他们的钱包中。这通过玩家的`coinsInPurse`属性来表示:
`Player` 类描述了游戏中的一个玩家。每一个玩家在任意时间都有一定数量的硬币存储在他们的钱包中。这通过玩家的 `coinsInPurse` 属性来表示:
```swift
class Player {
@ -87,9 +88,9 @@ class Player {
}
```
每个`Player`实例在初始化的过程中,都从`Bank`对象获取指定数量的硬币。如果没有足够的硬币可用,`Player`实例可能会收到比指定数量少的硬币。
每个 `Player` 实例在初始化的过程中,都从 `Bank` 对象获取指定数量的硬币。如果没有足够的硬币可用,`Player` 实例可能会收到比指定数量少的硬币。
`Player`类定义了一个`win(coins:)`方法,该方法从`Bank`对象获取一定数量的硬币,并把它们添加到玩家的钱包。`Player`类还实现了一个析构器,这个析构器在`Player`实例释放前被调用。在这里,析构器的作用只是将玩家的所有硬币都返还给`Bank`对象:
`Player` 类定义了一个 `win(coins:)` 方法,该方法从 `Bank` 对象获取一定数量的硬币,并把它们添加到玩家的钱包。`Player` 类还实现了一个析构器,这个析构器在 `Player` 实例释放前被调用。在这里,析构器的作用只是将玩家的所有硬币都返还给 `Bank` 对象:
```swift
var playerOne: Player? = Player(coins: 100)
@ -99,9 +100,9 @@ print("There are now \(Bank.coinsInBank) coins left in the bank")
// 打印 "There are now 9900 coins left in the bank"
```
创建一个`Player`实例的时候,会向`Bank`对象请求 100 个硬币,如果有足够的硬币可用的话。这个`Player`实例存储在一个名为`playerOne`的可选类型的变量中。这里使用了一个可选类型的变量,因为玩家可以随时离开游戏,设置为可选使你可以追踪玩家当前是否在游戏中。
创建一个 `Player` 实例的时候,会向 `Bank` 对象请求 100 个硬币,如果有足够的硬币可用的话。这个 `Player` 实例存储在一个名为 `playerOne` 的可选类型的变量中。这里使用了一个可选类型的变量,因为玩家可以随时离开游戏,设置为可选使你可以追踪玩家当前是否在游戏中。
因为`playerOne`是可选的,所以访问其`coinsInPurse`属性来打印钱包中的硬币数量时,使用感叹号(`!`)强制解包:
因为 `playerOne` 是可选的,所以访问其 `coinsInPurse` 属性来打印钱包中的硬币数量时,使用感叹号(`!`)强制解包:
```swift
playerOne!.win(coins: 2_000)
@ -111,7 +112,7 @@ print("The bank now only has \(Bank.coinsInBank) coins left")
// 输出 "The bank now only has 7900 coins left"
```
这里,玩家已经赢得了 2,000 枚硬币,所以玩家的钱包中现在有 2,100 枚硬币,而`Bank`对象只剩余 7,900 枚硬币。
这里,玩家已经赢得了 2,000 枚硬币,所以玩家的钱包中现在有 2,100 枚硬币,而 `Bank` 对象只剩余 7,900 枚硬币。
```swift
playerOne = nil
@ -121,4 +122,4 @@ print("The bank now has \(Bank.coinsInBank) coins")
// 打印 "The bank now has 10000 coins"
```
玩家现在已经离开了游戏。这通过将可选类型的`playerOne`变量设置为`nil`来表示,意味着“没有`Player`实例”。当这一切发生时,`playerOne`变量对`Player`实例的引用被破坏了。没有其它属性或者变量引用`Player`实例,因此该实例会被释放,以便回收内存。在这之前,该实例的析构器被自动调用,玩家的硬币被返还给银行。
玩家现在已经离开了游戏。这通过将可选类型的 `playerOne` 变量设置为 `nil` 来表示,意味着“没有 `Player` 实例”。当这一切发生时,`playerOne` 变量对 `Player` 实例的引用被破坏了。没有其它属性或者变量引用 `Player` 实例,因此该实例会被释放,以便回收内存。在这之前,该实例的析构器被自动调用,玩家的硬币被返还给银行。

View File

@ -11,16 +11,17 @@
> 2.1
> 校对:[shanks](http://codebuild.me)2015-10-31
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-15
> 3.0.1shanks2016-11-13
> 4.0
> 校对:[kemchenj](https://kemchenj.github.io/) 2017-09-21
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
本页包含内容:
@ -32,24 +33,24 @@
- [连接多层可选链式调用](#linking_multiple_levels_of_chaining)
- [在方法的可选返回值上进行可选链式调用](#chaining_on_methods_with_optional_return_values)
*可选链式调用*是一种可以在当前值可能为`nil`的可选值上请求和调用属性、方法及下标的方法。如果可选值有值,那么调用就会成功;如果可选值是`nil`,那么调用将返回`nil`。多个调用可以连接在一起形成一个调用链,如果其中任何一个节点为`nil`,整个调用链都会失败,即返回`nil`
*可选链式调用*是一种可以在当前值可能为 `nil` 的可选值上请求和调用属性、方法及下标的方法。如果可选值有值,那么调用就会成功;如果可选值是 `nil`,那么调用将返回 `nil`。多个调用可以连接在一起形成一个调用链,如果其中任何一个节点为 `nil`,整个调用链都会失败,即返回 `nil`
> 注意
>
> Swift 的可选链式调用和 Objective-C 中向`nil`发送消息有些相像,但是 Swift 的可选链式调用可以应用于任意类型,并且能检查调用是否成功。
> 注意
>
> Swift 的可选链式调用和 Objective-C 中向 `nil` 发送消息有些相像,但是 Swift 的可选链式调用可以应用于任意类型,并且能检查调用是否成功。
<a name="optional_chaining_as_an_alternative_to_forced_unwrapping"></a>
## 使用可选链式调用代替强制展开
通过在想调用的属性、方法或下标的可选值后面放一个问号(`?`),可以定义一个可选链。这一点很像在可选值后面放一个叹号(`!`)来强制展开它的值。它们的主要区别在于当可选值为空时可选链式调用只会调用失败,然而强制展开将会触发运行时错误。
通过在想调用的属性、方法或下标的可选值后面放一个问号(`?`),可以定义一个可选链。这一点很像在可选值后面放一个叹号(`!`)来强制展开它的值。它们的主要区别在于当可选值为空时可选链式调用只会调用失败,然而强制展开将会触发运行时错误。
为了反映可选链式调用可以在空值(`nil`)上调用的事实,不论这个调用的属性、方法及下标返回的值是不是可选值,它的返回结果都是一个可选值。你可以利用这个返回值来判断你的可选链式调用是否调用成功,如果调用有返回值则说明调用成功,返回`nil`则说明调用失败。
为了反映可选链式调用可以在空值(`nil`)上调用的事实,不论这个调用的属性、方法及下标返回的值是不是可选值,它的返回结果都是一个可选值。你可以利用这个返回值来判断你的可选链式调用是否调用成功,如果调用有返回值则说明调用成功,返回 `nil` 则说明调用失败。
特别地,可选链式调用的返回结果与原本的返回结果具有相同的类型,但是被包装成了一个可选值。例如,使用可选链式调用访问属性,当可选链式调用成功时,如果属性原本的返回结果是`Int`类型,则会变为`Int?`类型。
特别地,可选链式调用的返回结果与原本的返回结果具有相同的类型,但是被包装成了一个可选值。例如,使用可选链式调用访问属性,当可选链式调用成功时,如果属性原本的返回结果是 `Int` 类型,则会变为 `Int?` 类型。
下面几段代码将解释可选链式调用和强制展开的不同。
首先定义两个类`Person``Residence`
首先定义两个类 `Person``Residence`
```swift
class Person {
@ -61,24 +62,24 @@ class Residence {
}
```
`Residence`有一个`Int`类型的属性`numberOfRooms`,其默认值为`1``Person`具有一个可选的`residence`属性,其类型为`Residence?`
`Residence` 有一个 `Int` 类型的属性 `numberOfRooms`,其默认值为 `1``Person` 具有一个可选的 `residence` 属性,其类型为 `Residence?`
假如你创建了一个新的`Person`实例,它的`residence`属性由于是是可选型而将初始化为`nil`,在下面的代码中,`john`有一个值为`nil``residence`属性:
假如你创建了一个新的 `Person` 实例,它的 `residence` 属性由于是是可选型而将初始化为 `nil`,在下面的代码中,`john` 有一个值为 `nil``residence` 属性:
```swift
let john = Person()
```
如果使用叹号(`!`)强制展开获得这个`john``residence`属性中的`numberOfRooms`值,会触发运行时错误,因为这时`residence`没有可以展开的值:
如果使用叹号(`!`)强制展开获得这个 `john``residence` 属性中的 `numberOfRooms` 值,会触发运行时错误,因为这时 `residence` 没有可以展开的值:
```swift
let roomCount = john.residence!.numberOfRooms
// 这会引发运行时错误
```
`john.residence`为非`nil`值的时候,上面的调用会成功,并且把`roomCount`设置为`Int`类型的房间数量。正如上面提到的,当`residence``nil`的时候上面这段代码会触发运行时错误。
`john.residence` 为非 `nil` 值的时候,上面的调用会成功,并且把 `roomCount` 设置为 `Int` 类型的房间数量。正如上面提到的,当 `residence``nil` 的时候上面这段代码会触发运行时错误。
可选链式调用提供了另一种访问`numberOfRooms`的方式,使用问号(`?`)来替代原来的叹号(`!`
可选链式调用提供了另一种访问 `numberOfRooms` 的方式,使用问号(`?`)来替代原来的叹号(`!`
```swift
if let roomCount = john.residence?.numberOfRooms {
@ -89,19 +90,19 @@ if let roomCount = john.residence?.numberOfRooms {
// 打印 “Unable to retrieve the number of rooms.”
```
`residence`后面添加问号之后Swift 就会在`residence`不为`nil`的情况下访问`numberOfRooms`
`residence` 后面添加问号之后Swift 就会在 `residence` 不为 `nil` 的情况下访问 `numberOfRooms`
因为访问`numberOfRooms`有可能失败,可选链式调用会返回`Int?`类型,或称为“可选的 `Int`”。如上例所示,当`residence``nil`的时候,可选的`Int`将会为`nil`,表明无法访问`numberOfRooms`。访问成功时,可选的`Int`值会通过可选绑定展开,并赋值给非可选类型的`roomCount`常量。
因为访问 `numberOfRooms` 有可能失败,可选链式调用会返回 `Int?` 类型,或称为“可选的 `Int`”。如上例所示,当 `residence``nil` 的时候,可选的 `Int` 将会为 `nil`,表明无法访问 `numberOfRooms`。访问成功时,可选的 `Int` 值会通过可选绑定展开,并赋值给非可选类型的 `roomCount` 常量。
要注意的是,即使`numberOfRooms`是非可选的`Int`时,这一点也成立。只要使用可选链式调用就意味着`numberOfRooms`会返回一个`Int?`而不是`Int`
要注意的是,即使 `numberOfRooms` 是非可选的 `Int` 时,这一点也成立。只要使用可选链式调用就意味着 `numberOfRooms` 会返回一个 `Int?` 而不是 `Int`
可以将一个`Residence`的实例赋给`john.residence`,这样它就不再是`nil`了:
可以将一个 `Residence` 的实例赋给 `john.residence`,这样它就不再是 `nil` 了:
```swift
john.residence = Residence()
```
`john.residence`现在包含一个实际的`Residence`实例,而不再是`nil`。如果你试图使用先前的可选链式调用访问`numberOfRooms`,它现在将返回值为`1``Int?`类型的值:
`john.residence` 现在包含一个实际的 `Residence` 实例,而不再是 `nil`。如果你试图使用先前的可选链式调用访问 `numberOfRooms`,它现在将返回值为 `1``Int?` 类型的值:
```swift
if let roomCount = john.residence?.numberOfRooms {
@ -117,9 +118,9 @@ if let roomCount = john.residence?.numberOfRooms {
通过使用可选链式调用可以调用多层属性、方法和下标。这样可以在复杂的模型中向下访问各种子属性,并且判断能否访问子属性的属性、方法或下标。
下面这段代码定义了四个模型类,这些例子包括多层可选链式调用。为了方便说明,在`Person``Residence`的基础上增加了`Room`类和`Address`类,以及相关的属性、方法以及下标。
下面这段代码定义了四个模型类,这些例子包括多层可选链式调用。为了方便说明,在 `Person``Residence` 的基础上增加了 `Room` 类和 `Address` 类,以及相关的属性、方法以及下标。
`Person`类的定义基本保持不变:
`Person` 类的定义基本保持不变:
```swift
class Person {
@ -127,7 +128,7 @@ class Person {
}
```
`Residence`类比之前复杂些,增加了一个名为`rooms`的变量属性,该属性被初始化为`[Room]`类型的空数组:
`Residence` 类比之前复杂些,增加了一个名为 `rooms` 的变量属性,该属性被初始化为 `[Room]` 类型的空数组:
```swift
class Residence {
@ -150,15 +151,15 @@ class Residence {
}
```
现在`Residence`有了一个存储`Room`实例的数组,`numberOfRooms`属性被实现为计算型属性,而不是存储型属性。`numberOfRooms`属性简单地返回`rooms`数组的`count`属性的值。
现在 `Residence` 有了一个存储 `Room` 实例的数组,`numberOfRooms` 属性被实现为计算型属性,而不是存储型属性。`numberOfRooms` 属性简单地返回 `rooms` 数组的 `count` 属性的值。
`Residence`还提供了访问`rooms`数组的快捷方式,即提供可读写的下标来访问`rooms`数组中指定位置的元素。
`Residence` 还提供了访问 `rooms` 数组的快捷方式,即提供可读写的下标来访问 `rooms` 数组中指定位置的元素。
此外,`Residence`还提供了`printNumberOfRooms`方法,这个方法的作用是打印`numberOfRooms`的值。
此外,`Residence` 还提供了 `printNumberOfRooms` 方法,这个方法的作用是打印 `numberOfRooms` 的值。
最后,`Residence`还定义了一个可选属性`address`,其类型为`Address?``Address`类的定义在下面会说明。
最后,`Residence` 还定义了一个可选属性 `address`,其类型为 `Address?``Address` 类的定义在下面会说明。
`Room`类是一个简单类,其实例被存储在`rooms`数组中。该类只包含一个属性`name`,以及一个用于将该属性设置为适当的房间名的初始化函数:
`Room` 类是一个简单类,其实例被存储在 `rooms` 数组中。该类只包含一个属性 `name`,以及一个用于将该属性设置为适当的房间名的初始化函数:
```swift
class Room {
@ -167,7 +168,7 @@ class Room {
}
```
最后一个类是`Address`,这个类有三个`String?`类型的可选属性。`buildingName`以及`buildingNumber`属性分别表示某个大厦的名称和号码,第三个属性`street`表示大厦所在街道的名称:
最后一个类是 `Address`,这个类有三个 `String?` 类型的可选属性。`buildingName` 以及 `buildingNumber` 属性分别表示某个大厦的名称和号码,第三个属性 `street` 表示大厦所在街道的名称:
```swift
class Address {
@ -186,14 +187,14 @@ class Address {
}
```
`Address`类提供了`buildingIdentifier()`方法,返回值为`String?`。 如果`buildingName`有值则返回`buildingName`。或者,如果`buildingNumber``street`均有值则返回`buildingNumber`。否则,返回`nil`
`Address` 类提供了 `buildingIdentifier()` 方法,返回值为 `String?`。 如果 `buildingName` 有值则返回 `buildingName`。或者,如果 `buildingNumber``street` 均有值则返回 `buildingNumber`。否则,返回 `nil`
<a name="accessing_properties_through_optional_chaining"></a>
## 通过可选链式调用访问属性
正如[使用可选链式调用代替强制展开](#optional_chaining_as_an_alternative_to_forced_unwrapping)中所述,可以通过可选链式调用在一个可选值上访问它的属性,并判断访问是否成功。
下面的代码创建了一个`Person`实例,然后像之前一样,尝试访问`numberOfRooms`属性:
下面的代码创建了一个 `Person` 实例,然后像之前一样,尝试访问 `numberOfRooms` 属性:
```swift
let john = Person()
@ -205,7 +206,7 @@ if let roomCount = john.residence?.numberOfRooms {
// 打印 “Unable to retrieve the number of rooms.”
```
因为`john.residence``nil`,所以这个可选链式调用依旧会像先前一样失败。
因为 `john.residence``nil`,所以这个可选链式调用依旧会像先前一样失败。
还可以通过可选链式调用来设置属性值:
@ -216,9 +217,9 @@ someAddress.street = "Acacia Road"
john.residence?.address = someAddress
```
在这个例子中,通过`john.residence`来设定`address`属性也会失败,因为`john.residence`当前为`nil`
在这个例子中,通过 `john.residence` 来设定 `address` 属性也会失败,因为 `john.residence` 当前为 `nil`
上面代码中的赋值过程是可选链式调用的一部分,这意味着可选链式调用失败时,等号右侧的代码不会被执行。对于上面的代码来说,很难验证这一点,因为像这样赋值一个常量没有任何副作用。下面的代码完成了同样的事情,但是它使用一个函数来创建`Address`实例然后将该实例返回用于赋值。该函数会在返回前打印“Function was called”这使你能验证等号右侧的代码是否被执行。
上面代码中的赋值过程是可选链式调用的一部分,这意味着可选链式调用失败时,等号右侧的代码不会被执行。对于上面的代码来说,很难验证这一点,因为像这样赋值一个常量没有任何副作用。下面的代码完成了同样的事情,但是它使用一个函数来创建 `Address` 实例然后将该实例返回用于赋值。该函数会在返回前打印“Function was called”这使你能验证等号右侧的代码是否被执行。
```swift
func createAddress() -> Address {
@ -233,14 +234,14 @@ func createAddress() -> Address {
john.residence?.address = createAddress()
```
没有任何打印消息,可以看出`createAddress()`函数并未被执行。
没有任何打印消息,可以看出 `createAddress()` 函数并未被执行。
<a name="calling_methods_through_optional_chaining"></a>
## 通过可选链式调用调用方法
## 通过可选链式调用调用方法
可以通过可选链式调用来调用方法,并判断是否调用成功,即使这个方法没有返回值。
`Residence`类中的`printNumberOfRooms()`方法打印当前的`numberOfRooms`值,如下所示:
`Residence` 类中的 `printNumberOfRooms()` 方法打印当前的 `numberOfRooms` 值,如下所示:
```swift
func printNumberOfRooms() {
@ -248,9 +249,9 @@ func printNumberOfRooms() {
}
```
这个方法没有返回值。然而,没有返回值的方法具有隐式的返回类型`Void`,如[无返回值函数](./06_Functions.html#functions_without_return_values)中所述。这意味着没有返回值的方法也会返回`()`,或者说空的元组。
这个方法没有返回值。然而,没有返回值的方法具有隐式的返回类型 `Void`,如[无返回值函数](./06_Functions.html#functions_without_return_values)中所述。这意味着没有返回值的方法也会返回 `()`,或者说空的元组。
如果在可选值上通过可选链式调用来调用这个方法,该方法的返回类型会是`Void?`,而不是`Void`,因为通过可选链式调用得到的返回值都是可选的。这样我们就可以使用`if`语句来判断能否成功调用`printNumberOfRooms()`方法,即使方法本身没有定义返回值。通过判断返回值是否为`nil`可以判断调用是否成功:
如果在可选值上通过可选链式调用来调用这个方法,该方法的返回类型会是 `Void?`,而不是 `Void`,因为通过可选链式调用得到的返回值都是可选的。这样我们就可以使用 `if` 语句来判断能否成功调用 `printNumberOfRooms()` 方法,即使方法本身没有定义返回值。通过判断返回值是否为 `nil` 可以判断调用是否成功:
```swift
if john.residence?.printNumberOfRooms() != nil {
@ -261,7 +262,7 @@ if john.residence?.printNumberOfRooms() != nil {
// 打印 “It was not possible to print the number of rooms.”
```
同样的,可以据此判断通过可选链式调用为属性赋值是否成功。在上面的[通过可选链式调用访问属性](#accessing_properties_through_optional_chaining)的例子中,我们尝试给`john.residence`中的`address`属性赋值,即使`residence``nil`。通过可选链式调用给属性赋值会返回`Void?`,通过判断返回值是否为`nil`就可以知道赋值是否成功:
同样的,可以据此判断通过可选链式调用为属性赋值是否成功。在上面的[通过可选链式调用访问属性](#accessing_properties_through_optional_chaining)的例子中,我们尝试给 `john.residence` 中的 `address` 属性赋值,即使 `residence``nil`。通过可选链式调用给属性赋值会返回 `Void?`,通过判断返回值是否为 `nil` 就可以知道赋值是否成功:
```swift
if (john.residence?.address = someAddress) != nil {
@ -277,11 +278,11 @@ if (john.residence?.address = someAddress) != nil {
通过可选链式调用,我们可以在一个可选值上访问下标,并且判断下标调用是否成功。
> 注意
>
> 注意
>
> 通过可选链式调用访问可选值的下标时,应该将问号放在下标方括号的前面而不是后面。可选链式调用的问号一般直接跟在可选表达式的后面。
下面这个例子用下标访问`john.residence`属性存储的`Residence`实例的`rooms`数组中的第一个房间的名称,因为`john.residence``nil`,所以下标调用失败了:
下面这个例子用下标访问 `john.residence` 属性存储的 `Residence` 实例的 `rooms` 数组中的第一个房间的名称,因为 `john.residence``nil`,所以下标调用失败了:
```swift
if let firstRoomName = john.residence?[0].name {
@ -292,7 +293,7 @@ if let firstRoomName = john.residence?[0].name {
// 打印 “Unable to retrieve the first room name.”
```
在这个例子中,问号直接放在`john.residence`的后面,并且在方括号的前面,因为`john.residence`是可选值。
在这个例子中,问号直接放在 `john.residence` 的后面,并且在方括号的前面,因为 `john.residence` 是可选值。
类似的,可以通过下标,用可选链式调用来赋值:
@ -300,9 +301,9 @@ if let firstRoomName = john.residence?[0].name {
john.residence?[0] = Room(name: "Bathroom")
```
这次赋值同样会失败,因为`residence`目前是`nil`
这次赋值同样会失败,因为 `residence` 目前是 `nil`
如果你创建一个`Residence`实例,并为其`rooms`数组添加一些`Room`实例,然后将`Residence`实例赋值给`john.residence`,那就可以通过可选链和下标来访问数组中的元素:
如果你创建一个 `Residence` 实例,并为其 `rooms` 数组添加一些 `Room` 实例,然后将 `Residence` 实例赋值给 `john.residence`,那就可以通过可选链和下标来访问数组中的元素:
```swift
let johnsHouse = Residence()
@ -321,7 +322,7 @@ if let firstRoomName = john.residence?[0].name {
<a name="accessing_subscripts_of_optional_type"></a>
### 访问可选类型的下标
如果下标返回可选类型值,比如 Swift 中`Dictionary`类型的键的下标,可以在下标的结尾括号后面放一个问号来在其可选返回值上进行可选链式调用:
如果下标返回可选类型值,比如 Swift 中 `Dictionary` 类型的键的下标,可以在下标的结尾括号后面放一个问号来在其可选返回值上进行可选链式调用:
```swift
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
@ -331,7 +332,7 @@ testScores["Brian"]?[0] = 72
// "Dave" 数组现在是 [91, 82, 84]"Bev" 数组现在是 [80, 94, 81]
```
上面的例子中定义了一个`testScores`数组,包含了两个键值对,把`String`类型的键映射到一个`Int`值的数组。这个例子用可选链式调用把`"Dave"`数组中第一个元素设为`91`,把`"Bev"`数组的第一个元素`+1`,然后尝试把`"Brian"`数组中的第一个元素设为`72`。前两个调用成功,因为`testScores`字典中包含`"Dave"``"Bev"`这两个键。但是`testScores`字典中没有`"Brian"`这个键,所以第三个调用失败。
上面的例子中定义了一个 `testScores` 数组,包含了两个键值对,把 `String` 类型的键映射到一个 `Int` 值的数组。这个例子用可选链式调用把 `"Dave"` 数组中第一个元素设为 `91`,把 `"Bev"` 数组的第一个元素 `+1`,然后尝试把 `"Brian"` 数组中的第一个元素设为 `72`。前两个调用成功,因为 `testScores` 字典中包含 `"Dave"``"Bev"` 这两个键。但是 `testScores` 字典中没有 `"Brian"` 这个键,所以第三个调用失败。
<a name="linking_multiple_levels_of_chaining"></a>
## 连接多层可选链式调用
@ -345,10 +346,10 @@ testScores["Brian"]?[0] = 72
因此:
+ 通过可选链式调用访问一个`Int`值,将会返回`Int?`,无论使用了多少层可选链式调用。
+ 类似的,通过可选链式调用访问`Int?`值,依旧会返回`Int?`值,并不会返回`Int??`
+ 通过可选链式调用访问一个 `Int` 值,将会返回 `Int?`,无论使用了多少层可选链式调用。
+ 类似的,通过可选链式调用访问 `Int?` 值,依旧会返回 `Int?` 值,并不会返回 `Int??`
下面的例子尝试访问`john`中的`residence`属性中的`address`属性中的`street`属性。这里使用了两层可选链式调用,`residence`以及`address`都是可选值:
下面的例子尝试访问 `john` 中的 `residence` 属性中的 `address` 属性中的 `street` 属性。这里使用了两层可选链式调用,`residence` 以及 `address` 都是可选值:
```swift
if let johnsStreet = john.residence?.address?.street {
@ -359,11 +360,11 @@ if let johnsStreet = john.residence?.address?.street {
// 打印 “Unable to retrieve the address.”
```
`john.residence`现在包含一个有效的`Residence`实例。然而,`john.residence.address`的值当前为`nil`。因此,调用`john.residence?.address?.street`会失败。
`john.residence` 现在包含一个有效的 `Residence` 实例。然而,`john.residence.address` 的值当前为 `nil`。因此,调用 `john.residence?.address?.street` 会失败。
需要注意的是,上面的例子中,`street`的属性为`String?``john.residence?.address?.street`的返回值也依然是`String?`,即使已经使用了两层可选链式调用。
需要注意的是,上面的例子中,`street` 的属性为 `String?``john.residence?.address?.street` 的返回值也依然是 `String?`,即使已经使用了两层可选链式调用。
如果为`john.residence.address`赋值一个`Address`实例,并且为`address`中的`street`属性设置一个有效值,我们就能过通过可选链式调用来访问`street`属性:
如果为 `john.residence.address` 赋值一个 `Address` 实例,并且为 `address` 中的 `street` 属性设置一个有效值,我们就能过通过可选链式调用来访问 `street` 属性:
```swift
let johnsAddress = Address()
@ -379,14 +380,14 @@ if let johnsStreet = john.residence?.address?.street {
// 打印 “John's street name is Laurel Street.”
```
在上面的例子中,因为`john.residence`包含一个有效的`Address`实例,所以对`john.residence``address`属性赋值将会成功。
在上面的例子中,因为 `john.residence` 包含一个有效的 `Address` 实例,所以对 `john.residence``address` 属性赋值将会成功。
<a name="chaining_on_methods_with_optional_return_values"></a>
## 在方法的可选返回值上进行可选链式调用
上面的例子展示了如何在一个可选值上通过可选链式调用来获取它的属性值。我们还可以在一个可选值上通过可选链式调用来调用方法,并且可以根据需要继续在方法的可选返回值上进行可选链式调用。
在下面的例子中,通过可选链式调用来调用`Address``buildingIdentifier()`方法。这个方法返回`String?`类型的值。如上所述,通过可选链式调用来调用该方法,最终的返回值依旧会是`String?`类型:
在下面的例子中,通过可选链式调用来调用 `Address``buildingIdentifier()` 方法。这个方法返回 `String?` 类型的值。如上所述,通过可选链式调用来调用该方法,最终的返回值依旧会是 `String?` 类型:
```swift
if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
@ -409,6 +410,6 @@ if let beginsWithThe =
// 打印 “John's building identifier begins with "The".”
```
> 注意
>
> 在上面的例子中,在方法的圆括号后面加上问号是因为你要在`buildingIdentifier()`方法的可选返回值上进行可选链式调用,而不是`buildingIdentifier()`方法本身。
> 注意
>
> 在上面的例子中,在方法的圆括号后面加上问号是因为你要在 `buildingIdentifier()` 方法的可选返回值上进行可选链式调用,而不是 `buildingIdentifier()` 方法本身。

View File

@ -4,12 +4,13 @@
> 2.1
> 翻译+校对:[lyojo](https://github.com/lyojo) [ray16897188](https://github.com/ray16897188) 2015-10-23
> 校对:[shanks](http://codebuild.me) 2015-10-24
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-15
>
> 3.0
> 翻译+校对:[shanks](http://codebuild.me) 2016-09-24
> 3.0.1shanks2016-11-13
> 4.0
@ -32,12 +33,12 @@
> 注意
>
> Swift 中的错误处理涉及到错误处理模式,这会用到 Cocoa 和 Objective-C 中的`NSError`。关于这个类的更多信息请参见 [Using Swift with Cocoa and Objective-C (Swift 4.1)](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中的[错误处理](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID10)。
> Swift 中的错误处理涉及到错误处理模式,这会用到 Cocoa 和 Objective-C 中的 `NSError`。关于这个类的更多信息请参见 [Using Swift with Cocoa and Objective-C (Swift 4.1)](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中的[错误处理](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID10)。
<a name="representing_and_throwing_errors"></a>
## 表示并抛出错误
在 Swift 中,错误用符合`Error`协议的类型的值来表示。这个空协议表明该类型可以用于错误处理。
在 Swift 中,错误用符合 `Error` 协议的类型的值来表示。这个空协议表明该类型可以用于错误处理。
Swift 的枚举类型尤为适合构建一组相关的错误状态,枚举的关联值还可以提供错误状态的额外信息。例如,你可以这样表示在一个游戏中操作自动贩卖机时可能会出现的错误状态:
@ -49,10 +50,10 @@ enum VendingMachineError: Error {
}
```
抛出一个错误可以让你表明有意外情况发生,导致正常的执行流程无法继续执行。抛出错误使用`throw`关键字。例如,下面的代码抛出一个错误,提示贩卖机还需要`5`个硬币:
抛出一个错误可以让你表明有意外情况发生,导致正常的执行流程无法继续执行。抛出错误使用 `throw` 关键字。例如,下面的代码抛出一个错误,提示贩卖机还需要 `5` 个硬币:
```swift
throw VendingMachineError. insufficientFunds(coinsNeeded: 5)
throw VendingMachineError.insufficientFunds(coinsNeeded: 5)
```
<a name="handling_errors"></a>
@ -60,18 +61,18 @@ throw VendingMachineError. insufficientFunds(coinsNeeded: 5)
某个错误被抛出时,附近的某部分代码必须负责处理这个错误,例如纠正这个问题、尝试另外一种方式、或是向用户报告错误。
Swift 中有`4`种处理错误的方式。你可以把函数抛出的错误传递给调用此函数的代码、用`do-catch`语句处理错误、将错误作为可选类型处理、或者断言此错误根本不会发生。每种方式在下面的小节中都有描述。
Swift 中有 `4` 种处理错误的方式。你可以把函数抛出的错误传递给调用此函数的代码、用 `do-catch` 语句处理错误、将错误作为可选类型处理、或者断言此错误根本不会发生。每种方式在下面的小节中都有描述。
当一个函数抛出一个错误时,你的程序流程会发生改变,所以重要的是你能迅速识别代码中会抛出错误的地方。为了标识出这些地方,在调用一个能抛出错误的函数、方法或者构造器之前,加上`try`关键字,或者`try?``try!`这种变体。这些关键字在下面的小节中有具体讲解。
当一个函数抛出一个错误时,你的程序流程会发生改变,所以重要的是你能迅速识别代码中会抛出错误的地方。为了标识出这些地方,在调用一个能抛出错误的函数、方法或者构造器之前,加上 `try` 关键字,或者 `try?``try!` 这种变体。这些关键字在下面的小节中有具体讲解。
> 注意
>
> Swift 中的错误处理和其他语言中用`try``catch``throw`进行异常处理很像。和其他语言中(包括 Objective-C 的异常处理不同的是Swift 中的错误处理并不涉及解除调用栈,这是一个计算代价高昂的过程。就此而言,`throw`语句的性能特性是可以和`return`语句相媲美的。
> Swift 中的错误处理和其他语言中用 `try``catch``throw` 进行异常处理很像。和其他语言中(包括 Objective-C 的异常处理不同的是Swift 中的错误处理并不涉及解除调用栈,这是一个计算代价高昂的过程。就此而言,`throw` 语句的性能特性是可以和 `return` 语句相媲美的。
<a name="propagating_errors_using_throwing_functions"></a>
### 用 throwing 函数传递错误
为了表示一个函数、方法或构造器可以抛出错误,在函数声明的参数列表之后加上`throws`关键字。一个标有`throws`关键字的函数被称作*throwing 函数*。如果这个函数指明了返回值类型,`throws`关键词需要写在箭头(`->`)的前面。
为了表示一个函数、方法或构造器可以抛出错误,在函数声明的参数列表之后加上 `throws` 关键字。一个标有 `throws` 关键字的函数被称作*throwing 函数*。如果这个函数指明了返回值类型,`throws` 关键词需要写在箭头(`->`)的前面。
```swift
func canThrowErrors() throws -> String
@ -84,7 +85,7 @@ func cannotThrowErrors() -> String
>
> 只有 throwing 函数可以传递错误。任何在某个非 throwing 函数内部抛出的错误只能在函数内部处理。
下面的例子中,`VendingMachine`类有一个`vend(itemNamed:)`方法,如果请求的物品不存在、缺货或者投入金额小于物品价格,该方法就会抛出一个相应的`VendingMachineError`
下面的例子中,`VendingMachine` 类有一个 `vend(itemNamed:)` 方法,如果请求的物品不存在、缺货或者投入金额小于物品价格,该方法就会抛出一个相应的 `VendingMachineError`
```swift
struct Item {
@ -127,9 +128,9 @@ class VendingMachine {
}
```
`vend(itemNamed:)`方法的实现中使用了`guard`语句来提前退出方法,确保在购买某个物品所需的条件中,有任一条件不满足时,能提前退出方法并抛出相应的错误。由于`throw`语句会立即退出方法,所以物品只有在所有条件都满足时才会被售出。
`vend(itemNamed:)` 方法的实现中使用了 `guard` 语句来提前退出方法,确保在购买某个物品所需的条件中,有任一条件不满足时,能提前退出方法并抛出相应的错误。由于 `throw` 语句会立即退出方法,所以物品只有在所有条件都满足时才会被售出。
因为`vend(itemNamed:)`方法会传递出它抛出的任何错误,在你的代码中调用此方法的地方,必须要么直接处理这些错误——使用`do-catch`语句,`try?``try!`;要么继续将这些错误传递下去。例如下面例子中,`buyFavoriteSnack(person:vendingMachine:)`同样是一个 throwing 函数,任何由`vend(itemNamed:)`方法抛出的错误会一直被传递到`buyFavoriteSnack(person:vendingMachine:) `函数被调用的地方。
因为 `vend(itemNamed:)` 方法会传递出它抛出的任何错误,在你的代码中调用此方法的地方,必须要么直接处理这些错误——使用 `do-catch` 语句,`try?``try!`;要么继续将这些错误传递下去。例如下面例子中,`buyFavoriteSnack(person:vendingMachine:)` 同样是一个 throwing 函数,任何由 `vend(itemNamed:)` 方法抛出的错误会一直被传递到 `buyFavoriteSnack(person:vendingMachine:)` 函数被调用的地方。
```swift
let favoriteSnacks = [
@ -143,9 +144,9 @@ func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
}
```
上例中,`buyFavoriteSnack(person:vendingMachine:) `函数会查找某人最喜欢的零食,并通过调用`vend(itemNamed:)`方法来尝试为他们购买。因为`vend(itemNamed:)`方法能抛出错误,所以在调用的它时候在它前面加了`try`关键字。
上例中,`buyFavoriteSnack(person:vendingMachine:)` 函数会查找某人最喜欢的零食,并通过调用 `vend(itemNamed:)` 方法来尝试为他们购买。因为 `vend(itemNamed:)` 方法能抛出错误,所以在调用的它时候在它前面加了 `try` 关键字。
`throwing`构造器能像`throwing`函数一样传递错误。例如下面代码中的`PurchasedSnack`构造器在构造过程中调用了throwing函数并且通过传递到它的调用者来处理这些错误。
`throwing` 构造器能像 `throwing` 函数一样传递错误。例如下面代码中的 `PurchasedSnack` 构造器在构造过程中调用了 throwing 函数,并且通过传递到它的调用者来处理这些错误。
```swift
struct PurchasedSnack {
@ -159,9 +160,9 @@ struct PurchasedSnack {
### 用 Do-Catch 处理错误
你可以使用一个`do-catch`语句运行一段闭包代码来处理错误。如果在`do`子句中的代码抛出了一个错误,这个错误会与`catch`子句做匹配,从而决定哪条子句能处理它。
你可以使用一个 `do-catch` 语句运行一段闭包代码来处理错误。如果在 `do` 子句中的代码抛出了一个错误,这个错误会与 `catch` 子句做匹配,从而决定哪条子句能处理它。
下面是`do-catch`语句的一般形式:
下面是 `do-catch` 语句的一般形式:
```swift
do {
@ -174,9 +175,9 @@ do {
}
```
`catch`后面写一个匹配模式来表明这个子句能处理什么样的错误。如果一条`catch`子句没有指定匹配模式,那么这条子句可以匹配任何错误,并且把错误绑定到一个名字为`error`的局部常量。关于模式匹配的更多信息请参考 [模式](../chapter3/07_Patterns.html)。
`catch` 后面写一个匹配模式来表明这个子句能处理什么样的错误。如果一条 `catch` 子句没有指定匹配模式,那么这条子句可以匹配任何错误,并且把错误绑定到一个名字为 `error` 的局部常量。关于模式匹配的更多信息请参考 [模式](../chapter3/07_Patterns.html)。
`catch`子句不必将`do`子句中的代码所抛出的每一个可能的错误都作处理。如果所有`catch`子句都未处理错误,错误就会传递到周围的作用域。然而,错误还是必须要被某个周围的作用域处理的——要么是一个外围的`do-catch`错误处理语句,要么是一个 throwing 函数的内部。举例来说,下面的代码处理了`VendingMachineError`枚举类型的全部枚举值,但是所有其它的错误就必须由它周围的作用域处理:
`catch` 子句不必将 `do` 子句中的代码所抛出的每一个可能的错误都作处理。如果所有 `catch` 子句都未处理错误,错误就会传递到周围的作用域。然而,错误还是必须要被某个周围的作用域处理的——要么是一个外围的 `do-catch` 错误处理语句,要么是一个 throwing 函数的内部。举例来说,下面的代码处理了 `VendingMachineError` 枚举类型的全部枚举值,但是所有其它的错误就必须由它周围的作用域处理:
```swift
var vendingMachine = VendingMachine()
@ -193,11 +194,11 @@ do {
// 打印 “Insufficient funds. Please insert an additional 2 coins.”
```
上面的例子中,`buyFavoriteSnack(person:vendingMachine:) `函数在一个`try`表达式中调用,因为它能抛出错误。如果错误被抛出,相应的执行会马上转移到`catch`子句中,并判断这个错误是否要被继续传递下去。如果没有错误抛出,`do`子句中余下的语句就会被执行。
上面的例子中,`buyFavoriteSnack(person:vendingMachine:)` 函数在一个 `try` 表达式中调用,因为它能抛出错误。如果错误被抛出,相应的执行会马上转移到 `catch` 子句中,并判断这个错误是否要被继续传递下去。如果没有错误抛出,`do` 子句中余下的语句就会被执行。
### 将错误转换成可选值
可以使用`try?`通过将错误转换成一个可选值来处理错误。如果在评估`try?`表达式时一个错误被抛出,那么表达式的值就是`nil`。例如,在下面的代码中,`x``y`有着相同的数值和等价的含义:
可以使用 `try?` 通过将错误转换成一个可选值来处理错误。如果在评估 `try?` 表达式时一个错误被抛出,那么表达式的值就是 `nil`。例如,在下面的代码中,`x``y` 有着相同的数值和等价的含义:
```swift
func someThrowingFunction() throws -> Int {
@ -214,9 +215,9 @@ do {
}
```
如果`someThrowingFunction()`抛出一个错误,`x``y`的值是`nil`。否则`x``y`的值就是该函数的返回值。注意,无论`someThrowingFunction()`的返回值类型是什么类型,`x``y`都是这个类型的可选类型。例子中此函数返回一个整型,所以`x``y`是可选整型。
如果 `someThrowingFunction()` 抛出一个错误,`x``y` 的值是 `nil`。否则 `x``y` 的值就是该函数的返回值。注意,无论 `someThrowingFunction()` 的返回值类型是什么类型,`x``y` 都是这个类型的可选类型。例子中此函数返回一个整型,所以 `x``y` 是可选整型。
如果你想对所有的错误都采用同样的方式来处理,用`try?`就可以让你写出简洁的错误处理代码。例如,下面的代码用几种方式来获取数据,如果所有方式都失败了则返回`nil`
如果你想对所有的错误都采用同样的方式来处理,用 `try?` 就可以让你写出简洁的错误处理代码。例如,下面的代码用几种方式来获取数据,如果所有方式都失败了则返回 `nil`
```swift
func fetchData() -> Data? {
@ -228,9 +229,9 @@ func fetchData() -> Data? {
### 禁用错误传递
有时你知道某个`throwing`函数实际上在运行时是不会抛出错误的,在这种情况下,你可以在表达式前面写`try!`来禁用错误传递,这会把调用包装在一个不会有错误抛出的运行时断言中。如果真的抛出了错误,你会得到一个运行时错误。
有时你知道某个 `throwing` 函数实际上在运行时是不会抛出错误的,在这种情况下,你可以在表达式前面写 `try!` 来禁用错误传递,这会把调用包装在一个不会有错误抛出的运行时断言中。如果真的抛出了错误,你会得到一个运行时错误。
例如,下面的代码使用了`loadImage(atPath:)`函数,该函数从给定的路径加载图片资源,如果图片无法载入则抛出一个错误。在这种情况下,因为图片是和应用绑定的,运行时不会有错误抛出,所以适合禁用错误传递。
例如,下面的代码使用了 `loadImage(atPath:)` 函数,该函数从给定的路径加载图片资源,如果图片无法载入则抛出一个错误。在这种情况下,因为图片是和应用绑定的,运行时不会有错误抛出,所以适合禁用错误传递。
```swift
let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
@ -239,9 +240,9 @@ let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
<a name="specifying_cleanup_actions"></a>
## 指定清理操作
你可以使用`defer`语句在即将离开当前代码块时执行一系列语句。该语句让你能执行一些必要的清理工作,不管是以何种方式离开当前代码块的——无论是由于抛出错误而离开,或是由于诸如`return``break`的语句。例如,你可以用`defer`语句来确保文件描述符得以关闭,以及手动分配的内存得以释放。
你可以使用 `defer` 语句在即将离开当前代码块时执行一系列语句。该语句让你能执行一些必要的清理工作,不管是以何种方式离开当前代码块的——无论是由于抛出错误而离开,或是由于诸如 `return``break` 的语句。例如,你可以用 `defer` 语句来确保文件描述符得以关闭,以及手动分配的内存得以释放。
`defer`语句将代码的执行延迟到当前的作用域退出之前。该语句由`defer`关键字和要被延迟执行的语句组成。延迟执行的语句不能包含任何控制转移语句,例如`break``return`语句,或是抛出一个错误。延迟执行的操作会按照它们声明的顺序从后往前执行——也就是说,第一条`defer`语句中的代码最后才执行,第二条`defer`语句中的代码倒数第二个执行,以此类推。最后一条语句会第一个执行。
`defer` 语句将代码的执行延迟到当前的作用域退出之前。该语句由 `defer` 关键字和要被延迟执行的语句组成。延迟执行的语句不能包含任何控制转移语句,例如 `break``return` 语句,或是抛出一个错误。延迟执行的操作会按照它们声明的顺序从后往前执行——也就是说,第一条 `defer` 语句中的代码最后才执行,第二条 `defer` 语句中的代码倒数第二个执行,以此类推。最后一条语句会第一个执行。
```swift
func processFile(filename: String) throws {
@ -258,8 +259,8 @@ func processFile(filename: String) throws {
}
```
上面的代码使用一条`defer`语句来确保`open(_:)`函数有一个相应的对`close(_:)`函数的调用。
上面的代码使用一条 `defer` 语句来确保 `open(_:)` 函数有一个相应的对 `close(_:)` 函数的调用。
> 注意
>
> 即使没有涉及到错误处理的代码,你也可以使用`defer`语句。
> 即使没有涉及到错误处理的代码,你也可以使用 `defer` 语句。

View File

@ -119,7 +119,7 @@ print("Media library contains \(movieCount) movies and \(songCount) songs")
某类型的一个常量或变量可能在幕后实际上属于一个子类。当确定是这种情况时,你可以尝试向下转到它的子类型,用*类型转换操作符*`as?``as!`)。
因为向下转型可能会失败,类型转型操作符带有两种不同形式。条件形式`as?` 返回一个你试图向下转成的类型的可选值。强制形式 `as!` 把试图向下转型和强制解包转换结果结合为一个操作。
因为向下转型可能会失败,类型转型操作符带有两种不同形式。条件形式 `as?` 返回一个你试图向下转成的类型的可选值。强制形式 `as!` 把试图向下转型和强制解包转换结果结合为一个操作。
当你不确定向下转型可以成功时,用类型转换的条件形式(`as?`)。条件形式的类型转换总是返回一个可选值,并且若下转是不可能的,可选值将是 `nil`。这使你能够检查向下转型是否成功。
@ -185,7 +185,7 @@ things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
things.append({ (name: String) -> String in "Hello, \(name)" })
```
`things` 数组包含两个 `Int` 值,两个 `Double` 值,一个 `String` 值,一个元组 `(Double, Double)`,一个`Movie`实例“Ghostbusters”以及一个接受 `String` 值并返回另一个 `String` 值的闭包表达式。
`things` 数组包含两个 `Int` 值,两个 `Double` 值,一个 `String` 值,一个元组 `(Double, Double)`,一个 `Movie` 实例“Ghostbusters”以及一个接受 `String` 值并返回另一个 `String` 值的闭包表达式。
你可以在 `switch` 表达式的 `case` 中使用 `is``as` 操作符来找出只知道是 `Any``AnyObject` 类型的常量或变量的具体类型。下面的示例迭代 `things` 数组中的每一项,并用 `switch` 语句查找每一项的类型。有几个 `switch` 语句的 `case` 绑定它们匹配到的值到一个指定类型的常量,从而可以打印这些值:
@ -208,7 +208,7 @@ for thing in things {
print("an (x, y) point at \(x), \(y)")
case let movie as Movie:
print("a movie called '\(movie.name)', dir. \(movie.director)")
case let stringConverter as String -> String:
case let stringConverter as (String) -> String:
print(stringConverter("Michael"))
default:
print("something else")
@ -227,7 +227,7 @@ for thing in things {
> 注意
>
> `Any`类型可以表示所有类型的值包括可选类型。Swift 会在你用`Any`类型来表示一个可选值的时候,给你一个警告。如果你确实想使用`Any`类型来承载可选值,你可以使用`as`操作符显式转换为`Any`,如下所示:
> `Any` 类型可以表示所有类型的值包括可选类型。Swift 会在你用 `Any` 类型来表示一个可选值的时候,给你一个警告。如果你确实想使用 `Any` 类型来承载可选值,你可以使用 `as` 操作符显式转换为 `Any`,如下所示:
>
>
```swift

View File

@ -17,9 +17,9 @@
> 4.0
> 翻译+校对:[EyreFree](https://www.eyrefree.org/) 2017-10-19
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
本页包含内容:
@ -35,7 +35,7 @@
下面这个例子定义了一个结构体 `BlackjackCard`(二十一点),用来模拟 `BlackjackCard` 中的扑克牌点数。`BlackjackCard` 结构体包含两个嵌套定义的枚举类型 `Suit``Rank`
`BlackjackCard` 中,`Ace` 牌可以表示 `1` 或者 `11` `Ace` 牌的这一特征通过一个嵌套在 `Rank` 枚举中的结构体 `Values` 来表示:
`BlackjackCard` 中,`Ace` 牌可以表示 `1` 或者 `11``Ace` 牌的这一特征通过一个嵌套在 `Rank` 枚举中的结构体 `Values` 来表示:
```swift
struct BlackjackCard {
@ -98,7 +98,7 @@ print("theAceOfSpades: \(theAceOfSpades.description)")
// 打印 “theAceOfSpades: suit is ♠, value is 1 or 11”
```
尽管 `Rank``Suit` 嵌套在 `BlackjackCard` 中,但它们的类型仍可从上下文中推断出来,所以在初始化实例时能够单独通过成员名称(`.ace``.spades`)引用枚举实例。在上面的例子中,`description` 属性正确地反映了黑桃A牌具有 `1``11` 两个值。
尽管 `Rank``Suit` 嵌套在 `BlackjackCard` 中,但它们的类型仍可从上下文中推断出来,所以在初始化实例时能够单独通过成员名称(`.ace``.spades`)引用枚举实例。在上面的例子中,`description` 属性正确地反映了黑桃 A 牌具有 `1``11` 两个值。
<a name="referring_to_nested_types"></a>
## 引用嵌套类型

View File

@ -9,7 +9,7 @@
> 翻译+校对:[futantan](https://github.com/futantan)
> 2.1
> 翻译:[小铁匠Linus](https://github.com/kevin833752)
> 翻译:[小铁匠 Linus](https://github.com/kevin833752)
> 校对:[shanks](http://codebuild.me)
>
> 2.2
@ -371,7 +371,7 @@ class SnakesAndLadders: DiceGame {
var square = 0
var board: [Int]
init() {
board = [Int](count: finalSquare + 1, repeatedValue: 0)
board = [Int](repeating: 0, count: finalSquare + 1)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
}
@ -433,7 +433,7 @@ class DiceGameTracker: DiceGameDelegate {
`DiceGameTracker` 实现了 `DiceGameDelegate` 协议要求的三个方法,用来记录游戏已经进行的轮数。当游戏开始时,`numberOfTurns` 属性被赋值为 `0`,然后在每新一轮中递增,游戏结束后,打印游戏的总轮数。
`gameDidStart(_:)` 方法从 `game` 参数获取游戏信息并打印。`game` 参数是 `DiceGame` 类型而不是 `SnakeAndLadders` 类型,所以在`gameDidStart(_:)` 方法中只能访问 `DiceGame` 协议中的内容。当然了,`SnakeAndLadders` 的方法也可以在类型转换之后调用。在上例代码中,通过 `is` 操作符检查 `game` 是否为 `SnakesAndLadders` 类型的实例,如果是,则打印出相应的消息。
`gameDidStart(_:)` 方法从 `game` 参数获取游戏信息并打印。`game` 参数是 `DiceGame` 类型而不是 `SnakeAndLadders` 类型,所以在 `gameDidStart(_:)` 方法中只能访问 `DiceGame` 协议中的内容。当然了,`SnakeAndLadders` 的方法也可以在类型转换之后调用。在上例代码中,通过 `is` 操作符检查 `game` 是否为 `SnakesAndLadders` 类型的实例,如果是,则打印出相应的消息。
无论当前进行的是何种游戏,由于 `game` 符合 `DiceGame` 协议,可以确保 `game` 含有 `dice` 属性。因此在 `gameDidStart(_:)` 方法中可以通过传入的 `game` 参数来访问 `dice` 属性,进而打印出 `dice``sides` 属性的值。
@ -505,9 +505,9 @@ print(game.textualDescription)
<a name="Conditionally_Conforming_to_a_Protocol"></a>
## 有条件地遵循协议
泛型类型可能只在某些情况下满足一个协议的要求,比如当类型的泛型形式参数遵循对应协议时。你可以通过在扩展类型时列出限制让泛型类型有条件地遵循某协议。在你采纳协议的名字后面写泛型 `where`分句。更多关于泛型 `where` 分句,见[泛型Where分句](./22_Generics.html##where_clauses)。
泛型类型可能只在某些情况下满足一个协议的要求,比如当类型的泛型形式参数遵循对应协议时。你可以通过在扩展类型时列出限制让泛型类型有条件地遵循某协议。在你采纳协议的名字后面写泛型 `where` 分句。更多关于泛型 `where` 分句,见[泛型 Where 分句](./22_Generics.html##where_clauses)。
下面的扩展让 `Array` 类型只要在存储遵循 `TextRepresentable`协议的元素时就遵循 `TextRepresentable` 协议。
下面的扩展让 `Array` 类型只要在存储遵循 `TextRepresentable` 协议的元素时就遵循 `TextRepresentable` 协议。
```swift
extension Array: TextRepresentable where Element: TextRepresentable {
@ -638,7 +638,7 @@ protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
}
```
在以上例子中,协议 `SomeClassOnlyProtocol` 只能被类类型采纳。如果尝试让结构体或枚举类型采纳`SomeClassOnlyProtocol`,则会导致编译时错误。
在以上例子中,协议 `SomeClassOnlyProtocol` 只能被类类型采纳。如果尝试让结构体或枚举类型采纳 `SomeClassOnlyProtocol`,则会导致编译时错误。
> 注意
>
@ -678,7 +678,7 @@ wishHappyBirthday(to: birthdayPerson)
上面的例子创建了一个名为 `birthdayPerson``Person` 的实例,作为参数传递给了 `wishHappyBirthday(to:)` 函数。因为 `Person` 同时符合这两个协议,所以这个参数合法,函数将打印生日问候语。
这里有一个例子将Location类和前面的Named协议进行组合
这里有一个例子:将 Location 类和前面的 Named 协议进行组合:
```swift
class Location {
@ -705,9 +705,9 @@ beginConcert(in: seattle)
// Prints "Hello, Seattle!"
```
`beginConcert(in:)`方法接受一个类型为 `Location & Named` 的参数,这意味着"任何Location的子类并且遵循Named协议"。例如City就满足这样的条件。
`beginConcert(in:)` 方法接受一个类型为 `Location & Named` 的参数,这意味着"任何 Location 的子类,并且遵循 Named 协议"。例如City 就满足这样的条件。
将 birthdayPerson 传入`beginConcert(in:)`函数是不合法的,因为 Person不是一个Location的子类。就像如果你新建一个类继承与Location但是没有遵循Named协议你用这个类的实例去调用`beginConcert(in:)`函数也是不合法的。
将 birthdayPerson 传入 `beginConcert(in:)` 函数是不合法的,因为 Person 不是一个 Location 的子类。就像,如果你新建一个类继承与 Location但是没有遵循 Named 协议,你用这个类的实例去调用 `beginConcert(in:)` 函数也是不合法的。
<a name="checking_for_protocol_conformance"></a>
## 检查协议一致性
@ -786,7 +786,7 @@ for object in objects {
<a name="optional_protocol_requirements"></a>
## 可选的协议要求
协议可以定义*可选要求*,遵循协议的类型可以选择是否实现这些要求。在协议中使用 `optional` 关键字作为前缀来定义可选要求。可选要求用在你需要和 Objective-C 打交道的代码中。协议和可选要求都必须带上`@objc`属性。标记 `@objc` 特性的协议只能被继承自 Objective-C 类的类或者 `@objc` 类遵循,其他类以及结构体和枚举均不能遵循这种协议。
协议可以定义*可选要求*,遵循协议的类型可以选择是否实现这些要求。在协议中使用 `optional` 关键字作为前缀来定义可选要求。可选要求用在你需要和 Objective-C 打交道的代码中。协议和可选要求都必须带上 `@objc` 属性。标记 `@objc` 特性的协议只能被继承自 Objective-C 类的类或者 `@objc` 类遵循,其他类以及结构体和枚举均不能遵循这种协议。
使用可选要求时(例如,可选的方法或者属性),它们的类型会自动变成可选的。比如,一个类型为 `(Int) -> String` 的方法会变成 `((Int) -> String)?`。需要注意的是整个函数类型是可选的,而不是函数的返回值。
@ -804,6 +804,7 @@ for object in objects {
`CounterDataSource` 协议定义了一个可选方法 `increment(forCount:)` 和一个可选属性 `fiexdIncrement`,它们使用了不同的方法来从数据源中获取适当的增量值。
> 注意
>
> 严格来讲,`CounterDataSource` 协议中的方法和属性都是可选的,因此遵循协议的类可以不实现这些要求,尽管技术上允许这样做,不过最好不要这样写。
`Counter` 类含有 `CounterDataSource?` 类型的可选属性 `dataSource`,如下所示:
@ -875,7 +876,7 @@ for _ in 1...4 {
}
```
`TowardsZeroSource` 实现了 `CounterDataSource` 协议中的 `increment(forCount:) ` 方法,以 `count` 参数为依据,计算出每次的增量。如果 `count` 已经为 `0`,此方法返回 `0`,以此表明之后不应再有增量操作发生。
`TowardsZeroSource` 实现了 `CounterDataSource` 协议中的 `increment(forCount:)` 方法,以 `count` 参数为依据,计算出每次的增量。如果 `count` 已经为 `0`,此方法返回 `0`,以此表明之后不应再有增量操作发生。
你可以使用 `TowardsZeroSource` 实例将 `Counter` 实例来从 `-4` 增加到 `0`。一旦增加到 `0`,数值便不会再有变动:
@ -940,9 +941,9 @@ extension PrettyTextRepresentable {
<a name="adding_constraints_to_protocol_extensions"></a>
### 为协议扩展添加限制条件
在扩展协议的时候,可以指定一些限制条件,只有遵循协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用 `where` 子句来描述,正如[泛型Where子句](./22_Generics.html#where_clauses)中所描述的。
在扩展协议的时候,可以指定一些限制条件,只有遵循协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用 `where` 子句来描述,正如[泛型 Where 子句](./22_Generics.html#where_clauses)中所描述的。
例如,你可以扩展 `Collection` 协议,适用于集合中的元素遵循了 `Equatable` 协议的情况。通过限制集合元素遵 `Equatable` 协议, 作为标准库的一部分, 你可以使用`==``!=`操作符来检查两个元素的等价性和非等价性。
例如,你可以扩展 `Collection` 协议,适用于集合中的元素遵循了 `Equatable` 协议的情况。通过限制集合元素遵 `Equatable` 协议, 作为标准库的一部分, 你可以使用 `==``!=` 操作符来检查两个元素的等价性和非等价性。
```swift
extension Collection where Element: Equatable {
@ -966,7 +967,7 @@ extension Collection where Element: Equatable {
let equalNumbers = [100, 100, 100, 100, 100]
let differentNumbers = [100, 100, 200, 100, 200]
```
由于数组遵循`Collection`而且整数遵循`Equatable` `equalNumbers``differentNumbers` 都可以使用 `allEqual()` 方法。
由于数组遵循 `Collection` 而且整数遵循 `Equatable``equalNumbers``differentNumbers` 都可以使用 `allEqual()` 方法。
```swift

View File

@ -68,7 +68,7 @@ print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// 打印 “someInt is now 107, and anotherInt is now 3”
```
诚然,`swapTwoInts(_:_:)` 函数挺有用,但是它只能交换 `Int` 值,如果你想要交换两个 `String` 值或者 `Double`值,就不得不写更多的函数,例如 `swapTwoStrings(_:_:)``swapTwoDoubles(_:_:)`,如下所示:
诚然,`swapTwoInts(_:_:)` 函数挺有用,但是它只能交换 `Int` 值,如果你想要交换两个 `String` 值或者 `Double` 值,就不得不写更多的函数,例如 `swapTwoStrings(_:_:)``swapTwoDoubles(_:_:)`,如下所示:
```swift
func swapTwoStrings(_ a: inout String, _ b: inout String) {
@ -354,7 +354,7 @@ func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
}
```
`findIndex(of:in:)` 唯一的类型参数写做 `T: Equatable`,也就意味着“任何符合 `Equatable` 协议的类型 `T` ”。
`findIndex(of:in:)` 唯一的类型参数写做 `T: Equatable`,也就意味着“任何符合 `Equatable` 协议的类型 `T`”。
`findIndex(of:in:)` 函数现在可以成功编译了,并且可以作用于任何符合 `Equatable` 的类型,如 `Double``String`
@ -373,14 +373,14 @@ let stringIndex = findIndex(of: "Andrea", in: ["Mike", "Malcolm", "Andrea"])
<a name="associated_types_in_action"></a>
### 关联类型实践
下面例子定义了一个 `Container` 协议,该协议定义了一个关联类型 `ItemType`
下面例子定义了一个 `Container` 协议,该协议定义了一个关联类型 `Item`
```swift
protocol Container {
associatedtype ItemType
mutating func append(_ item: ItemType)
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
subscript(i: Int) -> Item { get }
}
```
@ -396,7 +396,7 @@ protocol Container {
为了定义这三个条件,`Container` 协议需要在不知道容器中元素的具体类型的情况下引用这种类型。`Container` 协议需要指定任何通过 `append(_:)` 方法添加到容器中的元素和容器中的元素是相同类型,并且通过容器下标返回的元素的类型也是这种类型。
为了达到这个目的,`Container` 协议声明了一个关联类型 `ItemType`,写作 `associatedtype ItemType`。这个协议无法定义 `ItemType` 是什么类型的别名,这个信息将留给遵从协议的类型来提供。尽管如此,`ItemType` 别名提供了一种方式来引用 `Container` 中元素的类型,并将之用于 `append(_:)` 方法和下标,从而保证任何 `Container` 的行为都能够正如预期地被执行。
为了达到这个目的,`Container` 协议声明了一个关联类型 `Item`,写作 `associatedtype Item`。这个协议无法定义 `Item` 是什么类型的别名,这个信息将留给遵从协议的类型来提供。尽管如此,`Item` 别名提供了一种方式来引用 `Container` 中元素的类型,并将之用于 `append(_:)` 方法和下标,从而保证任何 `Container` 的行为都能够正如预期地被执行。
下面是先前的非泛型的 `IntStack` 类型,这一版本采纳并符合了 `Container` 协议:
@ -411,7 +411,7 @@ struct IntStack: Container {
return items.removeLast()
}
// Container 协议的实现部分
typealias ItemType = Int
typealias Item = Int
mutating func append(_ item: Int) {
self.push(item)
}
@ -426,9 +426,9 @@ struct IntStack: Container {
`IntStack` 结构体实现了 `Container` 协议的三个要求,其原有功能也不会和这些要求相冲突。
此外,`IntStack` 在实现 `Container` 的要求时,指定 `ItemType``Int` 类型,即 `typealias ItemType = Int`,从而将 `Container` 协议中抽象的 `ItemType` 类型转换为具体的 `Int` 类型。
此外,`IntStack` 在实现 `Container` 的要求时,指定 `Item``Int` 类型,即 `typealias Item = Int`,从而将 `Container` 协议中抽象的 `Item` 类型转换为具体的 `Int` 类型。
由于 Swift 的类型推断,你实际上不用在 `IntStack` 的定义中声明 `ItemType``Int`。因为 `IntStack` 符合 `Container` 协议的所有要求Swift 只需通过 `append(_:)` 方法的 `item` 参数类型和下标返回值的类型,就可以推断出 `ItemType` 的具体类型。事实上,如果你在上面的代码中删除了 `typealias ItemType = Int` 这一行,一切仍旧可以正常工作,因为 Swift 清楚地知道 `ItemType` 应该是哪种类型。
由于 Swift 的类型推断,你实际上不用在 `IntStack` 的定义中声明 `Item``Int`。因为 `IntStack` 符合 `Container` 协议的所有要求Swift 只需通过 `append(_:)` 方法的 `item` 参数类型和下标返回值的类型,就可以推断出 `Item` 的具体类型。事实上,如果你在上面的代码中删除了 `typealias Item = Int` 这一行,一切仍旧可以正常工作,因为 Swift 清楚地知道 `Item` 应该是哪种类型。
你也可以让泛型 `Stack` 结构体遵从 `Container` 协议:
@ -455,7 +455,7 @@ struct Stack<Element>: Container {
}
```
这一次,占位类型参数 `Element` 被用作 `append(_:)` 方法的 `item` 参数和下标的返回类型。Swift 可以据此推断出 `Element` 的类型即是 `ItemType` 的类型。
这一次,占位类型参数 `Element` 被用作 `append(_:)` 方法的 `item` 参数和下标的返回类型。Swift 可以据此推断出 `Element` 的类型即是 `Item` 的类型。
<a name="extending_an_existing_type_to_specify_an_associated_type"></a>
### 通过扩展一个存在的类型来指定关联类型
@ -468,7 +468,7 @@ Swift 的 `Array` 类型已经提供 `append(_:)` 方法,一个 `count` 属性
extension Array: Container {}
```
如同上面的泛型 `Stack` 结构体一样,`Array``append(_:)` 方法和下标确保了 Swift 可以推断出 `ItemType` 的类型。定义了这个扩展后,你可以将任意 `Array` 当作 `Container` 来使用。
如同上面的泛型 `Stack` 结构体一样,`Array``append(_:)` 方法和下标确保了 Swift 可以推断出 `Item` 的类型。定义了这个扩展后,你可以将任意 `Array` 当作 `Container` 来使用。
<a name="using_type_annotations_to_constrain_an_associated_type"></a>
### 给关联类型添加约束
@ -489,7 +489,7 @@ protocol Container {
<a name="Using_a_Protocol_in_Its_Associated_Types_Constraints"></a>
### 在关联类型约束里使用协议
协议可以作为它自身的要求出现。例如,有一个协议细化了 `Container` 协议,添加了一个 `suffix(_:)` 方法。 `suffix(_:)` 方法返回容器中从后往前给定数量的元素,把它们存储在一个 `Suffix` 类型的实例里。
协议可以作为它自身的要求出现。例如,有一个协议细化了 `Container` 协议,添加了一个 `suffix(_:)` 方法。`suffix(_:)` 方法返回容器中从后往前给定数量的元素,把它们存储在一个 `Suffix` 类型的实例里。
```swift
protocol SuffixableContainer: Container {
@ -498,7 +498,7 @@ protocol SuffixableContainer: Container {
}
```
在这个协议里, `Suffix` 是一个关联类型,就像上边例子中 `Container``Item` 类型一样。 `Suffix` 拥有两个约束:它必须遵循 `SuffixableContainer` 协议(就是当前定义的协议),以及它的 `Item` 类型必须是和容器里的 `Item` 类型相同。 `Item` 的约束是一个 `wher`e 分句,它在下面[带有泛型 Where 分句的扩展](#extensions_with_a_generic_where_clause)中有讨论。
在这个协议里,`Suffix` 是一个关联类型,就像上边例子中 `Container``Item` 类型一样。`Suffix` 拥有两个约束:它必须遵循 `SuffixableContainer` 协议(就是当前定义的协议),以及它的 `Item` 类型必须是和容器里的 `Item` 类型相同。`Item` 的约束是一个 `wher`e 分句,它在下面[带有泛型 Where 分句的扩展](#extensions_with_a_generic_where_clause)中有讨论。
这里有一个来自[闭包的循环强引用](./23_Automatic_Reference_Counting.html#strong_reference_cycles_for_closures)的 Stack 类型的扩展,它添加了对 `SuffixableContainer` 协议的遵循:
@ -521,7 +521,7 @@ let suffix = stackOfInts.suffix(2)
// suffix contains 20 and 30
```
在上面的例子中, `Suffix``Stack` 的关联类型,也就是 `Stack` ,所以 `Stack` 的后缀运算返回另一个 `Stack` 。另外,遵循 `SuffixableContainer` 的类型可以拥有一个与它自己不同的 `Suffix` 类型——也就是说后缀运算可以返回不同的类型。比如说,这里有一个非泛型 `IntStack` 类型的扩展,它添加了 `SuffixableContainer` 遵循,使用 `Stack<Int>` 作为它的后缀类型而不是 `IntStack`
在上面的例子中,`Suffix``Stack` 的关联类型,也就是 `Stack` ,所以 `Stack` 的后缀运算返回另一个 `Stack` 。另外,遵循 `SuffixableContainer` 的类型可以拥有一个与它自己不同的 `Suffix` 类型——也就是说后缀运算可以返回不同的类型。比如说,这里有一个非泛型 `IntStack` 类型的扩展,它添加了 `SuffixableContainer` 遵循,使用 `Stack<Int>` 作为它的后缀类型而不是 `IntStack`
```swift
extension IntStack: SuffixableContainer {
@ -551,7 +551,7 @@ extension IntStack: SuffixableContainer {
```swift
func allItemsMatch<C1: Container, C2: Container>
(_ someContainer: C1, _ anotherContainer: C2) -> Bool
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
where C1.Item == C2.Item, C1.Item: Equatable {
// 检查两个容器含有相同数量的元素
if someContainer.count != anotherContainer.count {
@ -576,8 +576,8 @@ func allItemsMatch<C1: Container, C2: Container>
- `C1` 必须符合 `Container` 协议(写作 `C1: Container`)。
- `C2` 必须符合 `Container` 协议(写作 `C2: Container`)。
- `C1``ItemType` 必须和 `C2``ItemType`类型相同(写作 `C1.ItemType == C2.ItemType`)。
- `C1``ItemType` 必须符合 `Equatable` 协议(写作 `C1.ItemType: Equatable`)。
- `C1``Item` 必须和 `C2``Item` 类型相同(写作 `C1.Item == C2.Item`)。
- `C1``Item` 必须符合 `Equatable` 协议(写作 `C1.Item: Equatable`)。
第三个和第四个要求被定义为一个 `where` 子句,写在关键字 `where` 后面,它们也是泛型函数类型参数列表的一部分。

View File

@ -13,13 +13,13 @@
> 校对:[shanks](http://codebuild.me)[Realank](https://github.com/Realank) 2016-01-23
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-14
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-14
> 3.0.1
> 3.0.1
> shanks2016-11-13
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
本页包含内容:
@ -34,8 +34,8 @@ Swift 使用*自动引用计数ARC*机制来跟踪和管理你的应用程
然而在少数情况下为了能帮助你管理内存ARC 需要更多的,代码之间关系的信息。本章描述了这些情况,并且为你示范怎样才能使 ARC 来管理你的应用程序的所有内存。在 Swift 使用 ARC 与在 Obejctive-C 中使用 ARC 非常类似,具体请参考[过渡到 ARC 的发布说明](https://developer.apple.com/library/content/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011226)
> 注意
>
> 注意
>
> 引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型,也不是通过引用的方式存储和传递。
<a name="how_arc_works"></a>
@ -47,14 +47,14 @@ Swift 使用*自动引用计数ARC*机制来跟踪和管理你的应用程
然而,当 ARC 收回和释放了正在被使用中的实例,该实例的属性和方法将不能再被访问和调用。实际上,如果你试图访问这个实例,你的应用程序很可能会崩溃。
为了确保使用中的实例不会被销毁ARC 会跟踪和计算每一个实例正在被多少属性常量和变量所引用。哪怕实例的引用数为1ARC都不会销毁这个实例。
为了确保使用中的实例不会被销毁ARC 会跟踪和计算每一个实例正在被多少属性常量和变量所引用。哪怕实例的引用数为1ARC 都不会销毁这个实例。
为了使上述成为可能,无论你将实例赋值给属性、常量或变量,它们都会创建此实例的强引用。之所以称之为“强”引用,是因为它会将实例牢牢地保持住,只要强引用还在,实例是不允许被销毁的。
<a name="arc_in_action"></a>
## 自动引用计数实践
下面的例子展示了自动引用计数的工作机制。例子以一个简单的`Person`类开始,并定义了一个叫`name`的常量属性:
下面的例子展示了自动引用计数的工作机制。例子以一个简单的 `Person` 类开始,并定义了一个叫 `name` 的常量属性:
```swift
class Person {
@ -69,9 +69,9 @@ class Person {
}
```
`Person`类有一个构造函数,此构造函数为实例的`name`属性赋值,并打印一条消息以表明初始化过程生效。`Person`类也拥有一个析构函数,这个析构函数会在实例被销毁时打印一条消息。
`Person` 类有一个构造函数,此构造函数为实例的 `name` 属性赋值,并打印一条消息以表明初始化过程生效。`Person` 类也拥有一个析构函数,这个析构函数会在实例被销毁时打印一条消息。
接下来的代码片段定义了三个类型为`Person?`的变量,用来按照代码片段中的顺序,为新的`Person`实例建立多个引用。由于这些变量是被定义为可选类型(`Person?`,而不是`Person`),它们的值会被自动初始化为`nil`,目前还不会引用到`Person`类的实例。
接下来的代码片段定义了三个类型为 `Person?` 的变量,用来按照代码片段中的顺序,为新的 `Person` 实例建立多个引用。由于这些变量是被定义为可选类型(`Person?`,而不是 `Person`),它们的值会被自动初始化为 `nil`,目前还不会引用到 `Person` 类的实例。
```swift
var reference1: Person?
@ -79,34 +79,34 @@ var reference2: Person?
var reference3: Person?
```
现在你可以创建`Person`类的新实例,并且将它赋值给三个变量中的一个:
现在你可以创建 `Person` 类的新实例,并且将它赋值给三个变量中的一个:
```swift
reference1 = Person(name: "John Appleseed")
// 打印 "John Appleseed is being initialized"
```
应当注意到当你调用`Person`类的构造函数的时候,`"John Appleseed is being initialized"`会被打印出来。由此可以确定构造函数被执行。
应当注意到当你调用 `Person` 类的构造函数的时候,`"John Appleseed is being initialized"` 会被打印出来。由此可以确定构造函数被执行。
由于`Person`类的新实例被赋值给了`reference1`变量,所以`reference1``Person`类的新实例之间建立了一个强引用。正是因为这一个强引用ARC 会保证`Person`实例被保持在内存中不被销毁。
由于 `Person` 类的新实例被赋值给了 `reference1` 变量,所以 `reference1``Person` 类的新实例之间建立了一个强引用。正是因为这一个强引用ARC 会保证 `Person` 实例被保持在内存中不被销毁。
如果你将同一个`Person`实例也赋值给其他两个变量,该实例又会多出两个强引用:
如果你将同一个 `Person` 实例也赋值给其他两个变量,该实例又会多出两个强引用:
```swift
reference2 = reference1
reference3 = reference1
```
现在这一个`Person`实例已经有三个强引用了。
现在这一个 `Person` 实例已经有三个强引用了。
如果你通过给其中两个变量赋值`nil`的方式断开两个强引用(包括最先的那个强引用),只留下一个强引用,`Person`实例不会被销毁:
如果你通过给其中两个变量赋值 `nil` 的方式断开两个强引用(包括最先的那个强引用),只留下一个强引用,`Person` 实例不会被销毁:
```swift
reference1 = nil
reference2 = nil
```
在你清楚地表明不再使用这个`Person`实例时即第三个也就是最后一个强引用被断开时ARC 会销毁它:
在你清楚地表明不再使用这个 `Person` 实例时即第三个也就是最后一个强引用被断开时ARC 会销毁它:
```swift
reference3 = nil
@ -116,13 +116,13 @@ reference3 = nil
<a name="strong_reference_cycles_between_class_instances"></a>
## 类实例之间的循环强引用
在上面的例子中ARC 会跟踪你所新创建的`Person`实例的引用数量,并且会在`Person`实例不再被需要时销毁它。
在上面的例子中ARC 会跟踪你所新创建的 `Person` 实例的引用数量,并且会在 `Person` 实例不再被需要时销毁它。
然而,我们可能会写出一个类实例的强引用数*永远不能*变成`0`的代码。如果两个类实例互相持有对方的强引用,因而每个实例都让对方一直存在,就是这种情况。这就是所谓的*循环强引用*。
然而,我们可能会写出一个类实例的强引用数*永远不能*变成 `0` 的代码。如果两个类实例互相持有对方的强引用,因而每个实例都让对方一直存在,就是这种情况。这就是所谓的*循环强引用*。
你可以通过定义类之间的关系为弱引用或无主引用,以替代强引用,从而解决循环强引用的问题。具体的过程在[解决类实例之间的循环强引用](#resolving_strong_reference_cycles_between_class_instances)中有描述。不管怎样,在你学习怎样解决循环强引用之前,很有必要了解一下它是怎样产生的。
下面展示了一个不经意产生循环强引用的例子。例子定义了两个类:`Person``Apartment`,用来建模公寓和它其中的居民:
下面展示了一个不经意产生循环强引用的例子。例子定义了两个类:`Person``Apartment`,用来建模公寓和它其中的居民:
```swift
class Person {
@ -140,31 +140,31 @@ class Apartment {
}
```
每一个`Person`实例有一个类型为`String`,名字为`name`的属性,并有一个可选的初始化为`nil``apartment`属性。`apartment`属性是可选的,因为一个人并不总是拥有公寓。
每一个 `Person` 实例有一个类型为 `String`,名字为 `name` 的属性,并有一个可选的初始化为 `nil``apartment` 属性。`apartment` 属性是可选的,因为一个人并不总是拥有公寓。
类似的,每个`Apartment`实例有一个叫`unit`,类型为`String`的属性,并有一个可选的初始化为`nil``tenant`属性。`tenant`属性是可选的,因为一栋公寓并不总是有居民。
类似的,每个 `Apartment` 实例有一个叫 `unit`,类型为 `String` 的属性,并有一个可选的初始化为 `nil``tenant` 属性。`tenant` 属性是可选的,因为一栋公寓并不总是有居民。
这两个类都定义了析构函数,用以在类实例被析构的时候输出信息。这让你能够知晓`Person``Apartment`的实例是否像预期的那样被销毁。
这两个类都定义了析构函数,用以在类实例被析构的时候输出信息。这让你能够知晓 `Person``Apartment` 的实例是否像预期的那样被销毁。
接下来的代码片段定义了两个可选类型的变量`john``unit4A`,并分别被设定为下面的`Apartment``Person`的实例。这两个变量都被初始化为`nil`,这正是可选类型的优点:
接下来的代码片段定义了两个可选类型的变量 `john``unit4A`,并分别被设定为下面的 `Apartment``Person` 的实例。这两个变量都被初始化为 `nil`,这正是可选类型的优点:
```swift
var john: Person?
var unit4A: Apartment?
```
现在你可以创建特定的`Person``Apartment`实例并将赋值给`john``unit4A`变量:
现在你可以创建特定的 `Person``Apartment` 实例并将赋值给 `john``unit4A` 变量:
```swift
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
```
在两个实例被创建和赋值后,下图表现了强引用的关系。变量`john`现在有一个指向`Person`实例的强引用,而变量`unit4A`有一个指向`Apartment`实例的强引用:
在两个实例被创建和赋值后,下图表现了强引用的关系。变量 `john` 现在有一个指向 `Person` 实例的强引用,而变量 `unit4A` 有一个指向 `Apartment` 实例的强引用:
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/referenceCycle01_2x.png)
现在你能够将这两个实例关联在一起,这样人就能有公寓住了,而公寓也有了房客。注意感叹号是用来展开和访问可选变量`john``unit4A`中的实例,这样实例的属性才能被赋值:
现在你能够将这两个实例关联在一起,这样人就能有公寓住了,而公寓也有了房客。注意感叹号是用来展开和访问可选变量 `john``unit4A` 中的实例,这样实例的属性才能被赋值:
```swift
john!.apartment = unit4A
@ -175,20 +175,20 @@ unit4A!.tenant = john
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/referenceCycle02_2x.png)
不幸的是,这两个实例关联后会产生一个循环强引用。`Person`实例现在有了一个指向`Apartment`实例的强引用,而`Apartment`实例也有了一个指向`Person`实例的强引用。因此,当你断开`john``unit4A`变量所持有的强引用时,引用计数并不会降为`0`,实例也不会被 ARC 销毁:
不幸的是,这两个实例关联后会产生一个循环强引用。`Person` 实例现在有了一个指向 `Apartment` 实例的强引用,而 `Apartment` 实例也有了一个指向 `Person` 实例的强引用。因此,当你断开 `john``unit4A` 变量所持有的强引用时,引用计数并不会降为 `0`,实例也不会被 ARC 销毁:
```swift
john = nil
unit4A = nil
```
注意,当你把这两个变量设为`nil`时,没有任何一个析构函数被调用。循环强引用会一直阻止`Person``Apartment`类实例的销毁,这就在你的应用程序中造成了内存泄漏。
注意,当你把这两个变量设为 `nil` 时,没有任何一个析构函数被调用。循环强引用会一直阻止 `Person``Apartment` 类实例的销毁,这就在你的应用程序中造成了内存泄漏。
在你将`john``unit4A`赋值为`nil`后,强引用关系如下图:
在你将 `john``unit4A` 赋值为 `nil` 后,强引用关系如下图:
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/referenceCycle03_2x.png)
`Person``Apartment`实例之间的强引用关系保留了下来并且不会被断开。
`Person``Apartment` 实例之间的强引用关系保留了下来并且不会被断开。
<a name="resolving_strong_reference_cycles_between_class_instances"></a>
## 解决实例之间的循环强引用
@ -202,17 +202,17 @@ Swift 提供了两种办法用来解决你在使用类的属性时所遇到的
<a name="weak_references"></a>
### 弱引用
*弱引用*不会对其引用的实例保持强引用,因而不会阻止 ARC 销毁被引用的实例。这个特性阻止了引用变为循环强引用。声明属性或者变量时,在前面加上`weak`关键字表明这是一个弱引用。
*弱引用*不会对其引用的实例保持强引用,因而不会阻止 ARC 销毁被引用的实例。这个特性阻止了引用变为循环强引用。声明属性或者变量时,在前面加上 `weak` 关键字表明这是一个弱引用。
因为弱引用不会保持所引用的实例即使引用存在实例也有可能被销毁。因此ARC 会在引用的实例被销毁后自动将其赋值为`nil`。并且因为弱引用可以允许它们的值在运行时被赋值为`nil`,所以它们会被定义为可选类型变量,而不是常量。
因为弱引用不会保持所引用的实例即使引用存在实例也有可能被销毁。因此ARC 会在引用的实例被销毁后自动将其赋值为 `nil`。并且因为弱引用可以允许它们的值在运行时被赋值为 `nil`,所以它们会被定义为可选类型变量,而不是常量。
你可以像其他可选值一样,检查弱引用的值是否存在,你将永远不会访问已销毁的实例的引用。
> 注意
> 注意
>
> 当 ARC 设置弱引用为`nil`时,属性观察不会被触发。
> 当 ARC 设置弱引用为 `nil` 时,属性观察不会被触发。
下面的例子跟上面`Person``Apartment`的例子一致,但是有一个重要的区别。这一次,`Apartment``tenant`属性被声明为弱引用:
下面的例子跟上面 `Person``Apartment` 的例子一致,但是有一个重要的区别。这一次,`Apartment``tenant` 属性被声明为弱引用:
```swift
class Person {
@ -230,7 +230,7 @@ class Apartment {
}
```
然后跟之前一样,建立两个变量(`john``unit4A`)之间的强引用,并关联两个实例:
然后跟之前一样,建立两个变量(`john``unit4A`)之间的强引用,并关联两个实例:
```swift
var john: Person?
@ -247,53 +247,54 @@ unit4A!.tenant = john
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/weakReference01_2x.png)
`Person`实例依然保持对`Apartment`实例的强引用,但是`Apartment`实例只持有对`Person`实例的弱引用。这意味着当你断开`john`变量所保持的强引用时,再也没有指向`Person`实例的强引用了:
`Person` 实例依然保持对 `Apartment` 实例的强引用,但是 `Apartment` 实例只持有对 `Person` 实例的弱引用。这意味着当你断开 `john` 变量所保持的强引用时,再也没有指向 `Person` 实例的强引用了:
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/weakReference02_2x.png)
由于再也没有指向`Person`实例的强引用,该实例会被销毁:
由于再也没有指向 `Person` 实例的强引用,该实例会被销毁:
```swift
john = nil
// 打印 "John Appleseed is being deinitialized"
```
唯一剩下的指向`Apartment`实例的强引用来自于变量`unit4A`。如果你断开这个强引用,再也没有指向`Apartment`实例的强引用了:
唯一剩下的指向 `Apartment` 实例的强引用来自于变量 `unit4A`。如果你断开这个强引用,再也没有指向 `Apartment` 实例的强引用了:
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/weakReference03_2x.png)
由于再也没有指向`Apartment`实例的强引用,该实例也会被销毁:
由于再也没有指向 `Apartment` 实例的强引用,该实例也会被销毁:
```swift
unit4A = nil
// 打印 "Apartment 4A is being deinitialized"
```
上面的两段代码展示了变量`john``unit4A`在被赋值为`nil`后,`Person`实例和`Apartment`实例的析构函数都打印出“销毁”的信息。这证明了引用循环被打破了。
上面的两段代码展示了变量 `john``unit4A` 在被赋值为 `nil` 后,`Person` 实例和 `Apartment` 实例的析构函数都打印出“销毁”的信息。这证明了引用循环被打破了。
> 注意
在使用垃圾收集的系统里,弱指针有时用来实现简单的缓冲机制,因为没有强引用的对象只会在内存压力触发垃圾收集时才被销毁。但是在 ARC 中,一旦值的最后一个强引用被移除,就会被立即销毁,这导致弱引用并不适合上面的用途。
>
> 在使用垃圾收集的系统里,弱指针有时用来实现简单的缓冲机制,因为没有强引用的对象只会在内存压力触发垃圾收集时才被销毁。但是在 ARC 中,一旦值的最后一个强引用被移除,就会被立即销毁,这导致弱引用并不适合上面的用途。
<a name="unowned_references"></a>
### 无主引用
和弱引用类似,*无主引用*不会牢牢保持住引用的实例。和弱引用不同的是,无主引用在其他实例有相同或者更长的生命周期时使用。你可以在声明属性或者变量时,在前面加上关键字`unowned`表示这是一个无主引用。
和弱引用类似,*无主引用*不会牢牢保持住引用的实例。和弱引用不同的是,无主引用在其他实例有相同或者更长的生命周期时使用。你可以在声明属性或者变量时,在前面加上关键字 `unowned` 表示这是一个无主引用。
无主引用通常都被期望拥有值。不过 ARC 无法在实例被销毁后将无主引用设为`nil`,因为非可选类型的变量不允许被赋值为`nil`
无主引用通常都被期望拥有值。不过 ARC 无法在实例被销毁后将无主引用设为 `nil`,因为非可选类型的变量不允许被赋值为 `nil`
>
>
>
> 使用无主引用,你*必须*确保引用始终指向一个未销毁的实例。
> 使用无主引用,你*必须*确保引用始终指向一个未销毁的实例。
>
> 如果你试图在实例被销毁后,访问该实例的无主引用,会触发运行时错误。
下面的例子定义了两个类,`Customer``CreditCard`,模拟了银行客户和客户的信用卡。这两个类中,每一个都将另外一个类的实例作为自身的属性。这种关系可能会造成循环强引用。
下面的例子定义了两个类,`Customer``CreditCard`,模拟了银行客户和客户的信用卡。这两个类中,每一个都将另外一个类的实例作为自身的属性。这种关系可能会造成循环强引用。
`Customer``CreditCard`之间的关系与前面弱引用例子中`Apartment``Person`的关系略微不同。在这个数据模型中,一个客户可能有或者没有信用卡,但是一张信用卡总是关联着一个客户。为了表示这种关系,`Customer`类有一个可选类型的`card`属性,但是`CreditCard`类有一个非可选类型的`customer`属性。
`Customer``CreditCard` 之间的关系与前面弱引用例子中 `Apartment``Person` 的关系略微不同。在这个数据模型中,一个客户可能有或者没有信用卡,但是一张信用卡总是关联着一个客户。为了表示这种关系,`Customer` 类有一个可选类型的 `card` 属性,但是 `CreditCard` 类有一个非可选类型的 `customer` 属性。
此外,只能通过将一个`number`值和`customer`实例传递给`CreditCard`构造函数的方式来创建`CreditCard`实例。这样可以确保当创建`CreditCard`实例时总是有一个`customer`实例与之关联。
此外,只能通过将一个 `number` 值和 `customer` 实例传递给 `CreditCard` 构造函数的方式来创建 `CreditCard` 实例。这样可以确保当创建 `CreditCard` 实例时总是有一个 `customer` 实例与之关联。
由于信用卡总是关联着一个客户,因此将`customer`属性定义为无主引用,用以避免循环强引用:
由于信用卡总是关联着一个客户,因此将 `customer` 属性定义为无主引用,用以避免循环强引用:
```swift
class Customer {
@ -316,17 +317,17 @@ class CreditCard {
}
```
> 注意
> 注意
>
> `CreditCard`类的`number`属性被定义为`UInt64`类型而不是`Int`类型,以确保`number`属性的存储量在 32 位和 64 位系统上都能足够容纳 16 位的卡号。
> `CreditCard` 类的 `number` 属性被定义为 `UInt64` 类型而不是 `Int` 类型,以确保 `number` 属性的存储量在 32 位和 64 位系统上都能足够容纳 16 位的卡号。
下面的代码片段定义了一个叫`john`的可选类型`Customer`变量,用来保存某个特定客户的引用。由于是可选类型,所以变量被初始化为`nil`
下面的代码片段定义了一个叫 `john` 的可选类型 `Customer` 变量,用来保存某个特定客户的引用。由于是可选类型,所以变量被初始化为 `nil`
```swift
var john: Customer?
```
现在你可以创建`Customer`类的实例,用它初始化`CreditCard`实例,并将新创建的`CreditCard`实例赋值为客户的`card`属性:
现在你可以创建 `Customer` 类的实例,用它初始化 `CreditCard` 实例,并将新创建的 `CreditCard` 实例赋值为客户的 `card` 属性:
```swift
john = Customer(name: "John Appleseed")
@ -337,13 +338,13 @@ john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/unownedReference01_2x.png)
`Customer`实例持有对`CreditCard`实例的强引用,而`CreditCard`实例持有对`Customer`实例的无主引用。
`Customer` 实例持有对 `CreditCard` 实例的强引用,而 `CreditCard` 实例持有对 `Customer` 实例的无主引用。
由于`customer`的无主引用,当你断开`john`变量持有的强引用时,再也没有指向`Customer`实例的强引用了:
由于 `customer` 的无主引用,当你断开 `john` 变量持有的强引用时,再也没有指向 `Customer` 实例的强引用了:
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/unownedReference02_2x.png)
由于再也没有指向`Customer`实例的强引用,该实例被销毁了。其后,再也没有指向`CreditCard`实例的强引用,该实例也随之被销毁了:
由于再也没有指向 `Customer` 实例的强引用,该实例被销毁了。其后,再也没有指向 `CreditCard` 实例的强引用,该实例也随之被销毁了:
```swift
john = nil
@ -351,28 +352,26 @@ john = nil
// 打印 "Card #1234567890123456 is being deinitialized"
```
最后的代码展示了在`john`变量被设为`nil``Customer`实例和`CreditCard`实例的构造函数都打印出了“销毁”的信息。
最后的代码展示了在 `john` 变量被设为 `nil``Customer` 实例和 `CreditCard` 实例的构造函数都打印出了“销毁”的信息。
> 注意
>
>上面的例子展示了如何使用安全无主引用。对于需要禁用运行时的安全检查的情况例如出于性能方面的原因Swift还提供了不安全无主引用。与所有不安全的操作一样,你需要负责检查代码以确保其安全性
>
>你可以通过`unowned(unsafe)`来声明不安全无主引用。如果你试图在实例被销毁后,访问该实例的不安全无主引用,你的程序会尝试访问该实例之前所在的内存地址,这是一个不安全的操作。
> 注意
> 上面的例子展示了如何使用安全的无主引用。对于需要禁用运行时的安全检查的情况例如出于性能方面的原因Swift 还提供了不安全的无主引用。与所有不安全的操作一样,你需要负责检查代码以确保其安全性。
> 你可以通过 `unowned(unsafe)` 来声明不安全无主引用。如果你试图在实例被销毁后,访问该实例的不安全无主引用,你的程序会尝试访问该实例之前所在的内存地址,这是一个不安全的操作
<a name="unowned_references_and_implicitly_unwrapped_optional_properties"></a>
### 无主引用和隐式解析可选属性
上面弱引用和无主引用的例子涵盖了两种常用的需要打破循环强引用的场景。
`Person``Apartment`的例子展示了两个属性的值都允许为`nil`,并会潜在的产生循环强引用。这种场景最适合用弱引用来解决。
`Person``Apartment` 的例子展示了两个属性的值都允许为 `nil`,并会潜在的产生循环强引用。这种场景最适合用弱引用来解决。
`Customer``CreditCard`的例子展示了一个属性的值允许为`nil`,而另一个属性的值不允许为`nil`,这也可能会产生循环强引用。这种场景最适合通过无主引用来解决。
`Customer``CreditCard` 的例子展示了一个属性的值允许为 `nil`,而另一个属性的值不允许为 `nil`,这也可能会产生循环强引用。这种场景最适合通过无主引用来解决。
然而,存在着第三种场景,在这种场景中,两个属性都必须有值,并且初始化完成后永远不会为`nil`。在这种场景中,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性。
然而,存在着第三种场景,在这种场景中,两个属性都必须有值,并且初始化完成后永远不会为 `nil`。在这种场景中,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性。
这使两个属性在初始化完成后能被直接访问(不需要可选展开),同时避免了循环引用。这一节将为你展示如何建立这种关系。
下面的例子定义了两个类,`Country``City`,每个类将另外一个类的实例保存为属性。在这个模型中,每个国家必须有首都,每个城市必须属于一个国家。为了实现这种关系,`Country`类拥有一个`capitalCity`属性,而`City`类有一个`country`属性:
下面的例子定义了两个类,`Country``City`,每个类将另外一个类的实例保存为属性。在这个模型中,每个国家必须有首都,每个城市必须属于一个国家。为了实现这种关系,`Country` 类拥有一个 `capitalCity` 属性,而 `City` 类有一个 `country` 属性:
```swift
class Country {
@ -394,15 +393,15 @@ class City {
}
```
为了建立两个类的依赖关系,`City`的构造函数接受一个`Country`实例作为参数,并且将实例保存到`country`属性。
为了建立两个类的依赖关系,`City` 的构造函数接受一个 `Country` 实例作为参数,并且将实例保存到 `country` 属性。
`Country`的构造函数调用了`City`的构造函数。然而,只有`Country`的实例完全初始化后,`Country`的构造函数才能把`self`传给`City`的构造函数。在[两段式构造过程](./14_Initialization.html#two_phase_initialization)中有具体描述。
`Country` 的构造函数调用了 `City` 的构造函数。然而,只有 `Country` 的实例完全初始化后,`Country` 的构造函数才能把 `self` 传给 `City` 的构造函数。在[两段式构造过程](./14_Initialization.html#two_phase_initialization)中有具体描述。
为了满足这种需求,通过在类型结尾处加上感叹号(`City!`)的方式,将`Country``capitalCity`属性声明为隐式解析可选类型的属性。这意味着像其他可选类型一样,`capitalCity`属性的默认值为`nil`,但是不需要展开它的值就能访问它。在[隐式解析可选类型](./01_The_Basics.html#implicityly_unwrapped_optionals)中有描述。
为了满足这种需求,通过在类型结尾处加上感叹号(`City!`)的方式,将 `Country``capitalCity` 属性声明为隐式解析可选类型的属性。这意味着像其他可选类型一样,`capitalCity` 属性的默认值为 `nil`,但是不需要展开它的值就能访问它。在[隐式解析可选类型](./01_The_Basics.html#implicityly_unwrapped_optionals)中有描述。
由于`capitalCity`默认值为`nil`,一旦`Country`的实例在构造函数中给`name`属性赋值后,整个初始化过程就完成了。这意味着一旦`name`属性被赋值后,`Country`的构造函数就能引用并传递隐式的`self``Country`的构造函数在赋值`capitalCity`时,就能将`self`作为参数传递给`City`的构造函数。
由于 `capitalCity` 默认值为 `nil`,一旦 `Country` 的实例在构造函数中给 `name` 属性赋值后,整个初始化过程就完成了。这意味着一旦 `name` 属性被赋值后,`Country` 的构造函数就能引用并传递隐式的 `self``Country` 的构造函数在赋值 `capitalCity` 时,就能将 `self` 作为参数传递给 `City` 的构造函数。
以上的意义在于你可以通过一条语句同时创建`Country``City`的实例,而不产生循环强引用,并且`capitalCity`的属性能被直接访问,而不需要通过感叹号来展开它的可选值:
以上的意义在于你可以通过一条语句同时创建 `Country``City` 的实例,而不产生循环强引用,并且 `capitalCity` 的属性能被直接访问,而不需要通过感叹号来展开它的可选值:
```swift
var country = Country(name: "Canada", capitalName: "Ottawa")
@ -410,20 +409,20 @@ print("\(country.name)'s capital city is called \(country.capitalCity.name)")
// 打印 "Canada's capital city is called Ottawa"
```
在上面的例子中,使用隐式解析可选值意味着满足了类的构造函数的两个构造阶段的要求。`capitalCity`属性在初始化完成后,能像非可选值一样使用和存取,同时还避免了循环强引用。
在上面的例子中,使用隐式解析可选值意味着满足了类的构造函数的两个构造阶段的要求。`capitalCity` 属性在初始化完成后,能像非可选值一样使用和存取,同时还避免了循环强引用。
<a name="strong_reference_cycles_for_closures"></a>
## 闭包的循环强引用
前面我们看到了循环强引用是在两个类实例属性互相保持对方的强引用时产生的,还知道了如何用弱引用和无主引用来打破这些循环强引用。
循环强引用还会发生在当你将一个闭包赋值给类实例的某个属性,并且这个闭包体中又使用了这个类实例时。这个闭包体中可能访问了实例的某个属性,例如`self.someProperty`,或者闭包中调用了实例的某个方法,例如`self.someMethod()`。这两种情况都导致了闭包“捕获”`self`,从而产生了循环强引用。
循环强引用还会发生在当你将一个闭包赋值给类实例的某个属性,并且这个闭包体中又使用了这个类实例时。这个闭包体中可能访问了实例的某个属性,例如 `self.someProperty`,或者闭包中调用了实例的某个方法,例如 `self.someMethod()`。这两种情况都导致了闭包“捕获”`self`,从而产生了循环强引用。
循环强引用的产生,是因为闭包和类相似,都是引用类型。当你把一个闭包赋值给某个属性时,你是将这个闭包的引用赋值给了属性。实质上,这跟之前的问题是一样的——两个强引用让彼此一直有效。但是,和两个类实例不同,这次一个是类实例,另一个是闭包。
Swift 提供了一种优雅的方法来解决这个问题,称之为`闭包捕获列表`closure capture list。同样的在学习如何用闭包捕获列表打破循环强引用之前先来了解一下这里的循环强引用是如何产生的这对我们很有帮助。
Swift 提供了一种优雅的方法来解决这个问题,称之为 `闭包捕获列表`closure capture list。同样的在学习如何用闭包捕获列表打破循环强引用之前先来了解一下这里的循环强引用是如何产生的这对我们很有帮助。
下面的例子为你展示了当一个闭包引用了`self`后是如何产生一个循环强引用的。例子中定义了一个叫`HTMLElement`的类,用一种简单的模型表示 HTML 文档中的一个单独的元素:
下面的例子为你展示了当一个闭包引用了 `self` 后是如何产生一个循环强引用的。例子中定义了一个叫 `HTMLElement` 的类,用一种简单的模型表示 HTML 文档中的一个单独的元素:
```swift
class HTMLElement {
@ -431,35 +430,35 @@ class HTMLElement {
let name: String
let text: String?
lazy var asHTML: Void -> String = {
lazy var asHTML: () -> String = {
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
```
`HTMLElement`类定义了一个`name`属性来表示这个元素的名称,例如代表头部元素的`"h1"`,代表段落的`"p"`,或者代表换行的`"br"``HTMLElement`还定义了一个可选属性`text`,用来设置 HTML 元素呈现的文本。
`HTMLElement` 类定义了一个 `name` 属性来表示这个元素的名称,例如代表头部元素的 `"h1"`,代表段落的 `"p"`,或者代表换行的 `"br"``HTMLElement` 还定义了一个可选属性 `text`,用来设置 HTML 元素呈现的文本。
除了上面的两个属性,`HTMLElement`还定义了一个`lazy`属性`asHTML`。这个属性引用了一个将`name``text`组合成 HTML 字符串片段的闭包。该属性是`Void -> String`类型,或者可以理解为“一个没有参数,返回`String`的函数”。
除了上面的两个属性,`HTMLElement` 还定义了一个 `lazy` 属性 `asHTML`。这个属性引用了一个将 `name``text` 组合成 HTML 字符串片段的闭包。该属性是 `Void -> String` 类型,或者可以理解为“一个没有参数,返回 `String` 的函数”。
默认情况下,闭包赋值给了`asHTML`属性,这个闭包返回一个代表 HTML 标签的字符串。如果`text`值存在,该标签就包含可选值`text`;如果`text`不存在,该标签就不包含文本。对于段落元素,根据`text``"some text"`还是`nil`,闭包会返回`"<p>some text</p>"`或者`"<p />"`
默认情况下,闭包赋值给了 `asHTML` 属性,这个闭包返回一个代表 HTML 标签的字符串。如果 `text` 值存在,该标签就包含可选值 `text`;如果 `text` 不存在,该标签就不包含文本。对于段落元素,根据 `text``"some text"` 还是 `nil`,闭包会返回 `"<p>some text</p>"` 或者 `"<p />"`
可以像实例方法那样去命名、使用`asHTML`属性。然而,由于`asHTML`是闭包而不是实例方法,如果你想改变特定 HTML 元素的处理方式的话,可以用自定义的闭包来取代默认值。
可以像实例方法那样去命名、使用 `asHTML` 属性。然而,由于 `asHTML` 是闭包而不是实例方法,如果你想改变特定 HTML 元素的处理方式的话,可以用自定义的闭包来取代默认值。
例如,可以将一个闭包赋值给`asHTML`属性,这个闭包能在`text`属性是`nil`时使用默认文本,这是为了避免返回一个空的 HTML 标签:
例如,可以将一个闭包赋值给 `asHTML` 属性,这个闭包能在 `text` 属性是 `nil` 时使用默认文本,这是为了避免返回一个空的 HTML 标签:
```swift
let heading = HTMLElement(name: "h1")
@ -471,13 +470,13 @@ print(heading.asHTML())
// 打印 "<h1>some default text</h1>"
```
> 注意
>
> `asHTML`声明为`lazy`属性,因为只有当元素确实需要被处理为 HTML 输出的字符串时,才需要使用`asHTML`。也就是说,在默认的闭包中可以使用`self`,因为只有当初始化完成以及`self`确实存在后,才能访问`lazy`属性。
> 注意
>
> `asHTML` 声明为 `lazy` 属性,因为只有当元素确实需要被处理为 HTML 输出的字符串时,才需要使用 `asHTML`。也就是说,在默认的闭包中可以使用 `self`,因为只有当初始化完成以及 `self` 确实存在后,才能访问 `lazy` 属性。
`HTMLElement`类只提供了一个构造函数,通过`name``text`(如果有的话)参数来初始化一个新元素。该类也定义了一个析构函数,当`HTMLElement`实例被销毁时,打印一条消息。
`HTMLElement` 类只提供了一个构造函数,通过 `name``text`(如果有的话)参数来初始化一个新元素。该类也定义了一个析构函数,当 `HTMLElement` 实例被销毁时,打印一条消息。
下面的代码展示了如何用`HTMLElement`类创建实例并打印消息:
下面的代码展示了如何用 `HTMLElement` 类创建实例并打印消息:
```swift
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
@ -485,41 +484,41 @@ print(paragraph!.asHTML())
// 打印 "<p>hello, world</p>"
```
> 注意
>
> 上面的`paragraph`变量定义为可选类型的`HTMLElement`,因此我们可以赋值`nil`给它来演示循环强引用。
> 注意
>
> 上面的 `paragraph` 变量定义为可选类型的 `HTMLElement`,因此我们可以赋值 `nil` 给它来演示循环强引用。
不幸的是,上面写的`HTMLElement`类产生了类实例和作为`asHTML`默认值的闭包之间的循环强引用。循环强引用如下图所示:
不幸的是,上面写的 `HTMLElement` 类产生了类实例和作为 `asHTML` 默认值的闭包之间的循环强引用。循环强引用如下图所示:
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/closureReferenceCycle01_2x.png)
实例的`asHTML`属性持有闭包的强引用。但是,闭包在其闭包体内使用了`self`(引用了`self.name``self.text`),因此闭包捕获了`self`,这意味着闭包又反过来持有了`HTMLElement`实例的强引用。这样两个对象就产生了循环强引用。(更多关于闭包捕获值的信息,请参考[值捕获](./07_Closures.html#capturing_values))。
实例的 `asHTML` 属性持有闭包的强引用。但是,闭包在其闭包体内使用了 `self`(引用了 `self.name``self.text`),因此闭包捕获了 `self`,这意味着闭包又反过来持有了 `HTMLElement` 实例的强引用。这样两个对象就产生了循环强引用。(更多关于闭包捕获值的信息,请参考[值捕获](./07_Closures.html#capturing_values))。
> 注意
>
> 虽然闭包多次使用了`self`,它只捕获`HTMLElement`实例的一个强引用。
> 注意
>
> 虽然闭包多次使用了 `self`,它只捕获 `HTMLElement` 实例的一个强引用。
如果设置`paragraph`变量为`nil`,打破它持有的`HTMLElement`实例的强引用,`HTMLElement`实例和它的闭包都不会被销毁,也是因为循环强引用:
如果设置 `paragraph` 变量为 `nil`,打破它持有的 `HTMLElement` 实例的强引用,`HTMLElement` 实例和它的闭包都不会被销毁,也是因为循环强引用:
```swift
paragraph = nil
```
注意,`HTMLElement`的析构函数中的消息并没有被打印,证明了`HTMLElement`实例并没有被销毁。
注意,`HTMLElement` 的析构函数中的消息并没有被打印,证明了 `HTMLElement` 实例并没有被销毁。
<a name="resolving_strong_reference_cycles_for_closures"></a>
## 解决闭包的循环强引用
在定义闭包时同时定义捕获列表作为闭包的一部分,通过这种方式可以解决闭包和类实例之间的循环强引用。捕获列表定义了闭包体内捕获一个或者多个引用类型的规则。跟解决两个类实例间的循环强引用一样,声明每个捕获的引用为弱引用或无主引用,而不是强引用。应当根据代码关系来决定使用弱引用还是无主引用。
> 注意
>
> Swift 有如下要求:只要在闭包内使用`self`的成员,就要用`self.someProperty`或者`self.someMethod()`(而不只是`someProperty``someMethod()`)。这提醒你可能会一不小心就捕获了`self`。
> 注意
>
> Swift 有如下要求:只要在闭包内使用 `self` 的成员,就要用 `self.someProperty` 或者 `self.someMethod()`(而不只是 `someProperty``someMethod()`)。这提醒你可能会一不小心就捕获了 `self`。
<a name="defining_a_capture_list"></a>
### 定义捕获列表
捕获列表中的每一项都由一对元素组成,一个元素是`weak``unowned`关键字,另一个元素是类实例的引用(例如`self`)或初始化过的变量(如`delegate = self.delegate!`)。这些项在方括号中用逗号分开。
捕获列表中的每一项都由一对元素组成,一个元素是 `weak``unowned` 关键字,另一个元素是类实例的引用(例如 `self`)或初始化过的变量(如 `delegate = self.delegate!`)。这些项在方括号中用逗号分开。
如果闭包有参数列表和返回类型,把捕获列表放在它们前面:
@ -530,7 +529,7 @@ lazy var someClosure: (Int, String) -> String = {
}
```
如果闭包没有指明参数列表或者返回类型,它们会通过上下文推断,那么可以把捕获列表和关键字`in`放在闭包最开始的地方:
如果闭包没有指明参数列表或者返回类型,它们会通过上下文推断,那么可以把捕获列表和关键字 `in` 放在闭包最开始的地方:
```swift
lazy var someClosure: Void -> String = {
@ -542,15 +541,15 @@ lazy var someClosure: Void -> String = {
<a name="weak_and_unowned_references"></a>
### 弱引用和无主引用
在闭包和捕获的实例总是互相引用并且总是同时销毁时,将闭包内的捕获定义为`无主引用`
在闭包和捕获的实例总是互相引用并且总是同时销毁时,将闭包内的捕获定义为 `无主引用`
相反的,在被捕获的引用可能会变为`nil`时,将闭包内的捕获定义为`弱引用`。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为`nil`。这使我们可以在闭包体内检查它们是否存在。
相反的,在被捕获的引用可能会变为 `nil` 时,将闭包内的捕获定义为 `弱引用`。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为 `nil`。这使我们可以在闭包体内检查它们是否存在。
> 注意
>
> 如果被捕获的引用绝对不会变为`nil`,应该用无主引用,而不是弱引用。
> 注意
>
> 如果被捕获的引用绝对不会变为 `nil`,应该用无主引用,而不是弱引用。
前面的`HTMLElement`例子中,无主引用是正确的解决循环强引用的方法。这样编写`HTMLElement`类来避免循环强引用:
前面的 `HTMLElement` 例子中,无主引用是正确的解决循环强引用的方法。这样编写 `HTMLElement` 类来避免循环强引用:
```swift
class HTMLElement {
@ -579,9 +578,9 @@ class HTMLElement {
}
```
上面的`HTMLElement`实现和之前的实现一致,除了在`asHTML`闭包中多了一个捕获列表。这里,捕获列表是`[unowned self]`,表示“将`self`捕获为无主引用而不是强引用”。
上面的 `HTMLElement` 实现和之前的实现一致,除了在 `asHTML` 闭包中多了一个捕获列表。这里,捕获列表是 `[unowned self]`,表示“将 `self` 捕获为无主引用而不是强引用”。
和之前一样,我们可以创建并打印`HTMLElement`实例:
和之前一样,我们可以创建并打印 `HTMLElement` 实例:
```swift
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
@ -593,7 +592,7 @@ print(paragraph!.asHTML())
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/closureReferenceCycle02_2x.png)
这一次,闭包以无主引用的形式捕获`self`,并不会持有`HTMLElement`实例的强引用。如果将`paragraph`赋值为`nil``HTMLElement`实例将会被销毁,并能看到它的析构函数打印出的消息:
这一次,闭包以无主引用的形式捕获 `self`,并不会持有 `HTMLElement` 实例的强引用。如果将 `paragraph` 赋值为 `nil``HTMLElement` 实例将会被销毁,并能看到它的析构函数打印出的消息:
```swift
paragraph = nil

View File

@ -7,11 +7,11 @@
> 2.0
> 翻译+校对:[mmoaay](https://github.com/mmoaay)
>
> 2.1
> 翻译:[Prayer](https://github.com/futantan)
> 校对:[shanks](http://codebuild.me)2015-11-01
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-17
@ -133,7 +133,7 @@ fileprivate func someFilePrivateFunction() {}
private func somePrivateFunction() {}
```
除非专门指定,否则实体默认的访问级别为 `internal`,可以查阅[默认访问级别](#default_access_levels)这一节。这意味着在不使用修饰符显式声明访问级别的情况下,`SomeInternalClass``someInternalConstant` 仍然拥有隐式的 `internal`
除非专门指定,否则实体默认的访问级别为 `internal`,可以查阅[默认访问级别](#default_access_levels)这一节。这意味着在不使用修饰符显式声明访问级别的情况下,`SomeInternalClass``someInternalConstant` 仍然拥有隐式的 `internal`
```swift
class SomeInternalClass {} // 隐式 internal
@ -147,7 +147,7 @@ var someInternalConstant = 0 // 隐式 internal
一个类型的访问级别也会影响到类型*成员*(属性、方法、构造器、下标)的默认访问级别。如果你将类型指定为 `private` 或者 `fileprivate` 级别,那么该类型的所有成员的默认访问级别也会变成 `private` 或者 `fileprivate` 级别。如果你将类型指定为公开或者 `internal` (或者不明确指定访问级别,而使用默认的 `internal` ),那么该类型的所有成员的默认访问级别将是内部访问。
>
>
>
> 上面提到,一个 `public` 类型的所有成员的访问级别默认为 `internal` 级别,而不是 `public` 级别。如果你想将某个成员指定为 `public` 级别,那么你必须显式指定。这样做的好处是,在你定义公共接口的时候,可以明确地选择哪些接口是需要公开的,哪些是内部使用的,避免不小心将内部使用的接口公开。
@ -434,7 +434,7 @@ extension SomeStruct: SomeProtocol {
<a name="type_aliases"></a>
## 类型别名
你定义的任何类型别名都会被当作不同的类型,以便于进行访问控制。类型别名的访问级别不可高于其表示的类型的访问级别。例如,`private` 级别的类型别名可以作为 `private``file-private``internal``public`或者`open`类型的别名,但是 `public` 级别的类型别名只能作为 `public` 类型的别名,不能作为 `internal``file-private`,或 `private` 类型的别名。
你定义的任何类型别名都会被当作不同的类型,以便于进行访问控制。类型别名的访问级别不可高于其表示的类型的访问级别。例如,`private` 级别的类型别名可以作为 `private``file-private``internal``public` 或者 `open` 类型的别名,但是 `public` 级别的类型别名只能作为 `public` 类型的别名,不能作为 `internal``file-private`,或 `private` 类型的别名。
> 注意
>

View File

@ -155,7 +155,7 @@ let blueComponent = pink & 0x0000FF // blueComponent 是 0x99即 153
红色部分是通过对 `0xCC6699``0xFF0000` 进行按位与运算后得到的。`0xFF0000` 中的 `0` 部分“掩盖”了 `OxCC6699` 中的第二、第三个字节,使得数值中的 `6699` 被忽略,只留下 `0xCC0000`
然后,再将这个数按向右移动 16 位(`>> 16`)。十六进制中每两个字符表示 8 个比特位,所以移动 16 位后 `0xCC0000` 就变为 `0x0000CC`。这个数和`0xCC`是等同的,也就是十进制数值的 `204`
然后,再将这个数按向右移动 16 位(`>> 16`)。十六进制中每两个字符表示 8 个比特位,所以移动 16 位后 `0xCC0000` 就变为 `0x0000CC`。这个数和 `0xCC` 是等同的,也就是十进制数值的 `204`
同样的,绿色部分通过对 `0xCC6699``0x00FF00` 进行按位与运算得到 `0x006600`。然后将这个数向右移动 8 位,得到 `0x66`,也就是十进制数值的 `102`
@ -441,7 +441,7 @@ Swift 为以下自定义类型提等价运算符供合成实现:
在类型原本的声明中声明遵循 `Equatable` 来接收这些默认实现。
下面为三维位置向量 `(x, y, z)` 定义的 `Vector3D` 结构体,与 `Vector2D` 类似,由于 `x` `y``z` 属性都是 `Equatable` 类型, `Vector3D` 就收到默认的等价运算符实现了。
下面为三维位置向量 `(x, y, z)` 定义的 `Vector3D` 结构体,与 `Vector2D` 类似,由于 `x``y``z` 属性都是 `Equatable` 类型,`Vector3D` 就收到默认的等价运算符实现了。
```swift
struct Vector3D: Equatable {

View File

@ -34,12 +34,12 @@ Swift 语言相对较小,这是由于 Swift 代码中在各种地方出现的
举个例子getter-setter 方法块的语法定义如下:
> getter-setter 方法块语法
>
>
> *getter-setter 方法块* → { [*getter 子句*](05_Declarations.html#getter-clause) [*setter 子句*](05_Declarations.html#setter-clause)<sub>可选</sub> } | { [*setter 子句*](05_Declarations.html#setter-clause) [*getter 子句*](05_Declarations.html#getter-clause) }
这个定义表明,一个 getter-setter 方法块可以由一个 getter 分句后跟一个可选的 setter 分句构成,然后用大括号括起来,或者由一个 setter 分句后跟一个 getter 分句构成,然后用大括号括起来。上述的语法产式等价于下面的两个语法产式,
> getter-setter 方法块语法
>
> getter-setter 方法块 → { [*getter 子句*](05_Declarations.html#getter-clause) [*setter 子句*](05_Declarations.html#setter-clause)<sub>可选</sub> }
> getter-setter 方法块 → { [*getter 子句*](05_Declarations.html#getter-clause) [*setter 子句*](05_Declarations.html#setter-clause)<sub>可选</sub> }
> getter-setter 方法块 → { [*setter 子句*](05_Declarations.html#setter-clause) [*getter 子句*](05_Declarations.html#getter-clause) }

View File

@ -42,26 +42,26 @@ Swift 的*“词法结构 (lexical structure)”* 描述了能构成该语言中
> 空白语法
<a id="whitespace"></a>
> *空白* → [*空白项*](#whitespace-item) [*空白*](#whitespace)<sub>可选</sub>
> *空白项* → [*断行符*](#line-break)
> *空白项* → [*注释*](#comment)
> *空白项* → [*多行注释*](#multiline-comment)
> *空白* → [*空白项*](#whitespace-item) [*空白*](#whitespace)<sub>可选</sub>
> *空白项* → [*断行符*](#line-break)
> *空白项* → [*注释*](#comment)
> *空白项* → [*多行注释*](#multiline-comment)
> *空白项* → U+0000U+0009U+000BU+000C 或者 U+0020
<a id="line-break"></a>
> *断行符* → U+000A
> *断行符* → U+000D
> *断行符* → U+000A
> *断行符* → U+000D
> *断行符* → U+000D 接着是 U+000A
<a id="comment"></a>
> *注释* → // [*注释内容 断行*](#comment-text line-break)
> *多行注释* → `/*` [*多行注释内容*](#multiline-commnet-text) `*/`
> *注释内容* → [*注释内容项*](#comment-text-item) [*注释内容*](#comment-text)<sub>可选</sub>
> *注释内容项* → 任何Unicode标量值 除了 U+000A 或者 U+000D
> *多行注释内容* → [*多行注释内容项*](#multiline-comment-text-item) [*多行注释内容*](#multiline-comment-text)<sub>可选</sub>
> *多行注释内容项* → [*多行注释*](#multiline-comment).
> *多行注释内容项* → [*注释内容项*](#comment-text-item)
> *多行注释内容项* → 任何Unicode标量值 除了 `/*` 或者 `*/`
<a id="comment"></a>
> *注释* → // [*注释内容 断行*](#comment-text line-break)
> *多行注释* → `/*` [*多行注释内容*](#multiline-commnet-text) `*/`
> *注释内容* → [*注释内容项*](#comment-text-item) [*注释内容*](#comment-text)<sub>可选</sub>
> *注释内容项* → 任何 Unicode 标量值, 除了 U+000A 或者 U+000D
> *多行注释内容* → [*多行注释内容项*](#multiline-comment-text-item) [*多行注释内容*](#multiline-comment-text)<sub>可选</sub>
> *多行注释内容项* → [*多行注释*](#multiline-comment).
> *多行注释内容项* → [*注释内容项*](#comment-text-item)
> *多行注释内容项* → 任何 Unicode 标量值, 除了 `/*` 或者 `*/`
注释可以包含额外的格式和标记,正如 [*Markup Formatting Reference*](https://developer.apple.com/library/prerelease/ios/documentation/Xcode/Reference/xcode_markup_formatting_ref/index.html#//apple_ref/doc/uid/TP40016497) 所述。
@ -77,40 +77,40 @@ Swift 的*“词法结构 (lexical structure)”* 描述了能构成该语言中
> 标识符语法
<a id="identifier"></a>
> *标识符* → [*头部标识符*](#identifier-head) [*标识符字符组*](#identifier-characters)<sub>可选</sub>
> *标识符* → \`[*头部标识符*](#identifier-head) [*标识符字符组*](#identifier-characters)<sub>可选</sub>\`
> *标识符* → [*隐式参数名*](#implicit-parameter-name)
> *标识符* → [*头部标识符*](#identifier-head) [*标识符字符组*](#identifier-characters)<sub>可选</sub>
> *标识符* → \`[*头部标识符*](#identifier-head) [*标识符字符组*](#identifier-characters)<sub>可选</sub>\`
> *标识符* → [*隐式参数名*](#implicit-parameter-name)
<a id="identifier-list"></a>
> *标识符列表* → [*标识符*](#identifier) | [*标识符*](#identifier) **,** [*标识符列表*](#identifier-list)
<a id="identifier-head"></a>
> *头部标识符* → 大写或小写字母 A - Z
> *头部标识符* → _
> *头部标识符* → U+00A8U+00AAU+00ADU+00AFU+00B2U+00B5或者 U+00B7U+00BA
> *头部标识符* → U+00BCU+00BEU+00C0U+00D6U+00D8U+00F6或者 U+00F8U+00FF
> *头部标识符* → U+0100U+02FFU+0370U+167FU+1681U+180D或者 U+180FU+1DBF
> *头部标识符* → U+1E00U+1FFF
> *头部标识符* → U+200BU+200DU+202AU+202EU+203FU+2040U+2054或者 U+2060U+206F
> *头部标识符* → U+2070U+20CFU+2100U+218FU+2460U+24FF或者 U+2776U+2793
> *头部标识符* → U+2C00U+2DFF 或者 U+2E80U+2FFF
> *头部标识符* → U+3004U+3007U+3021U+302FU+3031U+303F或者 U+3040U+D7FF
> *头部标识符* → U+F900U+FD3DU+FD40U+FDCFU+FDF0U+FE1F或者 U+FE30U+FE44
> *头部标识符* → U+FE47U+FFFD
> *头部标识符* → U+10000U+1FFFDU+20000U+2FFFDU+30000U+3FFFD或者 U+40000U+4FFFD
> *头部标识符* → U+50000U+5FFFDU+60000U+6FFFDU+70000U+7FFFD或者 U+80000U+8FFFD
> *头部标识符* → U+90000U+9FFFDU+A0000U+AFFFDU+B0000U+BFFFD或者 U+C0000U+CFFFD
> *头部标识符* → U+D0000U+DFFFD 或者 U+E0000U+EFFFD
<a id="identifier-head"></a>
> *头部标识符* → 大写或小写字母 A - Z
> *头部标识符* → _
> *头部标识符* → U+00A8U+00AAU+00ADU+00AFU+00B2U+00B5或者 U+00B7U+00BA
> *头部标识符* → U+00BCU+00BEU+00C0U+00D6U+00D8U+00F6或者 U+00F8U+00FF
> *头部标识符* → U+0100U+02FFU+0370U+167FU+1681U+180D或者 U+180FU+1DBF
> *头部标识符* → U+1E00U+1FFF
> *头部标识符* → U+200BU+200DU+202AU+202EU+203FU+2040U+2054或者 U+2060U+206F
> *头部标识符* → U+2070U+20CFU+2100U+218FU+2460U+24FF或者 U+2776U+2793
> *头部标识符* → U+2C00U+2DFF 或者 U+2E80U+2FFF
> *头部标识符* → U+3004U+3007U+3021U+302FU+3031U+303F或者 U+3040U+D7FF
> *头部标识符* → U+F900U+FD3DU+FD40U+FDCFU+FDF0U+FE1F或者 U+FE30U+FE44
> *头部标识符* → U+FE47U+FFFD
> *头部标识符* → U+10000U+1FFFDU+20000U+2FFFDU+30000U+3FFFD或者 U+40000U+4FFFD
> *头部标识符* → U+50000U+5FFFDU+60000U+6FFFDU+70000U+7FFFD或者 U+80000U+8FFFD
> *头部标识符* → U+90000U+9FFFDU+A0000U+AFFFDU+B0000U+BFFFD或者 U+C0000U+CFFFD
> *头部标识符* → U+D0000U+DFFFD 或者 U+E0000U+EFFFD
<a id="identifier-character"></a>
> *标识符字符* → 数值 0 - 9
> *标识符字符* → U+0300U+036FU+1DC0U+1DFFU+20D0U+20FF或者 U+FE20U+FE2F
> *标识符字符* → 数值 0 - 9
> *标识符字符* → U+0300U+036FU+1DC0U+1DFFU+20D0U+20FF或者 U+FE20U+FE2F
> *标识符字符* → [*头部标识符*](#identifier-head)
> <a id="identifier-characters"></a>
> *标识符字符组* → [*标识符字符*](#identifier-character) [*标识符字符组*](#identifier-characters)<sub>可选</sub>
> <a id="identifier-characters"></a>
> *标识符字符组* → [*标识符字符*](#identifier-character) [*标识符字符组*](#identifier-characters)<sub>可选</sub>
<a id="implicit-parameter-name"></a>
> *隐式参数名* → **$** [*十进制数字列表*](#decimal-digits)
<a id="implicit-parameter-name"></a>
> *隐式参数名* → **$** [*十进制数字列表*](#decimal-digits)
<a id="keywords"></a>
## 关键字和标点符号
@ -145,13 +145,13 @@ true // 布尔值字面量
当为一个字面量值指定了类型标注的时候,这个标注的类型必须能通过这个字面量值实例化。也就是说,这个类型必须符合这些 Swift 标准库协议中的一个:整数字面量的 `IntegerLiteralConvertible` 协议、浮点数字面量的 `FloatingPointLiteralConvertible` 协议、字符串字面量的 `StringLiteralConvertible` 协议以及布尔值字面量的 `BooleanLiteralConvertible` 协议。比如,`Int8` 符合 `IntegerLiteralConvertible` 协议,因此它能在 `let x: Int8 = 42` 这个声明中作为整数字面量 `42` 的类型标注。
> 字面量语法
>
> *字面量* → [*数值字面量*](#numeric-literal) | [*字符串字面量*](#string-literal) | [*布尔值字面量*](#boolean-literal) | [*nil 字面量*](#nil-literal)
>
> *字面量* → [*数值字面量*](#numeric-literal) | [*字符串字面量*](#string-literal) | [*布尔值字面量*](#boolean-literal) | [*nil 字面量*](#nil-literal)
<a id="numeric-literal"></a>
> *数值字面量* → **-**<sub>可选</sub> [*整数字面量*](#integer-literal) | **-**<sub>可选</sub> [*浮点数字面量*](#floating-point-literal)
<a id="numeric-literal"></a>
> *数值字面量* → **-**<sub>可选</sub> [*整数字面量*](#integer-literal) | **-**<sub>可选</sub> [*浮点数字面量*](#floating-point-literal)
> <a id="boolean-literal"></a>
> *布尔值字面量* → **true** | **false**
> *布尔值字面量* → **true** | **false**
> <a id="nil-literal"></a>
> *nil 字面量* → **nil**
@ -168,51 +168,51 @@ true // 布尔值字面量
除非特别指定,整数字面量的默认推导类型为 Swift 标准库类型中的 `Int`。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 [整数](../chapter2/01_The_Basics.html#integers)。
> 整数字面量语法
> 整数字面量语法
>
<a id="integer-literal"></a>
> *整数字面量* → [*二进制字面量*](#binary-literal)
> *整数字面量* → [*八进制字面量*](#octal-literal)
> *整数字面量* → [*十进制字面量*](#decimal-literal)
> *整数字面量* → [*十六进制字面量*](#hexadecimal-literal)
> *整数字面量* → [*二进制字面量*](#binary-literal)
> *整数字面量* → [*八进制字面量*](#octal-literal)
> *整数字面量* → [*十进制字面量*](#decimal-literal)
> *整数字面量* → [*十六进制字面量*](#hexadecimal-literal)
<a id="binary-literal"></a>
> *二进制字面量* → **0b** [*二进制数字*](#binary-digit) [*二进制字面量字符组*](#binary-literal-characters)<sub>可选</sub>
> <a id="binary-digit"></a>
> *二进制数字* → 数值 0 到 1
> <a id="binary-digit"></a>
> *二进制数字* → 数值 0 到 1
> <a id="binary-literal-character"></a>
> *二进制字面量字符* → [*二进制数字*](#binary-digit) | _
> *二进制字面量字符* → [*二进制数字*](#binary-digit) | _
> <a id="binary-literal-characters"></a>
> *二进制字面量字符组* → [*二进制字面量字符*](#binary-literal-character) [*二进制字面量字符组*](#binary-literal-characters)<sub>可选</sub>
> *二进制字面量字符组* → [*二进制字面量字符*](#binary-literal-character) [*二进制字面量字符组*](#binary-literal-characters)<sub>可选</sub>
<a id="octal-literal"></a>
> *八进制字面量* → **0o** [*八进字数字*](#octal-digit) [*八进制字符组*](#octal-literal-characters)<sub>可选</sub>
> <a id="octal-digit"></a>
> *八进字数字* → 数值 0 到 7
<a id="octal-literal"></a>
> *八进制字面量* → **0o** [*八进字数字*](#octal-digit) [*八进制字符组*](#octal-literal-characters)<sub>可选</sub>
> <a id="octal-digit"></a>
> *八进字数字* → 数值 0 到 7
> <a id="octal-literal-character"></a>
> *八进制字符* → [*八进字数字*](#octal-digit) | _
> *八进制字符* → [*八进字数字*](#octal-digit) | _
> <a id="octal-literal-characters"></a>
> *八进制字符组* → [*八进制字符*](#octal-literal-character) [*八进制字符组*](#octal-literal-characters)<sub>可选</sub>
<a id="decimal-literal"></a>
> *十进制字面量* → [*十进制数字*](#decimal-digit) [*十进制字符组*](#decimal-literal-characters)<sub>可选</sub>
<a id="decimal-literal"></a>
> *十进制字面量* → [*十进制数字*](#decimal-digit) [*十进制字符组*](#decimal-literal-characters)<sub>可选</sub>
> <a id="decimal-digit"></a>
> *十进制数字* → 数值 0 到 9
> *十进制数字* → 数值 0 到 9
> <a id="decimal-digits"></a>
> *十进制数字组* → [*十进制数字*](#decimal-digit) [*十进制数字组*](#decimal-digits)<sub>可选</sub>
> *十进制数字组* → [*十进制数字*](#decimal-digit) [*十进制数字组*](#decimal-digits)<sub>可选</sub>
> <a id="decimal-literal-character"></a>
> *十进制字符* → [*十进制数字*](#decimal-digit) | _
> *十进制字符* → [*十进制数字*](#decimal-digit) | _
> <a id="decimal-literal-characters"></a>
> *十进制字符组* → [*十进制字符*](#decimal-literal-character) [*十进制字符组*](#decimal-literal-characters)<sub>可选</sub>
<a id="hexadecimal-literal"></a>
> *十六进制字面量* → **0x** [*十六进制数字*](#hexadecimal-digit) [*十六进制字面量字符组*](#hexadecimal-literal-characters)<sub>可选</sub>
<a id="hexadecimal-literal"></a>
> *十六进制字面量* → **0x** [*十六进制数字*](#hexadecimal-digit) [*十六进制字面量字符组*](#hexadecimal-literal-characters)<sub>可选</sub>
> <a id="hexadecimal-digit"></a>
> *十六进制数字* → 数值 0 到 9, 字母 a 到 f, 或 A 到 F
> *十六进制数字* → 数值 0 到 9, 字母 a 到 f, 或 A 到 F
> <a id="hexadecimal-literal-character"></a>
> *十六进制字符* → [*十六进制数字*](#hexadecimal-digit) | _
> *十六进制字符* → [*十六进制数字*](#hexadecimal-digit) | _
> <a id="hexadecimal-literal-characters"></a>
> *十六进制字面量字符组* → [*十六进制字符*](#hexadecimal-literal-character) [*十六进制字面量字符组*](#hexadecimal-literal-characters)<sub>可选</sub>
> *十六进制字面量字符组* → [*十六进制字符*](#hexadecimal-literal-character) [*十六进制字面量字符组*](#hexadecimal-literal-characters)<sub>可选</sub>
<a id="floating_point_literals"></a>
### 浮点数字面量
@ -231,28 +231,28 @@ true // 布尔值字面量
除非特别指定,浮点数字面量的默认推导类型为 Swift 标准库类型中的 `Double`,表示 64 位浮点数。Swift 标准库也定义了 `Float` 类型,表示 32 位浮点数。
> 浮点数字面量语法
> 浮点数字面量语法
>
<a id="floating-point-literal"></a>
> *浮点数字面量* → [*十进制字面量*](#decimal-literal) [*十进制分数*](#decimal-fraction)<sub>可选</sub> [*十进制指数*](#decimal-exponent)<sub>可选</sub>
> *浮点数字面量* → [*十进制字面量*](#decimal-literal) [*十进制分数*](#decimal-fraction)<sub>可选</sub> [*十进制指数*](#decimal-exponent)<sub>可选</sub>
> *浮点数字面量* → [*十六进制字面量*](#hexadecimal-literal) [*十六进制分数*](#hexadecimal-fraction)<sub>可选</sub> [*十六进制指数*](#hexadecimal-exponent)
<a id="decimal-fraction"></a>
<a id="decimal-fraction"></a>
> *十进制分数* → **.** [*十进制字面量*](#decimal-literal)
> <a id="decimal-exponent"></a>
> *十进制指数* → [*十进制指数 e*](#floating-point-e) [*正负号*](#sign)<sub>可选</sub> [*十进制字面量*](#decimal-literal)
> <a id="decimal-exponent"></a>
> *十进制指数* → [*十进制指数 e*](#floating-point-e) [*正负号*](#sign)<sub>可选</sub> [*十进制字面量*](#decimal-literal)
<a id="hexadecimal-fraction"></a>
> *十六进制分数* → **.** [*十六进制数字*](#hexadecimal-digit) [*十六进制字面量字符组*](#hexadecimal-literal-characters)<sub>可选</sub>
> *十六进制分数* → **.** [*十六进制数字*](#hexadecimal-digit) [*十六进制字面量字符组*](#hexadecimal-literal-characters)<sub>可选</sub>
> <a id="hexadecimal-exponent"></a>
> *十六进制指数* → [*十六进制指数 p*](#floating-point-p) [*正负号*](#sign)<sub>可选</sub> [*十进制字面量*](#decimal-literal)
> *十六进制指数* → [*十六进制指数 p*](#floating-point-p) [*正负号*](#sign)<sub>可选</sub> [*十进制字面量*](#decimal-literal)
<a id="floating-point-e"></a>
> *十进制指数 e* → **e** | **E**
> *十进制指数 e* → **e** | **E**
> <a id="floating-point-p"></a>
> *十六进制指数 p* → **p** | **P**
> *十六进制指数 p* → **p** | **P**
> <a id="sign"></a>
> *正负号* → **+** | **-**
> *正负号* → **+** | **-**
<a id="string_literals"></a>
### 字符串字面量
@ -264,9 +264,9 @@ true // 布尔值字面量
字符串字面量中不能包含未转义的双引号(`"`)、未转义的反斜线(`\`)、回车符、换行符。
多行字符串字面量被包在三个双引号中的一串字符组成,形式如下:
> """
> `字符`
> """
> """
> `字符`
> """
与单行字符串字面量不同的是,多行字符串字面量可以包含不转义的双引号( " ),回车以及换行。它不能包含三个非转义的连续双引号。
@ -310,29 +310,29 @@ let textA = "Hello " + "world"
let textB = "Hello world"
```
> 字符串字面量语法
> 字符串字面量语法
>
<a id="string-literal"></a>
> *字符串字面量* → [*静态字符串字面量*](#static-string-literal) | [*插值字符串字面量*](#interpolated-string-literal)
<a id="static-string-literal"></a>
> *静态字符串字面量* → **"**[*引用文本*](#quoted-text)<sub>可选</sub>**"**
> *静态字符串字面量* → **"**[*引用文本*](#quoted-text)<sub>可选</sub>**"**
> <a id="quoted-text"></a>
> *引用文本* → [*引用文本项*](#quoted-text-item) [*引用文本*](#quoted-text)<sub>可选</sub>
> <a id="quoted-text-item"></a>
> *引用文本项* → [*转义字符*](#escaped-character)
> <a id="quoted-text-item"></a>
> *引用文本项* → [*转义字符*](#escaped-character)
> *引用文本项* → 除了 **"**、**\\**、U+000A、U+000D 以外的所有 Unicode 字符
<a id="interpolated-string-literal"></a>
> *插值字符串字面量* → **"**[*插值文本*](#interpolated-text)<sub>可选</sub>**"**
> *插值字符串字面量* → **"**[*插值文本*](#interpolated-text)<sub>可选</sub>**"**
> <a id="interpolated-text"></a>
> *插值文本* → [*插值文本项*](#interpolated-text-item) [*插值文本*](#interpolated-text)<sub>可选</sub>
> <a id="interpolated-text-item"></a>
> <a id="interpolated-text-item"></a>
> *插值文本项* → **\\****(**[*表达式*](./04_Expressions.html)**)** | [*引用文本项*](#quoted-text-item)
<a id="escaped-character"></a>
> *转义字符* → **\\****0** | **\\****\\** | **\t** | **\n** | **\r** | **\\"** | **\\'**
> *转义字符* → **\u {** [*unicode 标量数字*](#unicode-scalar-digits) **}**
> *转义字符* → **\\****0** | **\\****\\** | **\t** | **\n** | **\r** | **\\"** | **\\'**
> *转义字符* → **\u {** [*unicode 标量数字*](#unicode-scalar-digits) **}**
> <a id="unicode-scalar-digits"></a>
> *unicode 标量数字* → 一到八位的十六进制数字
@ -341,14 +341,14 @@ let textB = "Hello world"
Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基础运算符](../chapter2/02_Basic_Operators.html) 和 [高级运算符](../chapter2/25_Advanced_Operators.html) 中进行了阐述。这一小节将描述哪些字符能用于自定义运算符。
自定义运算符可以由以下其中之一的 ASCII 字符 `/``=` `-``+``!``*``%``<``>``&``|``^``?` 以及 `~`,或者后面语法中规定的任一个 Unicode 字符(其中包含了*数学运算符*、*零散符号(Miscellaneous Symbols)* 以及印刷符号 (Dingbats) 之类的 Unicode 块)开始。在第一个字符之后,允许使用组合型 Unicode 字符。
自定义运算符可以由以下其中之一的 ASCII 字符 `/``=``-``+``!``*``%``<``>``&``|``^``?` 以及 `~`,或者后面语法中规定的任一个 Unicode 字符(其中包含了*数学运算符*、*零散符号(Miscellaneous Symbols)* 以及印刷符号 (Dingbats) 之类的 Unicode 块)开始。在第一个字符之后,允许使用组合型 Unicode 字符。
您也可以以点号 (`.`) 开头来定义自定义运算符。这些运算符可以包含额外的点,例如 `.+.`。如果某个运算符不是以点号开头的,那么它就无法再包含另外的点号了。例如,`+.+` 就会被看作为一个 `+` 运算符后面跟着一个 `.+` 运算符。
虽然您可以用问号 `?` 来自定义运算符,但是这个运算符不能只包含单独的一个问号。此外,虽然运算符可以包含一个惊叹号 `!`,但是前缀运算符不能够以问号或者惊叹号开头。
> 注意
>
>
> 以下这些标记 `=`、`->`、`//`、`/*`、`*/`、`.`、`<`(前缀运算符)、`&`、`?`、`?`(中缀运算符)、`>`(后缀运算符)、`!` 、`?` 是被系统保留的。这些符号不能被重载,也不能用于自定义运算符。
运算符两侧的空白被用来区分该运算符是否为前缀运算符、后缀运算符或二元运算符。规则总结如下:
@ -366,50 +366,50 @@ Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基
要学习如何自定义运算符,请参考 [自定义运算符](../chapter2/25_Advanced_Operators.html#custom_operators) 和 [运算符声明](05_Declarations.html#operator_declaration)。要学习如何重载运算符,请参考 [运算符函数](../chapter2/25_Advanced_Operators.html#operator_functions)。
> 运算符语法
> 运算符语法
>
<a id="operator"></a>
> *运算符* → [*头部运算符*](#operator-head) [*运算符字符组*](#operator-characters)<sub>可选</sub>
> *运算符* → [*头部点运算符*](#dot-operator-head) [*点运算符字符组*](#dot-operator-characters)<sub>可选</sub>
> *运算符* → [*头部运算符*](#operator-head) [*运算符字符组*](#operator-characters)<sub>可选</sub>
> *运算符* → [*头部点运算符*](#dot-operator-head) [*点运算符字符组*](#dot-operator-characters)<sub>可选</sub>
<a id="operator-head"></a>
> *头部运算符* → **/** | **=** | **-** | **+** | **!** | __*__ | **%** | **<** | **>** | **&** | **|** | **^** | **~** | **?**
> *头部运算符* → U+00A1U+00A7
> *头部运算符* → U+00A9 或 U+00AB
> *头部运算符* → U+00AC 或 U+00AE
> *头部运算符* → U+00B0U+00B1U+00B6U+00BBU+00BFU+00D7或 U+00F7
> *头部运算符* → U+2016U+2017 或 U+2020U+2027
> *头部运算符* → U+2030U+203E
> *头部运算符* → U+2041U+2053
> *头部运算符* → U+2055U+205E
> *头部运算符* → U+2190U+23FF
> *头部运算符* → U+2500U+2775
> *头部运算符* → U+2794U+2BFF
> *头部运算符* → U+2E00U+2E7F
> *头部运算符* → U+3001U+3003
<a id="operator-head"></a>
> *头部运算符* → **/** | **=** | **-** | **+** | **!** | __*__ | **%** | **<** | **>** | **&** | **|** | **^** | **~** | **?**
> *头部运算符* → U+00A1U+00A7
> *头部运算符* → U+00A9 或 U+00AB
> *头部运算符* → U+00AC 或 U+00AE
> *头部运算符* → U+00B0U+00B1U+00B6U+00BBU+00BFU+00D7或 U+00F7
> *头部运算符* → U+2016U+2017 或 U+2020U+2027
> *头部运算符* → U+2030U+203E
> *头部运算符* → U+2041U+2053
> *头部运算符* → U+2055U+205E
> *头部运算符* → U+2190U+23FF
> *头部运算符* → U+2500U+2775
> *头部运算符* → U+2794U+2BFF
> *头部运算符* → U+2E00U+2E7F
> *头部运算符* → U+3001U+3003
> *头部运算符* → U+3008U+3030
<a id="operator-character"></a>
> *运算符字符* → [*头部运算符*](#operator-head)
> *运算符字符* → U+0300U+036F
> *运算符字符* → U+1DC0U+1DFF
> *运算符字符* → U+20D0U+20FF
> *运算符字符* → U+FE00U+FE0F
> *运算符字符* → U+FE20U+FE2F
> *运算符字符* → U+E0100U+E01EF
<a id="operator-character"></a>
> *运算符字符* → [*头部运算符*](#operator-head)
> *运算符字符* → U+0300U+036F
> *运算符字符* → U+1DC0U+1DFF
> *运算符字符* → U+20D0U+20FF
> *运算符字符* → U+FE00U+FE0F
> *运算符字符* → U+FE20U+FE2F
> *运算符字符* → U+E0100U+E01EF
> <a id="operator-characters"></a>
> *运算符字符组* → [*运算符字符*](#operator-character) [*运算符字符组*](#operator-characters)<sub>可选</sub>
> *运算符字符组* → [*运算符字符*](#operator-character) [*运算符字符组*](#operator-characters)<sub>可选</sub>
<a id="dot-operator-head"></a>
> *头部点运算符* → **..**
<a id="dot-operator-head"></a>
> *头部点运算符* → **..**
> <a id="dot-operator-character"></a>
> *点运算符字符* → **.** | [*运算符字符*](#operator-character)
> *点运算符字符* → **.** | [*运算符字符*](#operator-character)
> <a id="dot-operator-characters"></a>
> *点运算符字符组* → [*点运算符字符*](#dot-operator-character) [*点运算符字符组*](#dot-operator-characters)<sub>可选</sub>
<a id="binary-operator"></a>
> *二元运算符* → [*运算符*](#operator)
> *二元运算符* → [*运算符*](#operator)
> <a id="prefix-operator"></a>
> *前缀运算符* → [*运算符*](#operator)
> *前缀运算符* → [*运算符*](#operator)
> <a id="postfix-operator"></a>
> *后缀运算符* → [*运算符*](#operator)
> *后缀运算符* → [*运算符*](#operator)

View File

@ -10,9 +10,9 @@
> 2.1
> 翻译:[mmoaay](https://github.com/mmoaay)
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
本页包含内容:
@ -33,26 +33,26 @@ Swift 语言存在两种类型:命名型类型和复合型类型。命名型
那些通常被其它语言认为是基本或原始的数据型类型,比如表示数字、字符和字符串的类型,实际上就是命名型类型,这些类型在 Swift 标准库中是使用结构体来定义和实现的。因为它们是命名型类型,因此你可以按照 [扩展](../chapter2/21_Extensions.html) 和 [扩展声明](05_Declarations.html#extension_declaration) 中讨论的那样,声明一个扩展来增加它们的行为以满足你程序的需求。
复合型类型是没有名字的类型,它由 Swift 本身定义。Swift 存在两种复合型类型:函数类型和元组类型。一个复合型类型可以包含命名型类型和其它复合型类型。例如,元组类型 `(Int, (Int, Int))` 包含两个元素:第一个是命名型类型 `Int`,第二个是另一个复合型类型 `(Int, Int)`
你可以在命名型类型和复合型类型使用小括号。但是在类型旁加小括号没有任何作用。举个例子,`(Int)`等同于`Int`
复合型类型是没有名字的类型,它由 Swift 本身定义。Swift 存在两种复合型类型:函数类型和元组类型。一个复合型类型可以包含命名型类型和其它复合型类型。例如,元组类型 `(Int, (Int, Int))` 包含两个元素:第一个是命名型类型 `Int`,第二个是另一个复合型类型 `(Int, Int)`
你可以在命名型类型和复合型类型使用小括号。但是在类型旁加小括号没有任何作用。举个例子,`(Int)` 等同于 `Int`
本节讨论 Swift 语言本身定义的类型,并描述 Swift 中的类型推断行为。
> 类型语法
>
> 类型语法
>
<a name="type"></a>
> *类型* → [*数组类型*](#array-type)
> *类型* → [*字典类型*](#dictionary-type)
> *类型* → [*函数类型*](#function-type)
> *类型* → [*类型标识*](#type-identifier)
> *类型* → [*元组类型*](#tuple-type)
> *类型* → [*可选类型*](#optional-type)
> *类型* → [*隐式解析可选类型*](#implicitly-unwrapped-optional-type)
> *类型* → [*协议合成类型*](#protocol-composition-type)
> *类型* → [*元型类型*](#metatype-type)
> *类型* → **任意类型**
> *类型* → **自身类型**
> *类型* → [*数组类型*](#array-type)
> *类型* → [*字典类型*](#dictionary-type)
> *类型* → [*函数类型*](#function-type)
> *类型* → [*类型标识*](#type-identifier)
> *类型* → [*元组类型*](#tuple-type)
> *类型* → [*可选类型*](#optional-type)
> *类型* → [*隐式解析可选类型*](#implicitly-unwrapped-optional-type)
> *类型* → [*协议合成类型*](#protocol-composition-type)
> *类型* → [*元型类型*](#metatype-type)
> *类型* → **任意类型**
> *类型* → **自身类型**
> *类型* → [*(类型)*](#type)
<a name="type_annotation"></a>
@ -68,8 +68,8 @@ func someFunction(a: Int) { /* ... */ }
类型注解可以在类型之前包含一个类型特性的可选列表。
> 类型注解语法
>
> 类型注解语法
>
<a name="type-annotation"></a>
> *类型注解* → **:** [*特性列表*](06_Attributes.html#attributes)<sub>可选</sub> **输入输出参数**<sub>可选</sub> [*类型*](#type)
@ -93,12 +93,12 @@ let origin: Point = (0, 0)
var someValue: ExampleModule.MyType
```
> 类型标识符语法
> 类型标识符语法
>
<a name="type-identifier"></a>
> *类型标识符* → [*类型名称*](#type-name) [*泛型参数子句*](08_Generic_Parameters_and_Arguments.html#generic_argument_clause)<sub>可选</sub> | [*类型名称*](#type-name) [*泛型参数子句*](08_Generic_Parameters_and_Arguments.html#generic_argument_clause)<sub>可选</sub> **.** [*类型标识符*](#type-identifier)
> *类型标识符* → [*类型名称*](#type-name) [*泛型参数子句*](08_Generic_Parameters_and_Arguments.html#generic_argument_clause)<sub>可选</sub> | [*类型名称*](#type-name) [*泛型参数子句*](08_Generic_Parameters_and_Arguments.html#generic_argument_clause)<sub>可选</sub> **.** [*类型标识符*](#type-identifier)
<a name="type-name"></a>
> *类型名称* → [*标识符*](02_Lexical_Structure.html#identifier)
> *类型名称* → [*标识符*](02_Lexical_Structure.html#identifier)
<a name="tuple_type"></a>
## 元组类型
@ -116,18 +116,18 @@ someTuple = (9, 99) // 正确:命名类型被自动推断
someTuple = (left: 5, right: 5) // 错误:命名类型不匹配
```
所有的元组类型都包含两个及以上元素, 除了`Void` `Void` 是空元组类型 `()` 的别名。
所有的元组类型都包含两个及以上元素, 除了 `Void``Void` 是空元组类型 `()` 的别名。
> 元组类型语法
> 元组类型语法
>
<a name="tuple-type"></a>
> *元组类型* → **(** [*元组类型元素列表*](#tuple-type-element-list) <sub>可选</sub> **)**
> *元组类型* → **(** [*元组类型元素列表*](#tuple-type-element-list) <sub>可选</sub> **)**
<a name="tuple-type-element-list"></a>
> *元组类型元素列表* → [*元组类型元素*](#tuple-type-element) | [*元组类型元素*](#tuple-type-element) **,** [*元组类型元素列表*](#tuple-type-element-list)
> *元组类型元素列表* → [*元组类型元素*](#tuple-type-element) | [*元组类型元素*](#tuple-type-element) **,** [*元组类型元素列表*](#tuple-type-element-list)
<a name="tuple-type-element"></a>
> *元组类型元素* → [*元素名*](#element-name) [*类型注解*](#type-annotation) | [*类型*](#type)
<a name="element-name"></a>
> *元素名* → [*标识符*](02_Lexical_Structure.html#identifier)
<a name="element-name"></a>
> *元素名* → [*标识符*](02_Lexical_Structure.html#identifier)
<a name="function_type"></a>
## 函数类型
@ -138,13 +138,13 @@ someTuple = (left: 5, right: 5) // 错误:命名类型不匹配
参数类型是由逗号间隔的类型列表。由于参数类型和返回值类型可以是元组类型,所以函数类型支持多参数与多返回值的函数与方法。
你可以对函数参数`() - > T `(其中 T 是任何类型)使用 `autoclosure` 特性。这会自动将参数表达式转化为闭包,表达式的结果即闭包返回值。这从语法结构上提供了一种便捷:延迟对表达式的求值,直到其值在函数体中被调用。以自动闭包做为参数的函数类型的例子详见 [自动闭包](../chapter2/07_Closures.html#autoclosures) 。
你可以对函数参数 `() -> T`(其中 T 是任何类型)使用 `autoclosure` 特性。这会自动将参数表达式转化为闭包,表达式的结果即闭包返回值。这从语法结构上提供了一种便捷:延迟对表达式的求值,直到其值在函数体中被调用。以自动闭包做为参数的函数类型的例子详见 [自动闭包](../chapter2/07_Closures.html#autoclosures) 。
函数类型可以拥有一个可变长参数作为参数类型中的最后一个参数。从语法角度上讲,可变长参数由一个基础类型名字紧随三个点(`...`)组成,如 `Int...`。可变长参数被认为是一个包含了基础类型元素的数组。即 `Int...` 就是 `[Int]`。关于使用可变长参数的例子,请参阅 [可变参数](../chapter2/06_Functions.html#variadic_parameters)。
为了指定一个 `in-out` 参数,可以在参数类型前加 `inout` 前缀。但是你不可以对可变长参数或返回值类型使用 `inout`。关于这种参数的详细讲解请参阅 [输入输出参数](../chapter2/06_Functions.html#in_out_parameters)。
如果一个函数类型只有一个形式参数而且形式参数的类型是元组类型,那么元组类型在写函数类型的时候必须用圆括号括起来。比如说, `((Int, Int)) -> Void` 是接收一个元组 `(Int, Int)` 作为形式参数的函数的类型。与此相反,不加括号的`(Int, Int) -> Void` 是一个接收两个 `Int` 形式参数并且不返回任何值的函数的类型。相似地,因为`Void` 是空元组类型 `()` 的别名, 函数类型`(Void)-> Void`与一个空元组的变量的函数类型`(()) -> ()`是一样的。但这些类型和无变量的函数类型`() -> ()`是不一样的。
为了指定一个 `in-out` 参数,可以在参数类型前加 `inout` 前缀。但是你不可以对可变长参数或返回值类型使用 `inout`。关于这种参数的详细讲解请参阅 [输入输出参数](../chapter2/06_Functions.html#in_out_parameters)。
如果一个函数类型只有一个形式参数而且形式参数的类型是元组类型,那么元组类型在写函数类型的时候必须用圆括号括起来。比如说,`((Int, Int)) -> Void` 是接收一个元组 `(Int, Int)` 作为形式参数的函数的类型。与此相反,不加括号的 `(Int, Int) -> Void` 是一个接收两个 `Int` 形式参数并且不返回任何值的函数的类型。相似地,因为 `Void` 是空元组类型 `()` 的别名, 函数类型 `(Void)-> Void` 与一个空元组的变量的函数类型 `(()) -> ()` 是一样的。但这些类型和无变量的函数类型 `() -> ()` 是不一样的。
函数和方法中的变量名并不是函数类型的一部分。例如:
@ -153,7 +153,7 @@ func someFunction(left: Int, right: Int) {}
func anotherFunction(left: Int, right: Int) {}
func functionWithDifferentLabels(top: Int, bottom: Int) {}
var f = someFunction // 函数f的类型为 (Int, Int) -> Void, 而不是 (left: Int, right: Int) -> Void.
var f = someFunction // 函数 f 的类型为 (Int, Int) -> Void, 而不是 (left: Int, right: Int) -> Void.
f = anotherFunction // 正确
f = functionWithDifferentLabels // 正确
@ -163,55 +163,55 @@ func functionWithDifferentNumberOfArguments(left: Int, right: Int, top: Int) {}
f = functionWithDifferentArgumentTypes // 错误
f = functionWithDifferentNumberOfArguments // 错误
```
由于变量标签不是函数类型的一部分,你可以在写函数类型的时候省略它们。
```
var operation: (lhs: Int, rhs: Int) -> Int // 错误
var operation: (_ lhs: Int, _ rhs: Int) -> Int // 正确
var operation: (Int, Int) -> Int // 正确
```
由于变量标签不是函数类型的一部分,你可以在写函数类型的时候省略它们。
```
var operation: (lhs: Int, rhs: Int) -> Int // 错误
var operation: (_ lhs: Int, _ rhs: Int) -> Int // 正确
var operation: (Int, Int) -> Int // 正确
```
如果一个函数类型包涵多个箭头(->),那么函数类型将从右向左进行组合。例如,函数类型 `Int -> Int -> Int` 可以理解为 `Int -> (Int -> Int)`,也就是说,该函数类型的参数为 `Int` 类型,其返回类型是一个参数类型为 `Int`,返回类型为 `Int` 的函数类型。
函数类型若要抛出错误就必须使用 `throws` 关键字来标记,若要重抛错误则必须使用 `rethrows` 关键字来标记。`throws` 关键字是函数类型的一部分,非抛出函数是抛出函数函数的一个子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。抛出和重抛函数的相关描述见章节 [抛出函数与方法](05_Declarations.html#throwing_functions_and_methods) 和 [重抛函数与方法](05_Declarations.html#rethrowing_functions_and_methods)。
<a name="Restrictions for Nonescaping Closures"></a>
### 对非逃逸闭包的限制
非逃逸闭包函数不能作为参数传递到另一个非逃逸闭包函数的参数。这样的限制可以让Swift在编译时就完成更多的内存访问冲突检查 而不是在运行时。举个例子:
```
let external: (Any) -> Void = { _ in () }
func takesTwoFunctions(first: (Any) -> Void, second: (Any) -> Void) {
first(first) // 错误
second(second) // 错误
first(second) // 错误
second(first) // 错误
first(external) // 正确
external(first) // 正确
}
```
在上面代码里,`takesTwoFunctions(first:second:)`的两个参数都是函数。 它们都没有标记为`@escaping`, 因此它们都是非逃逸的。
上述例子里的被标记为“错误”的四个函数调用会产生编译错误。因为第一个和第二个参数是非逃逸函数,它们不能够被当作变量被传递到另一个非闭包函数参数。与此相反, 标记“正确”的两个函数不回产生编译错误。这些函数调用不会违反限制, 因为`外部(external)`不是`takesTwoFunctions(first:second:)`里的一个参数。
如果你需要避免这个限制, 标记其中之一的参数为逃逸, 或者使用`withoutActuallyEscaping(_:do:)`函数临时地转换非逃逸函数的其中一个参数为逃逸函数。关于避免内存访问冲突,可以参阅[内存安全](../chapter2/24_Memory_Safety.html)。
> 函数类型语法
<a name="Restrictions for Nonescaping Closures"></a>
### 对非逃逸闭包的限制
非逃逸闭包函数不能作为参数传递到另一个非逃逸闭包函数的参数。这样的限制可以让 Swift 在编译时就完成更多的内存访问冲突检查, 而不是在运行时。举个例子:
```
let external: (Any) -> Void = { _ in () }
func takesTwoFunctions(first: (Any) -> Void, second: (Any) -> Void) {
first(first) // 错误
second(second) // 错误
first(second) // 错误
second(first) // 错误
first(external) // 正确
external(first) // 正确
}
```
在上面代码里,`takesTwoFunctions(first:second:)` 的两个参数都是函数。 它们都没有标记为 `@escaping`, 因此它们都是非逃逸的。
上述例子里的被标记为“错误”的四个函数调用会产生编译错误。因为第一个和第二个参数是非逃逸函数,它们不能够被当作变量被传递到另一个非闭包函数参数。与此相反, 标记“正确”的两个函数不回产生编译错误。这些函数调用不会违反限制, 因为 `外部(external)` 不是 `takesTwoFunctions(first:second:)` 里的一个参数。
如果你需要避免这个限制, 标记其中之一的参数为逃逸, 或者使用 `withoutActuallyEscaping(_:do:)` 函数临时地转换非逃逸函数的其中一个参数为逃逸函数。关于避免内存访问冲突,可以参阅[内存安全](../chapter2/24_Memory_Safety.html)。
> 函数类型语法
>
<a name="function-type"></a>
> *函数类型* → [*特性列表*](06_Attributes.html#attributes)<sub>可选</sub> [*函数类型子句*](#function-type-argument-clause) **throws**<sub>可选</sub> **->** [*类型*](#type)
> *函数类型* → [*特性列表*](06_Attributes.html#attributes)<sub>可选</sub> [*函数类型子句*](#function-type-argument-clause) **rethrows­** **->** [*类型*](#type)
> *函数类型* → [*特性列表*](06_Attributes.html#attributes)<sub>可选</sub> [*函数类型子句*](#function-type-argument-clause) **throws**<sub>可选</sub> **->** [*类型*](#type)
> *函数类型* → [*特性列表*](06_Attributes.html#attributes)<sub>可选</sub> [*函数类型子句*](#function-type-argument-clause) **rethrows­** **->** [*类型*](#type)
<a name="function-type-argument-clause"></a>
> *函数类型子句* → (­)­
> *函数类型子句* → ([*函数类型参数列表*](#function-type-argument-list)*...*­<sub>可选</sub>)­
> *函数类型子句* → (­)­
> *函数类型子句* → ([*函数类型参数列表*](#function-type-argument-list)*...*­<sub>可选</sub>)­
<a name="function-type-argument-list"></a>
> *函数类型参数列表* → [*函数类型参数*](function-type-argument) | [*函数类型参数*](function-type-argument) [*函数类型参数列表*](#function-type-argument-list)
> *函数类型参数列表* → [*函数类型参数*](function-type-argument) | [*函数类型参数*](function-type-argument) [*函数类型参数列表*](#function-type-argument-list)
<a name="function-type-argument"></a>
> *函数类型参数* → [*特性列表*](06_Attributes.html#attributes)<sub>可选</sub> **输入输出参数**<sub>可选</sub> [*类型*](#type) | [*参数标签*](#argument-label) [*类型注解*](#type-annotation)
> *函数类型参数* → [*特性列表*](06_Attributes.html#attributes)<sub>可选</sub> **输入输出参数**<sub>可选</sub> [*类型*](#type) | [*参数标签*](#argument-label) [*类型注解*](#type-annotation)
<a name="argument-label"></a>
> *参数标签* → [*标识符*](02_Lexical_Structure.html#identifier)
@ -241,7 +241,7 @@ var array3D: [[[Int]]] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
关于 Swift 标准库中 `Array` 类型的详细讨论,请参阅 [数组](../chapter2/04_Collection_Types.html#arrays)。
> 数组类型语法
> 数组类型语法
>
<a name="array-type"></a>
> *数组类型* → **[** [*类型*](#type) **]**
@ -268,7 +268,7 @@ let someDictionary: Dictionary<String, Int> = ["Alex": 31, "Paul": 39]
关于 Swift 标准库中 `Dictionary` 类型的详细讨论,请参阅 [字典](../chapter2/04_Collection_Types.html#dictionaries)。
> 字典类型语法
> 字典类型语法
>
<a name="dictionary-type"></a>
> *字典类型* → **[** [*类型*](#type) **:** [*类型*](#type) **]**
@ -300,10 +300,10 @@ optionalInteger! // 42
更多细节以及更多如何使用可选类型的例子,请参阅 [可选类型](../chapter2/01_The_Basics.html#optionals)。
> 可选类型语法
> 可选类型语法
>
<a name="optional-type"></a>
> *可选类型* → [*类型*](#type) **?**
> *可选类型* → [*类型*](#type) **?**
<a name="implicitly_unwrapped_optional_type"></a>
## 隐式解析可选类型
@ -327,7 +327,7 @@ let arrayOfImplicitlyUnwrappedElements: [Int!] // 错误
let implicitlyUnwrappedArray: [Int]! // 正确
```
由于隐式解析可选类型和可选类型有同样的表达式`Optional<Wrapped>`,你可以在使用可选类型的地方使用隐式解析可选类型。比如,你可以将隐式解析可选类型的值赋给变量、常量和可选属性,反之亦然。
由于隐式解析可选类型和可选类型有同样的表达式 `Optional<Wrapped>`,你可以在使用可选类型的地方使用隐式解析可选类型。比如,你可以将隐式解析可选类型的值赋给变量、常量和可选属性,反之亦然。
正如可选类型一样,你在声明隐式解析可选类型的变量或属性的时候也不用指定初始值,因为它有默认值 `nil`
@ -335,10 +335,10 @@ let implicitlyUnwrappedArray: [Int]! // 正确
关于隐式解析可选类型的更多细节,请参阅 [隐式解析可选类型](../chapter2/01_The_Basics.html#implicityly_unwrapped_optionals)。
> 隐式解析可选类型语法
>
> 隐式解析可选类型语法
>
<a name="implicitly-unwrapped-optional-type"></a>
> *隐式解析可选类型* → [*类型*](#type) **!**
> *隐式解析可选类型* → [*类型*](#type) **!**
<a name="protocol_composition_type"></a>
## 协议合成类型
@ -349,11 +349,11 @@ let implicitlyUnwrappedArray: [Int]! // 正确
> `Protocol 1` & `Procotol 2`
协议合成类型允许你指定一个值,其类型符合多个协议的要求且不需要定义一个新的命名型协议来继承它想要符合的各个协议。比如,协议合成类型 `Protocol A & Protocol B & Protocol C` 等效于一个从 `Protocol A``Protocol B` `Protocol C` 继承而来的新协议 `Protocol D`,很显然这样做有效率的多,甚至不需引入一个新名字。
协议合成类型允许你指定一个值,其类型符合多个协议的要求且不需要定义一个新的命名型协议来继承它想要符合的各个协议。比如,协议合成类型 `Protocol A & Protocol B & Protocol C` 等效于一个从 `Protocol A``Protocol B``Protocol C` 继承而来的新协议 `Protocol D`,很显然这样做有效率的多,甚至不需引入一个新名字。
协议合成列表中的每项必须是协议名或协议合成类型的类型别名。
> 协议合成类型语法
> 协议合成类型语法
>
<a name="protocol-composition-type"></a>
> *协议合成类型* → [*协议标识符*](#protocol-identifier) & [*协议合成延续*](#protocol-composition-continuation)
@ -389,7 +389,7 @@ type(of: someInstance).printClassName()
// 打印 “SomeSubClass”
```
更多信息可以查看Swift标准库里的`type(of:)`
更多信息可以查看 Swift 标准库里的 `type(of:)`
可以使用初始化表达式从某个类型的元类型构造出一个该类型的实例。对于类实例,被调用的构造器必须使用 `required` 关键字标记,或者整个类使用 `final` 关键字标记。
@ -407,7 +407,7 @@ let metatype: AnotherSubClass.Type = AnotherSubClass.self
let anotherInstance = metatype.init(string: "some string")
```
> 元类型语法
> 元类型语法
>
<a name="metatype-type"></a>
> *元类型* → [*类型*](#type) **.** **Type** | [*类型*](#type) **.** **Protocol**
@ -423,12 +423,12 @@ let anotherInstance = metatype.init(string: "some string")
枚举定义中的类型继承子句可以是一系列协议,或是枚举的原始值类型的命名型类型。在枚举定义中使用类型继承子句来指定原始值类型的例子,请参阅 [原始值](../chapter2/08_Enumerations.html#raw_values)。
> 类型继承子句语法
>
> 类型继承子句语法
>
<a name="type_inheritance_clause"></a>
> *类型继承子句* → **:** [*类型继承列表*](#type-inheritance-list)
> *类型继承子句* → **:** [*类型继承列表*](#type-inheritance-list)
<a name="type-inheritance-list"></a>
> *类型继承列表* → [*类型标识符*](#type-identifier) | [*类型标识符*](#type-identifier) **,** [*类型继承列表*](#type-inheritance-list)
> *类型继承列表* → [*类型标识符*](#type-identifier) | [*类型标识符*](#type-identifier) **,** [*类型继承列表*](#type-inheritance-list)
<a name="class-requirement"></a>

View File

@ -15,9 +15,9 @@
> 校对:[175](https://github.com/Brian175)
> 3.0
> 翻译+校对:[chenmingjia](https://github.com/chenmingjia)
> 4.1
> 翻译+校对:[chenmingjia](https://github.com/chenmingjia)
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
本页包含内容:
@ -51,12 +51,12 @@ Swift 中存在四种表达式:前缀表达式,二元表达式,基本表
通过前缀表达式和二元表达式可以对简单表达式使用各种运算符。基本表达式从概念上讲是最简单的一种表达式,它是一种访问值的方式。后缀表达式则允许你建立复杂的表达式,例如函数调用和成员访问。每种表达式都在下面有详细论述。
> 表达式语法
>
> 表达式语法
>
<a name="expression"></a>
> *表达式* → [*try运算符*](#try-operator)<sub>可选</sub> [*前缀表达式*](#prefix-expression) [*二元表达式列表*](#binary-expressions)<sub>可选</sub>
> *表达式* → [*try 运算符*](#try-operator)<sub>可选</sub> [*前缀表达式*](#prefix-expression) [*二元表达式列表*](#binary-expressions)<sub>可选</sub>
<a name="expression-list"></a>
> *表达式列表* → [*表达式*](#expression) | [*表达式*](#expression) **,** [*表达式列表*](#expression-list)
> *表达式列表* → [*表达式*](#expression) | [*表达式*](#expression) **,** [*表达式列表*](#expression-list)
<a name="prefix_expressions"></a>
## 前缀表达式
@ -69,13 +69,13 @@ Swift 中存在四种表达式:前缀表达式,二元表达式,基本表
除了标准库运算符,你也可以对某个变量使用 `&` 运算符,从而将其传递给函数的输入输出参数。更多信息,请参阅 [输入输出参数](../chapter2/06_Functions.html#in_out_parameters)。
> 前缀表达式语法
>
> 前缀表达式语法
>
<a name="prefix-expression"></a>
> *前缀表达式* → [*前缀运算符*](02_Lexical_Structure.md#prefix-operator)<sub>可选</sub> [*后缀表达式*](#postfix-expression)
> *前缀表达式* → [*输入输出表达式*](#in-out-expression)
> *前缀表达式* → [*前缀运算符*](02_Lexical_Structure.md#prefix-operator)<sub>可选</sub> [*后缀表达式*](#postfix-expression)
> *前缀表达式* → [*输入输出表达式*](#in-out-expression)
<a name="in-out-expression"></a>
> *输入输出表达式* → **&** [*标识符*](02_Lexical_Structure.md#identifier)
> *输入输出表达式* → **&** [*标识符*](02_Lexical_Structure.md#identifier)
<a name="try_operator"></a>
### Try 运算符
@ -107,8 +107,8 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误try
`try` 表达式不能出现在二进制运算符的的右侧,除非二进制运算符是赋值运算符或者 `try` 表达式是被圆括号括起来的。
关于 `try``try?``try!` 的更多信息,以及该如何使用的例子,请参阅 [错误处理](../chapter2/18_Error_Handling.html)。
> Try 表达式语法
>
> Try 表达式语法
>
<a name="try-operator"></a>
> *try 运算符* → **try** | **try?** | **try!**
@ -123,17 +123,17 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误try
关于 Swift 标准库提供的运算符的更多信息,请参阅 [*Swift Standard Library Operators Reference*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。
> 注意
> 注意
>
> 在解析时,一个二元表达式将作为一个扁平列表表示,然后根据运算符的优先级,再进一步进行组合。例如,`2 + 3 * 5` 首先被看作具有五个元素的列表,即 `2`、`+`、`3`、`*`、`5`,随后根据运算符优先级组合为 `(2 + (3 * 5))`。
<a name="binary-expression"></a>
> 二元表达式语法
>
> *二元表达式* → [*二元运算符*](02_Lexical_Structure.md#binary-operator) [*前缀表达式*](#prefix-expression)
> *二元表达式* → [*赋值运算符*](#assignment-operator) [*try运算符*](#try-operator)<sub>可选</sub> [*前缀表达式*](#prefix-expression)
> *二元表达式* → [*条件运算符*](#conditional-operator) [*try运算符*](#try-operator)<sub>可选</sub> [*前缀表达式*](#prefix-expression)
> *二元表达式* → [*类型转换运算符*](#type-casting-operator)
> 二元表达式语法
>
> *二元表达式* → [*二元运算符*](02_Lexical_Structure.md#binary-operator) [*前缀表达式*](#prefix-expression)
> *二元表达式* → [*赋值运算符*](#assignment-operator) [*try 运算符*](#try-operator)<sub>可选</sub> [*前缀表达式*](#prefix-expression)
> *二元表达式* → [*条件运算符*](#conditional-operator) [*try 运算符*](#try-operator)<sub>可选</sub> [*前缀表达式*](#prefix-expression)
> *二元表达式* → [*类型转换运算符*](#type-casting-operator)
<a name="binary-expressions"></a>
> *二元表达式列表* → [*二元表达式*](#binary-expression) [*二元表达式列表*](#binary-expressions)<sub>可选</sub>
@ -153,10 +153,10 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误try
赋值运算符不返回任何值。
> 赋值运算符语法
>
> 赋值运算符语法
>
<a name="assignment-operator"></a>
> *赋值运算符* → **=**
> *赋值运算符* → **=**
<a name="ternary_conditional_operator"></a>
### 三元条件运算符
@ -169,20 +169,23 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误try
关于使用三元条件运算符的例子,请参阅 [三元条件运算符](../chapter2/02_Basic_Operators.md#ternary_conditional_operator)。
> 三元条件运算符语法
>
> 三元条件运算符语法
>
<a name="conditional-operator"></a>
> *三元条件运算符* → **?** [try运算符](#try-operator)<sub>可选</sub> [*表达式*](#expression) **:**
> *三元条件运算符* → **?** [try 运算符](#try-operator)<sub>可选</sub> [*表达式*](#expression) **:**
<a name="type-casting_operators"></a>
### 类型转换运算符
有 4 种类型转换运算符:`is``as``as? ``as!`。它们有如下的形式:
有 4 种类型转换运算符:`is``as``as? ``as!`。它们有如下的形式:
> `表达式` is `类型`
`表达式` as `类型`
`表达式` as? `类型`
`表达式` as! `类型`
> `表达式` is `类型`
>
> `表达式` as `类型`
>
> `表达式` as? `类型`
>
> `表达式` as! `类型`
`is` 运算符在运行时检查表达式能否向下转化为指定的类型,如果可以则返回 `ture`,否则返回 `false`
@ -212,29 +215,29 @@ f(x as Any)
关于类型转换的更多内容和例子,请参阅 [类型转换](../chapter2/19_Type_Casting.md)。
<a name="type-casting-operator"></a>
> 类型转换运算符语法
>
> *类型转换运算符* → **is** [*类型*](03_Types.md#type)
> *类型转换运算符* → **as** [*类型*](03_Types.md#type)
> *类型转换运算符* → **as** **?** [*类型*](03_Types.md#type)
> *类型转换运算符* → **as** **!** [*类型*](03_Types.md#type)
> 类型转换运算符语法
>
> *类型转换运算符* → **is** [*类型*](03_Types.md#type)
> *类型转换运算符* → **as** [*类型*](03_Types.md#type)
> *类型转换运算符* → **as** **?** [*类型*](03_Types.md#type)
> *类型转换运算符* → **as** **!** [*类型*](03_Types.md#type)
<a name="primary_expressions"></a>
## 基本表达式
基本表达式是最基本的表达式。它们可以单独使用,也可以跟前缀表达式、二元表达式、后缀表达式组合使用。
> 基本表达式语法
>
> 基本表达式语法
>
<a name="primary-expression"></a>
> *基本表达式* → [*标识符*](02_Lexical_Structure.md#identifier) [*泛型实参子句*](08_Generic_Parameters_and_Arguments.md#generic-argument-clause)<sub>可选</sub>
> *基本表达式* → [*字面量表达式*](#literal-expression)
> *基本表达式* → [*self表达式*](#self-expression)
> *基本表达式* → [*超类表达式*](#superclass-expression)
> *基本表达式* → [*闭包表达式*](#closure-expression)
> *基本表达式* → [*圆括号表达式*](#parenthesized-expression)
> *基本表达式* → [*隐式成员表达式*](#implicit-member-expression)
> *基本表达式* → [*通配符表达式*](#wildcard-expression)
> *基本表达式* → [*标识符*](02_Lexical_Structure.md#identifier) [*泛型实参子句*](08_Generic_Parameters_and_Arguments.md#generic-argument-clause)<sub>可选</sub>
> *基本表达式* → [*字面量表达式*](#literal-expression)
> *基本表达式* → [*self 表达式*](#self-expression)
> *基本表达式* → [*超类表达式*](#superclass-expression)
> *基本表达式* → [*闭包表达式*](#closure-expression)
> *基本表达式* → [*圆括号表达式*](#parenthesized-expression)
> *基本表达式* → [*隐式成员表达式*](#implicit-member-expression)
> *基本表达式* → [*通配符表达式*](#wildcard-expression)
> *基本表达式* → [*选择器表达式*](#selector-expression)
<a name="literal_expression"></a>
@ -249,7 +252,7 @@ f(x as Any)
`#column` | `Int` | 所在的列数
`#function` | `String` | 所在的声明的名字
`#line`除了上述含义外,还有另一种含义。当它出现在单独一行时,会被理解成行控制语句,请参阅[线路控制语句](../chapter3/10_Statements.md#线路控制语句)。
`#line` 除了上述含义外,还有另一种含义。当它出现在单独一行时,会被理解成行控制语句,请参阅[线路控制语句](../chapter3/10_Statements.md#线路控制语句)。
对于 `function`,在函数中会返回当前函数的名字,在方法中会返回当前方法的名字,在属性的存取器中会返回属性的名字,在特殊的成员如 `init``subscript` 中会返回这个关键字的名字,在某个文件中会返回当前模块的名字。
@ -285,36 +288,36 @@ var emptyArray: [Double] = []
var emptyDictionary: [String : Double] = [:]
```
> 字面量表达式语法
> 字面量表达式语法
>
<a name="literal-expression"></a>
> *字面量表达式* → [*字面量*](02_Lexical_Structure.md#literal)
> *字面量表达式* → [*数组字面量*](#array-literal) | [*字典字面量*](#dictionary-literal)
> *字面量表达式* → **#file** | **#line** | **#column** | **#function**
> *字面量表达式* → [*字面量*](02_Lexical_Structure.md#literal)
> *字面量表达式* → [*数组字面量*](#array-literal) | [*字典字面量*](#dictionary-literal)
> *字面量表达式* → **#file** | **#line** | **#column** | **#function**
<a name="array-literal"></a>
> *数组字面量* → **[** [*数组字面量项列表*](#array-literal-items)<sub>可选</sub> **]**
> *数组字面量* → **[** [*数组字面量项列表*](#array-literal-items)<sub>可选</sub> **]**
<a name="array-literal-items"></a>
> *数组字面量项列表* → [*数组字面量项*](#array-literal-item) **,**<sub>可选</sub> | [*数组字面量项*](#array-literal-item) **,** [*数组字面量项列表*](#array-literal-items)
> *数组字面量项列表* → [*数组字面量项*](#array-literal-item) **,**<sub>可选</sub> | [*数组字面量项*](#array-literal-item) **,** [*数组字面量项列表*](#array-literal-items)
<a name="array-literal-item"></a>
> *数组字面量项* → [*表达式*](#expression)
> *数组字面量项* → [*表达式*](#expression)
<a name="dictionary-literal"></a>
> *字典字面量* → **[** [*字典字面量项列表*](#dictionary-literal-items) **]** | **[** **:** **]**
> *字典字面量* → **[** [*字典字面量项列表*](#dictionary-literal-items) **]** | **[** **:** **]**
<a name="dictionary-literal-items"></a>
> *字典字面量项列表* → [*字典字面量项*](#dictionary-literal-item) **,**<sub>可选</sub> | [*字典字面量项*](#dictionary-literal-item) **,** [*字典字面量项列表*](#dictionary-literal-items)
> *字典字面量项列表* → [*字典字面量项*](#dictionary-literal-item) **,**<sub>可选</sub> | [*字典字面量项*](#dictionary-literal-item) **,** [*字典字面量项列表*](#dictionary-literal-items)
<a name="dictionary-literal-item"></a>
> *字典字面量项* → [*表达式*](#expression) **:** [*表达式*](#expression)
> *字典字面量项* → [*表达式*](#expression) **:** [*表达式*](#expression)
<a name="self_expression"></a>
### Self 表达式
`self` 表达式是对当前类型或者当前实例的显式引用,它有如下形式:
> self
> self.`成员名称`
> self[`下标索引`]
> self(`构造器参数`)
> self
> self.`成员名称`
> self[`下标索引`]
> self(`构造器参数`)
> self.init(`构造器参数`)
如果在构造器、下标、实例方法中,`self` 引用的是当前类型的实例。在一个类型方法中,`self` 引用的是当前的类型。
@ -341,42 +344,42 @@ struct Point {
}
```
> Self 表达式语法
> Self 表达式语法
>
<a name="self-expression"></a>
> *self 表达式* → **self** | [*self 方法表达式*](#self-method-expression) [*self 下标表达式*](#self-subscript-expression) | [*self 构造器表达式*](#self-initializer-expression)
>
<a name="self-method-expression"></a>
> *self 方法表达式* → **self** **.** [*标识符*](02_Lexical_Structure.md#identifier)
> *self 方法表达式* → **self** **.** [*标识符*](02_Lexical_Structure.md#identifier)
<a name="self-subscript-expression"></a>
> *self 下标表达式* → **self** **[** [*表达式*](#expression) **]**
<a name="self-initializer-expression"></a>
> *self 构造器表达式* → **self** **.** **init**
> *self 构造器表达式* → **self** **.** **init**
<a name="superclass_expression"></a>
### 父类表达式
*父类*表达式可以使我们在某个类中访问它的超类,它有如下形式:
> super.`成员名称`
> super[`下标索引`]
> super.`成员名称`
> super[`下标索引`]
> super.init(`构造器参数`)
第一种形式用来访问超类的某个成员,第二种形式用来访问超类的下标,第三种形式用来访问超类的构造器。
子类可以通过超类表达式在它们的成员、下标和构造器中使用超类中的实现。
> 父类表达式语法
>
> 父类表达式语法
>
<a name="superclass-expression"></a>
> *超类表达式* → [*超类方法表达式*](#superclass-method-expression) | [*超类下标表达式*](#superclass-subscript-expression) | [*超类构造器表达式*](#superclass-initializer-expression)
>
> *超类表达式* → [*超类方法表达式*](#superclass-method-expression) | [*超类下标表达式*](#superclass-subscript-expression) | [*超类构造器表达式*](#superclass-initializer-expression)
>
<a name="superclass-method-expression"></a>
> *超类方法表达式* → **super** **.** [*标识符*](02_Lexical_Structure.md#identifier)
> *超类方法表达式* → **super** **.** [*标识符*](02_Lexical_Structure.md#identifier)
<a name="superclass-subscript-expression"></a>
> *超类下标表达式* → **super** **[** [*表达式*](#expression) **]**
> *超类下标表达式* → **super** **[** [*表达式*](#expression) **]**
<a name="superclass-initializer-expression"></a>
> *超类构造器表达式* → **super** **.** **init**
> *超类构造器表达式* → **super** **.** **init**
<a name="closure_expression"></a>
### 闭包表达式
@ -384,8 +387,8 @@ struct Point {
闭包表达式会创建一个闭包,在其他语言中也叫 *lambda* 或*匿名*函数。跟函数一样,闭包包含了待执行的代码,不同的是闭包还会捕获所在环境中的常量和变量。它的形式如下:
```swift
{ (parameters) -> return type in
statements
{ (parameters) -> return type in
statements
}
```
@ -415,8 +418,8 @@ myFunction { return $0 + $1 }
myFunction { $0 + $1 }
```
关于如何将闭包作为参数来传递的内容,请参阅 [函数调用表达式](#function_call_expression)。
关于如何将闭包作为参数来传递的内容,请参阅 [函数调用表达式](#function_call_expression)。
关于逃逸闭包的内容,请参阅[逃逸闭包](./chapter2/07_Closures.md#escaping_closures)
#### 捕获列表
@ -477,26 +480,26 @@ myFunction { [weak parent = self.parent] in print(parent!.title) }
关于闭包表达式的更多信息和例子,请参阅 [闭包表达式](../chapter2/07_Closures.md#closure_expressions)。关于捕获列表的更多信息和例子,请参阅 [解决闭包引起的循环强引用](../chapter2/16_Automatic_Reference_Counting.md#resolving_strong_reference_cycles_for_closures)。
> 闭包表达式语法
> 闭包表达式语法
>
<a name="closure-expression"></a>
> *闭包表达式* → **{** [*闭包签名*](#closure-signature)<sub>可选</sub> [*语句*](10_Statements.md#statements) **}**
> *闭包表达式* → **{** [*闭包签名*](#closure-signature)<sub>可选</sub> [*语句*](10_Statements.md#statements) **}**
<a name="closure-signature"></a>
> *闭包签名* → [*参数子句*](05_Declarations.md#parameter-clause) [*函数结果*](05_Declarations.md#function-result)<sub>可选</sub> **in**
> *闭包签名* → [*标识符列表*](02_Lexical_Structure.md#identifier-list) [*函数结果*](05_Declarations.md#function-result)<sub>可选</sub> **in**
> *闭包签名* → [*捕获列表*](#capture-list) [*参数子句*](05_Declarations.md#parameter-clause) [*函数结果*](05_Declarations.md#function-result)<sub>可选</sub> **in**
> *闭包签名* → [*捕获列表*](#capture-list) [*标识符列表*](02_Lexical_Structure.md#identifier-list) [*函数结果*](05_Declarations.md#function-result)<sub>可选</sub> **in**
> *闭包签名* → [*捕获列表*](#capture-list) **in**
> *闭包签名* → [*参数子句*](05_Declarations.md#parameter-clause) [*函数结果*](05_Declarations.md#function-result)<sub>可选</sub> **in**
> *闭包签名* → [*标识符列表*](02_Lexical_Structure.md#identifier-list) [*函数结果*](05_Declarations.md#function-result)<sub>可选</sub> **in**
> *闭包签名* → [*捕获列表*](#capture-list) [*参数子句*](05_Declarations.md#parameter-clause) [*函数结果*](05_Declarations.md#function-result)<sub>可选</sub> **in**
> *闭包签名* → [*捕获列表*](#capture-list) [*标识符列表*](02_Lexical_Structure.md#identifier-list) [*函数结果*](05_Declarations.md#function-result)<sub>可选</sub> **in**
> *闭包签名* → [*捕获列表*](#capture-list) **in**
<a name="capture-list"></a>
> *捕获列表* → **[** [*捕获列表项列表*](#capture-list-items) **]**
> *捕获列表* → **[** [*捕获列表项列表*](#capture-list-items) **]**
<a name="capture-list-items"></a>
> *捕获列表项列表* → [*捕获列表项*](#capture-list-item) | [*捕获列表项*](#capture-list-item) **,** [*捕获列表项列表*](#capture-list-items)
<a name="capture-list-item"></a>
<a name="capture-list-item"></a>
> *捕获列表项* → [*捕获说明符*](#capture-specifier)<sub>可选</sub> [*表达式*](#expression)
<a name="capture-specifier"></a>
> *捕获说明符* → **weak** | **unowned** | **unowned(safe)** | **unowned(unsafe)**
<a name="capture-specifier"></a>
> *捕获说明符* → **weak** | **unowned** | **unowned(safe)** | **unowned(unsafe)**
<a name="implicit_member_expression"></a>
### 隐式成员表达式
@ -512,23 +515,23 @@ var x = MyEnumeration.SomeValue
x = .AnotherValue
```
> 隐式成员表达式语法
>
> 隐式成员表达式语法
>
<a name="implicit-member-expression"></a>
> *隐式成员表达式* → **.** [*标识符*](02_Lexical_Structure.md#identifier)
> *隐式成员表达式* → **.** [*标识符*](02_Lexical_Structure.md#identifier)
<a name="parenthesized_expression"></a>
### 圆括号表达式
*圆括号表达式*是由圆括号包围的表达式。你可以用圆括号说明成组的表达式的先后操作。成组的圆括号不会改变表达式的类型 - 例如`(1)`的类型就是简单的`Int`
> 圆括号表达式语法
*圆括号表达式*是由圆括号包围的表达式。你可以用圆括号说明成组的表达式的先后操作。成组的圆括号不会改变表达式的类型 - 例如 `(1)` 的类型就是简单的 `Int`
> 圆括号表达式语法
>
<a name="parenthesized-expression"></a>
> *圆括号表达式* → **( [*表达式*](#expression) )**
> *圆括号表达式* → **( [*表达式*](#expression) )**
<a name="Tuple_Expression"></a>
### 元组表达式
### 元组表达式
元组表达式由圆括号和其中多个逗号分隔的子表达式组成。每个子表达式前面可以有一个标识符,用冒号隔开。元组表达式形式如下:
@ -536,14 +539,14 @@ x = .AnotherValue
元组表达式可以一个表达式都没有,也可以包含两个或是更多的表达式。单个表达式用括号括起来就是括号表达式了。
> 元组表达式语法
> 元组表达式语法
>
<a name="tuple-expression"></a>
> *元组表达式* → **( )** | **(**[*元组元素*](#tuple-element) [*元组元素列表*](#tuple-element-list) **)**
<a name="tuple-element-list"></a>
<a name="tuple-element-list"></a>
> *元组元素列表* → [*元组元素*](#tuple-element) | [*元组元素*](#tuple-element) **,** [*元组元素列表*](#tuple-element-list)
<a name="tuple-element"></a>
> *元组元素* → [*表达式*](#expression) | [*标识符*](identifier) **:** [*表达式*](#expression)
<a name="tuple-element"></a>
> *元组元素* → [*表达式*](#expression) | [*标识符*](identifier) **:** [*表达式*](#expression)
<a name="wildcard_expression"></a>
### 通配符表达式
@ -555,15 +558,15 @@ x = .AnotherValue
// x 为 1020 被忽略
```
> 通配符表达式语法
>
> 通配符表达式语法
>
<a name="wildcard-expression"></a>
> *通配符表达式* → **_**
> *通配符表达式* → **_**
<a name="selector_expression"></a>
### 选择器表达式
选择器表达式可以让你通过选择器来引用在Objective-C中方法(method)和属性(property)的settergetter方法。
选择器表达式可以让你通过选择器来引用在 Objective-C 中方法(method)和属性(property)的 settergetter 方法。
> \#selector(方法名)
\#selector(getter: 属性名)
@ -584,9 +587,9 @@ class SomeClass: NSObject {
let selectorForMethod = #selector(SomeClass.doSomething(_:))
let selectorForPropertyGetter = #selector(getter: SomeClass.property)
```
当为属性的getter创建选择器时属性名可以是变量属性或者常量属性的引用。但是当为属性的setter创建选择器时属性名只可以是对变量属性的引用。
当为属性的 getter 创建选择器时,属性名可以是变量属性或者常量属性的引用。但是当为属性的 setter 创建选择器时,属性名只可以是对变量属性的引用。
方法名称可以包含圆括号来进行分组并使用as 操作符来区分具有相同方法名但类型不同的方法,例如:
方法名称可以包含圆括号来进行分组,并使用 as 操作符来区分具有相同方法名但类型不同的方法,例如:
```swift
extension SomeClass {
@ -598,15 +601,16 @@ let anotherSelector = #selector(SomeClass.doSomething(_:) as (SomeClass) -> (Str
由于选择器是在编译时创建的,因此编译器可以检查方法或者属性是否存在,以及是否在运行时暴露给了 Objective-C 。
> 注意
> 注意
>
> 虽然方法名或者属性名是个表达式,但是它不会被求值。
更多关于如何在 Swift 代码中使用选择器来与 Objective-C API 进行交互的信息,请参阅 [Using Swift with Cocoa and Objective-C (Swift 3)](https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[Objective-C Selectors](https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-ID59)部分。
> 选择器表达式语法
> 选择器表达式语法
<a name="selector-expression"></a>
> *选择器表达式* → __#selector__ **(** [*表达式*](#expression) **)**
> *选择器表达式* → __#selector__ **(** [*getter:表达式*](#expression) **)**
> *选择器表达式* → __#selector__ **(** [*表达式*](#expression) **)**
> *选择器表达式* → __#selector__ **(** [*getter:表达式*](#expression) **)**
> *选择器表达式* → __#selector__ **(** [*setter:表达式*](#expression) **)**
<a name="postfix_expressions"></a>
@ -618,17 +622,17 @@ let anotherSelector = #selector(SomeClass.doSomething(_:) as (SomeClass) -> (Str
关于 Swift 标准库提供的运算符的更多信息,请参阅 [*Swift Standard Library Operators Reference*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_StandardLibrary_Operators/index.html#//apple_ref/doc/uid/TP40016054)。
> 后缀表达式语法
> 后缀表达式语法
<a name="postfix-expression"></a>
> *后缀表达式* → [*基本表达式*](#primary-expression)
> *后缀表达式* → [*后缀表达式*](#postfix-expression) [*后缀运算符*](02_Lexical_Structure.md#postfix-operator)
> *后缀表达式* → [*函数调用表达式*](#function-call-expression)
> *后缀表达式* → [*构造器表达式*](#initializer-expression)
> *后缀表达式* → [*显式成员表达式*](#explicit-member-expression)
> *后缀表达式* → [*后缀 self 表达式*](#postfix-self-expression)
> *后缀表达式* → [*dynamicType 表达式*](#dynamic-type-expression)
> *后缀表达式* → [*下标表达式*](#subscript-expression)
> *后缀表达式* → [*强制取值表达式*](#forced-value-expression)
> *后缀表达式* → [*基本表达式*](#primary-expression)
> *后缀表达式* → [*后缀表达式*](#postfix-expression) [*后缀运算符*](02_Lexical_Structure.md#postfix-operator)
> *后缀表达式* → [*函数调用表达式*](#function-call-expression)
> *后缀表达式* → [*构造器表达式*](#initializer-expression)
> *后缀表达式* → [*显式成员表达式*](#explicit-member-expression)
> *后缀表达式* → [*后缀 self 表达式*](#postfix-self-expression)
> *后缀表达式* → [*dynamicType 表达式*](#dynamic-type-expression)
> *后缀表达式* → [*下标表达式*](#subscript-expression)
> *后缀表达式* → [*强制取值表达式*](#forced-value-expression)
> *后缀表达式* → [*可选链表达式*](#optional-chaining-expression)
<a name="function_call_expression"></a>
@ -660,12 +664,12 @@ myData.someMethod() {$0 == 13}
myData.someMethod {$0 == 13}
```
> 函数调用表达式语法
> 函数调用表达式语法
<a name="function-call-expression"></a>
> *函数调用表达式* → [*后缀表达式*](#postfix-expression) [*圆括号表达式*](#parenthesized-expression)
> *函数调用表达式* → [*后缀表达式*](#postfix-expression) [*圆括号表达式*](#parenthesized-expression)<sub>可选</sub> [*尾随闭包*](#trailing-closure)
> *函数调用表达式* → [*后缀表达式*](#postfix-expression) [*圆括号表达式*](#parenthesized-expression)
> *函数调用表达式* → [*后缀表达式*](#postfix-expression) [*圆括号表达式*](#parenthesized-expression)<sub>可选</sub> [*尾随闭包*](#trailing-closure)
<a name="trailing-closure"></a>
> *尾随闭包* → [*闭包表达式*](#closure-expression)
> *尾随闭包* → [*闭包表达式*](#closure-expression)
<a name="initializer_expression"></a>
### 构造器表达式
@ -705,9 +709,9 @@ let s4 = someValue.dynamicType(data: 5) // 错误
let s3 = someValue.dynamicType.init(data: 7) // 有效
```
> 构造器表达式语法
> 构造器表达式语法
<a name="initializer-expression"></a>
> *构造器表达式* → [*后缀表达式*](#postfix-expression) **.** **init**
> *构造器表达式* → [*后缀表达式*](#postfix-expression) **.** **init**
> *构造器表达式* → [*后缀表达式*](#postfix-expression) **.** **init** **(** [*参数名称*](#argument-names) **)**
<a name="explicit_member_expression"></a>
@ -765,9 +769,9 @@ let x = [10, 3, 20, 15, 4]
.map { $0 * 100 }
```
> 显式成员表达式语法
> 显式成员表达式语法
<a name="explicit-member-expression"></a>
> *显式成员表达式* → [*后缀表达式*](#postfix-expression) **.** [*十进制数字*](02_Lexical_Structure.md#decimal-digit)
> *显式成员表达式* → [*后缀表达式*](#postfix-expression) **.** [*十进制数字*](02_Lexical_Structure.md#decimal-digit)
> *显式成员表达式* → [*后缀表达式*](#postfix-expression) **.** [*标识符*](02_Lexical_Structure.md#identifier) [*泛型实参子句*](08_Generic_Parameters_and_Arguments.md#generic-argument-clause)<sub>可选</sub><br/>
> *显式成员表达式* → [*后缀表达式*](#postfix-expression) **.** [*标识符*](02_Lexical_Structure.md#identifier) **(** [*参数名称*](#argument-names) **)**
>
@ -781,16 +785,16 @@ let x = [10, 3, 20, 15, 4]
后缀 `self` 表达式由某个表达式或类型名紧跟 `.self` 组成,其形式如下:
> `表达式`.self
> `类型`.self
> `表达式`.self
> `类型`.self
第一种形式返回表达式的值。例如:`x.self` 返回 `x`
第二种形式返回相应的类型。我们可以用它来获取某个实例的类型作为一个值来使用。例如,`SomeClass.self` 会返回 `SomeClass` 类型本身,你可以将其传递给相应函数或者方法作为参数。
> 后缀 self 表达式语法
> 后缀 self 表达式语法
<a name="postfix-self-expression"></a>
> *后缀 self 表达式* → [*后缀表达式*](#postfix-expression) **.** **self**
> *后缀 self 表达式* → [*后缀表达式*](#postfix-expression) **.** **self**
<a name="dynamic_type_expression"></a>
### dynamicType 表达式
@ -819,9 +823,9 @@ type(of: someInstance).printClassName()
// 打印 “SomeSubClass”
```
> 动态类型表达式语法
> 动态类型表达式语法
<a name="dynamic-type-expression"></a>
> *动态类型表达式* → type(of:表达式) **.** **dynamicType**
> *动态类型表达式* → type(of:表达式) **.** **dynamicType**
<a name="subscript_expression"></a>
### 下标表达式
@ -834,9 +838,9 @@ type(of: someInstance).printClassName()
关于下标的声明,请参阅 [协议下标声明](05_Declarations.md#protocol_subscript_declaration)。
> 下标表达式语法
> 下标表达式语法
<a name="subscript-expression"></a>
> *下标表达式* → [*后缀表达式*](#postfix-expression) **[** [*表达式列表*](#expression-list) **]**
> *下标表达式* → [*后缀表达式*](#postfix-expression) **[** [*表达式列表*](#expression-list) **]**
<a name="forced-Value_expression"></a>
### 强制取值表达式
@ -859,9 +863,9 @@ someDictionary["a"]![0] = 100
// someDictionary 现在是 [b: [10, 20], a: [100, 2, 3]]
```
> 强制取值语法
> 强制取值语法
<a name="forced-value-expression"></a>
> *强制取值表达式* → [*后缀表达式*](#postfix-expression) **!**
> *强制取值表达式* → [*后缀表达式*](#postfix-expression) **!**
<a name="optional-chaining_expression"></a>
### 可选链表达式
@ -909,6 +913,6 @@ someDictionary["a"]?[0] = someFunctionWithSideEffects()
// someDictionary 现在是 ["b": [10, 20], "a": [42, 2, 3]]
```
> 可选链表达式语法
> 可选链表达式语法
<a name="optional-chaining-expression"></a>
> *可选链表达式* → [*后缀表达式*](#postfix-expression) **?**
> *可选链表达式* → [*后缀表达式*](#postfix-expression) **?**

View File

@ -13,9 +13,9 @@
> 翻译:[chenmingbiao](https://github.com/chenmingbiao)
> 3.0
> 翻译:[chenmingjia](https://github.com/chenmingjia)
> 4.1
> 翻译:[chenmingjia](https://github.com/chenmingjia)
> 4.1
> 翻译+校对:[mylittleswift](https://github.com/mylittleswift)
本页包含内容:
@ -48,19 +48,19 @@
是否将分号(`;`)添加到语句的末尾是可选的。但若要在同一行内写多条独立语句,则必须使用分号。
> 语句语法
> 语句语法
<a name="statement"></a>
> *语句* → [*表达式*](04_Expressions.md#expression) **;**<sub>可选</sub>
> *语句* → [*声明*](05_Declarations.md#declaration) **;**<sub>可选</sub>
> *语句* → [*循环语句*](#loop-statement) **;**<sub>可选</sub>
> *语句* → [*分支语句*](#branch-statement) **;**<sub>可选</sub>
> *语句* → [*带标签的语句*](#labeled-statement) **;**<sub>可选</sub>
> *语句* → [*控制转移语句*](#control-transfer-statement) **;**<sub>可选</sub>
> *语句* → [*defer 语句*](#defer-statement) **;**<sub>可选</sub>
> *语句* → [*do 语句*](#do-statement) **:**<sub>可选</sub>
> *语句* → [*编译器控制语句*](#compiler-control-statement)
> *语句* → [*表达式*](04_Expressions.md#expression) **;**<sub>可选</sub>
> *语句* → [*声明*](05_Declarations.md#declaration) **;**<sub>可选</sub>
> *语句* → [*循环语句*](#loop-statement) **;**<sub>可选</sub>
> *语句* → [*分支语句*](#branch-statement) **;**<sub>可选</sub>
> *语句* → [*带标签的语句*](#labeled-statement) **;**<sub>可选</sub>
> *语句* → [*控制转移语句*](#control-transfer-statement) **;**<sub>可选</sub>
> *语句* → [*defer 语句*](#defer-statement) **;**<sub>可选</sub>
> *语句* → [*do 语句*](#do-statement) **:**<sub>可选</sub>
> *语句* → [*编译器控制语句*](#compiler-control-statement)
<a name="statements"></a>
> *多条语句* → [*语句*](#statement) [*多条语句*](#statements)<sub>可选</sub>
> *多条语句* → [*语句*](#statement) [*多条语句*](#statements)<sub>可选</sub>
<a name="loop_statements"></a>
## 循环语句
@ -69,11 +69,11 @@
通过 `break` 语句和 `continue` 语句可以改变循环语句的控制流。有关这两条语句,详情参见 [Break 语句](#break_statement) 和 [Continue 语句](#continue_statement)。
> 循环语句语法
> 循环语句语法
<a name="loop-statement"></a>
> *循环语句* → [*for-in 语句*](#for-in-statement)
> *循环语句* → [*while 语句*](#while-statement)
> *循环语句* → [*repeat-while 语句*](#repeat-while-statement)
> *循环语句* → [*for-in 语句*](#for-in-statement)
> *循环语句* → [*while 语句*](#while-statement)
> *循环语句* → [*repeat-while 语句*](#repeat-while-statement)
<a name="for-in_statements"></a>
### For-In 语句
@ -83,17 +83,17 @@
`for-in` 语句的形式如下:
```swift
for item in collection {
statements
}
for item in collection {
statements
}
```
`for-in` 语句在循环开始前会调用集合表达式的 `generate()` 方法来获取一个实现了 `GeneratorType` 协议的类型的值。接下来循环开始,反复调用该值的 `next()` 方法。如果其返回值不是 `None`,它将会被赋给“项”,然后执行循环体语句,执行完毕后回到循环开始处,继续重复这一过程;否则,既不会赋值也不会执行循环体语句,`for-in` 语句至此执行完毕。
> for-in 语句语法
>
> for-in 语句语法
>
<a name="for-in-statement"></a>
> *for-in 语句* → **for** **case**<sub>可选</sub> [*模式*](07_Patterns.md#pattern) **in** [*表达式*](04_Expressions.md#expression) [*where子句*](#where-clause)<sub>可选</sub> [*代码块*](05_Declarations.md#code-block)
> *for-in 语句* → **for** **case**<sub>可选</sub> [*模式*](07_Patterns.md#pattern) **in** [*表达式*](04_Expressions.md#expression) [*where 子句*](#where-clause)<sub>可选</sub> [*代码块*](05_Declarations.md#code-block)
<a name="while_statements"></a>
### While 语句
@ -103,9 +103,9 @@ for item in collection {
`while` 语句的形式如下:
```swift
while condition {
while condition {
statements
}
}
```
`while` 语句的执行流程如下:
@ -115,21 +115,21 @@ while condition {
由于会在执行循环体中的语句前判断条件的值,因此循环体中的语句可能会被执行若干次,也可能一次也不会被执行。
条件的结果必须是Bool类型或者Bool的桥接类型。另外条件语句也可以使用可选绑定请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。
条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。
> while 语句语法
> while 语句语法
>
<a name="while-statement"></a>
> *while 语句* → **while** [*条件子句*](#condition-clause) [*代码块*](05_Declarations.md#code-block)
> *while 语句* → **while** [*条件子句*](#condition-clause) [*代码块*](05_Declarations.md#code-block)
<a name="condition-clause"></a>
> *条件子句* → [*表达式*](04_Expressions.md#expression) | [*表达式*](04_Expressions.md#expression) **,** [*条件列表*](#condition-list)
> *条件子句* → [*表达式*](04_Expressions.md#expression) | [*表达式*](04_Expressions.md#expression) **,** [*条件列表*](#condition-list)
<a name="condition"></a>
> *条件* → [*表达式*](04_Expressions.md#expression) |[*可用性条件*](#availability-condition) | [*case条件*](#case-condition) | [*可选绑定条件*](#optional-binding-condition)
> *条件* → [*表达式*](04_Expressions.md#expression) |[*可用性条件*](#availability-condition) | [*case 条件*](#case-condition) | [*可选绑定条件*](#optional-binding-condition)
<a name="case-condition"></a>
> *case 条件* → **case** [*模式*](07_Patterns.md#pattern) [*构造器*](05_Declarations.md#initializer)
> *case 条件* → **case** [*模式*](07_Patterns.md#pattern) [*构造器*](05_Declarations.md#initializer)
<a name="optional-binding-condition"></a>
> *可选绑定条件* → **let** [*模式*](07_Patterns.md#pattern) [*构造器*](05_Declarations.md#initializer) | **var** [*模式*](07_Patterns.md#pattern) [*构造器*](05_Declarations.md#initializer)
> *可选绑定条件* → **let** [*模式*](07_Patterns.md#pattern) [*构造器*](05_Declarations.md#initializer) | **var** [*模式*](07_Patterns.md#pattern) [*构造器*](05_Declarations.md#initializer)
<a name="repeat-while_statements"></a>
### Repeat-While 语句
@ -139,8 +139,8 @@ while condition {
`repeat-while` 语句的形式如下:
```swift
repeat {
statements
repeat {
statements
} while condition
```
@ -151,12 +151,12 @@ repeat {
由于条件的值是在循环体中的语句执行后才进行判断,因此循环体中的语句至少会被执行一次。
条件的结果必须是Bool类型或者Bool的桥接类型。另外条件语句也可以使用可选绑定请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。
条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。
> repeat-while 语句语法
>
> repeat-while 语句语法
>
<a name="repeat-while-statement"></a>
> *repeat-while 语句* → **repeat** [*代码块*](05_Declarations.md#code-block) **while** [*表达式*](04_Expressions.md#expression)
> *repeat-while 语句* → **repeat** [*代码块*](05_Declarations.md#code-block) **while** [*表达式*](04_Expressions.md#expression)
<a name="branch_statements"></a>
## 分支语句
@ -165,12 +165,12 @@ repeat {
`if` 语句和 `switch` 语句中的控制流可以用 `break` 语句改变,请参阅 [Break 语句](#break_statement)。
> 分支语句语法
> 分支语句语法
>
<a name="branch-statement"></a>
> *分支语句* → [*if 语句*](#if-statement)
> *分支语句* → [*guard 语句*](#guard-statement)
> *分支语句* → [*switch 语句*](#switch-statement)
> *分支语句* → [*if 语句*](#if-statement)
> *分支语句* → [*guard 语句*](#guard-statement)
> *分支语句* → [*switch 语句*](#switch-statement)
<a name="if_statements"></a>
### If 语句
@ -182,17 +182,17 @@ repeat {
第一种形式是当且仅当条件为真时执行代码,像下面这样:
```swift
if condition {
statements
}
if condition {
statements
}
```
第二种形式是在第一种形式的基础上添加 `else` 语句,当只有一个 `else` 语句时,像下面这样:
```swift
if ondition {
statements to execute if condition is true
} else {
if ondition {
statements to execute if condition is true
} else {
statements to execute if condition is false
}
```
@ -200,52 +200,52 @@ if ondition {
`else` 语句也可包含 `if` 语句,从而形成一条链来测试更多的条件,像下面这样:
```swift
if condition 1 {
statements to execute if condition 1 is true
} else if condition 2 {
statements to execute if condition 2 is true
} else {
statements to execute if both conditions are false
if condition 1 {
statements to execute if condition 1 is true
} else if condition 2 {
statements to execute if condition 2 is true
} else {
statements to execute if both conditions are false
}
```
`if` 语句中条件的结果必须是Bool类型或者Bool的桥接类型。另外条件语句也可以使用可选绑定请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。
`if` 语句中条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。
> if 语句语法
> if 语句语法
>
<a name="if-statement"></a>
> *if 语句* → **if** [*条件子句*](#condition-clause) [*代码块*](05_Declarations.md#code-block) [*else子句*](#else-clause)<sub>可选</sub>
> *if 语句* → **if** [*条件子句*](#condition-clause) [*代码块*](05_Declarations.md#code-block) [*else 子句*](#else-clause)<sub>可选</sub>
<a name="else-clause"></a>
> *else 子句* → **else** [*代码块*](05_Declarations.md#code-block) | **else** [*if语句*](#if-statement)
> *else 子句* → **else** [*代码块*](05_Declarations.md#code-block) | **else** [*if 语句*](#if-statement)
<a name="guard_statements"></a>
### Guard 语句
### Guard 语句
如果一个或者多个条件不成立,可用 `guard` 语句用来退出当前作用域。
`guard` 语句的格式如下:
```swift
guard condition else {
guard condition else {
statements
}
}
```
`guard` 语句中条件的结果必须是Bool类型或者Bool的桥接类型。另外条件也可以是一条可选绑定请参阅 [可选绑定](../chapter2/01_The_Basics.html#optional_binding)。
`guard` 语句中条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件也可以是一条可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.html#optional_binding)。
`guard` 语句中进行可选绑定的常量或者变量,其可用范围从声明开始直到作用域结束。
`guard` 语句必须有 `else` 子句,而且必须在该子句中调用 `Never` 返回类型的函数,或者使用下面的语句退出当前作用域:
`guard` 语句必须有 `else` 子句,而且必须在该子句中调用 `Never` 返回类型的函数,或者使用下面的语句退出当前作用域:
* `return`
* `break`
* `continue`
* `throw`
* `throw`
关于控制转移语句,请参阅 [控制转移语句](#control_transfer_statements)。关于`Never`返回类型的函数,请参阅 [永不返回的函数](05_Declarations.md#rethrowing_functions_and_methods)。
关于控制转移语句,请参阅 [控制转移语句](#control_transfer_statements)。关于 `Never` 返回类型的函数,请参阅 [永不返回的函数](05_Declarations.md#rethrowing_functions_and_methods)。
> guard 语句语法
>
> guard 语句语法
>
<a name="guard-statement"></a>
> *guard 语句* → **guard** [*条件子句*](#condition-clause) **else** [*代码块*](05_Declarations.html#code-block)
@ -257,16 +257,16 @@ guard condition else {
`switch` 语句的形式如下:
```swift
switch control expression {
case pattern 1:
statements
case pattern 2 where condition:
statements
case pattern 3 where condition,
pattern 4 where condition:
statements
default:
statements
switch control expression {
case pattern 1:
statements
case pattern 2 where condition:
statements
case pattern 3 where condition,
pattern 4 where condition:
statements
default:
statements
}
```
@ -294,26 +294,26 @@ case let (x, y) where x == y:
当匹配到的 `case` 中的代码执行完毕后,`switch` 语句会直接退出,而不会继续执行下一个 `case` 。这就意味着,如果你想执行下一个 `case`,需要显式地在当前 `case` 中使用 `fallthrough` 语句。关于 `fallthrough` 语句的更多信息,请参阅 [Fallthrough 语句](#fallthrough_statements)。
> switch 语句语法
> switch 语句语法
>
<a name="switch-statement"></a>
> *switch 语句* → **switch** [*表达式*](04_Expressions.md#expression) **{** [*switch-case列表*](#switch-cases)<sub>可选</sub> **}**
> *switch 语句* → **switch** [*表达式*](04_Expressions.md#expression) **{** [*switch-case 列表*](#switch-cases)<sub>可选</sub> **}**
<a name="switch-cases"></a>
> *switch case 列表* → [*switch-case*](#switch-case) [*switch-case列表*](#switch-cases)<sub>可选</sub>
> *switch case 列表* → [*switch-case*](#switch-case) [*switch-case 列表*](#switch-cases)<sub>可选</sub>
<a name="switch-case"></a>
> *switch case* → [*case标签*](#case-label) [*多条语句*](#statements) | [*default标签*](#default-label) [*多条语句*](#statements)
> *switch case* → [*case 标签*](#case-label) [*多条语句*](#statements) | [*default 标签*](#default-label) [*多条语句*](#statements)
<a name="case-label"></a>
> *case 标签* → **case** [*case项列表*](#case-item-list) **:**
> *case 标签* → **case** [*case 项列表*](#case-item-list) **:**
<a name="case-item-list"></a>
> *case 项列表* → [*模式*](07_Patterns.md#pattern) [*where子句*](#where-clause)<sub>可选</sub> | [*模式*](07_Patterns.md#pattern) [*where子句*](#where-clause)<sub>可选</sub> **,** [*case项列表*](#case-item-list)
> *case 项列表* → [*模式*](07_Patterns.md#pattern) [*where 子句*](#where-clause)<sub>可选</sub> | [*模式*](07_Patterns.md#pattern) [*where 子句*](#where-clause)<sub>可选</sub> **,** [*case 项列表*](#case-item-list)
<a name="default-label"></a>
> *default 标签* → **default** **:**
> *default 标签* → **default** **:**
<a name="where-clause"></a>
> *where-clause* → **where** [*where表达式*](#where-expression)
> *where-clause* → **where** [*where 表达式*](#where-expression)
<a name="where-expression"></a>
> *where-expression* → [*表达式*](04_Expressions.md#expression)
> *where-expression* → [*表达式*](04_Expressions.md#expression)
<a name="labeled_statements"></a>
## 带标签的语句
@ -324,35 +324,35 @@ case let (x, y) where x == y:
关于使用带标签的语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章中的 [带标签的语句](../chapter2/05_Control_Flow.md#labeled_statements)。
> 带标签的语句语法
>
> 带标签的语句语法
>
<a name="labeled-statement"></a>
> *带标签的语句* → [*语句标签*](#statement-label) [*循环语句*](#loop-statement) | [*语句标签*](#statement-label) [*if语句*](#if-statement) | [*语句标签*](#statement-label) [*switch语句*](#switch-statement)
> *带标签的语句* → [*语句标签*](#statement-label) [*循环语句*](#loop-statement) | [*语句标签*](#statement-label) [*if 语句*](#if-statement) | [*语句标签*](#statement-label) [*switch 语句*](#switch-statement)
<a name="statement-label"></a>
> *语句标签* → [*标签名称*](#label-name) **:**
> *语句标签* → [*标签名称*](#label-name) **:**
<a name="label-name"></a>
> *标签名称* → [*标识符*](02_Lexical_Structure.md#identifier)
> *标签名称* → [*标识符*](02_Lexical_Structure.md#identifier)
<a name="control_transfer_statements"></a>
## 控制转移语句
控制转移语句能够无条件地把控制权从一片代码转移到另一片代码从而改变代码执行的顺序。Swift 提供五种类型的控制转移语句:`break` 语句、`continue` 语句、`fallthrough` 语句、`return` 语句和 `throw` 语句。
> 控制转移语句语法
> 控制转移语句语法
>
<a name="control-transfer-statement"></a>
> *控制转移语句* → [*break 语句*](#break-statement)
> *控制转移语句* → [*continue 语句*](#continue-statement)
> *控制转移语句* → [*fallthrough 语句*](#fallthrough-statement)
> *控制转移语句* → [*return 语句*](#return-statement)
> *控制转移语句* → [*throw 语句*](#throw-statement)
> *控制转移语句* → [*break 语句*](#break-statement)
> *控制转移语句* → [*continue 语句*](#continue-statement)
> *控制转移语句* → [*fallthrough 语句*](#fallthrough-statement)
> *控制转移语句* → [*return 语句*](#return-statement)
> *控制转移语句* → [*throw 语句*](#throw-statement)
<a name="break_statement"></a>
### Break 语句
`break` 语句用于终止循环语句、`if` 语句或 `switch` 语句的执行。使用 `break` 语句时,可以只写 `break` 这个关键词,也可以在 `break` 后面跟上标签名,像下面这样:
> break
> break
> break `label name`
`break` 语句后面带标签名时,可用于终止由这个标签标记的循环语句、`if` 语句或 `switch` 语句的执行。
@ -363,8 +363,8 @@ case let (x, y) where x == y:
关于使用 `break` 语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章的 [Break](../chapter2/05_Control_Flow.md#break) 和 [带标签的语句](../chapter2/05_Control_Flow.md#labeled_statements)。
> break 语句语法
>
> break 语句语法
>
<a name="break-statement"></a>
> *break 语句* → **break** [*标签名称*](#label-name)<sub>可选</sub>
@ -373,8 +373,8 @@ case let (x, y) where x == y:
`continue` 语句用于终止循环中当前迭代的执行,但不会终止该循环的执行。使用 `continue` 语句时,可以只写 `continue` 这个关键词,也可以在 `continue` 后面跟上标签名,像下面这样:
> continue
> continue `label name`
> continue
> continue `label name`
`continue` 语句后面带标签名时,可用于终止由这个标签标记的循环中当前迭代的执行。
@ -386,8 +386,8 @@ case let (x, y) where x == y:
关于使用 `continue` 语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章的 [Continue](../chapter2/05_Control_Flow.md#continue) 和 [带标签的语句](../chapter2/05_Control_Flow.md#labeled_statements)。
> continue 语句语法
>
> continue 语句语法
>
<a name="continue-statement"></a>
> *continue 语句* → **continue** [*标签名称*](#label-name)<sub>可选</sub>
@ -400,10 +400,10 @@ case let (x, y) where x == y:
关于在 `switch` 语句中使用 `fallthrough` 语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章的 [控制转移语句](../chapter2/05_Control_Flow.md#control_transfer_statements)。
> fallthrough 语句语法
>
> fallthrough 语句语法
>
<a name="fallthrough-statement"></a>
> *fallthrough 语句* → **fallthrough**
> *fallthrough 语句* → **fallthrough**
<a name="return_statements"></a>
### Return 语句
@ -412,46 +412,46 @@ case let (x, y) where x == y:
使用 `return` 语句时,可以只写 `return` 这个关键词,也可以在 `return` 后面跟上表达式,像下面这样:
> return
> return `expression`
> return
> return `expression`
`return` 语句后面带表达式时表达式的值将会返回给调用函数或方法。如果表达式的值的类型与函数或者方法声明的返回类型不匹配Swift 则会在返回表达式的值之前将表达式的值的类型转换为返回类型。
> 注意
>
> 注意
>
> 正如 [可失败构造器](05_Declarations.md#failable_initializers) 中所描述的,`return nil` 在可失败构造器中用于表明构造失败。
而只写 `return` 时,仅仅是从该函数或方法中返回,而不返回任何值(也就是说,函数或方法的返回类型为 `Void` 或者说 `()`)。
> return 语句语法
>
> return 语句语法
>
<a name="return-statement"></a>
> *return 语句* → **return** [*表达式*](04_Expressions.html#expression)<sub>可选</sub>
<a name="throw_statements"></a>
### Throw 语句
### Throw 语句
`throw` 语句出现在抛出函数或者抛出方法体内,或者类型被 `throws` 关键字标记的闭包表达式体内。
`throw` 语句出现在抛出函数或者抛出方法体内,或者类型被 `throws` 关键字标记的闭包表达式体内。
`throw` 语句使程序在当前作用域结束执行,并向外围作用域传播错误。抛出的错误会一直传递,直到被 `do` 语句的 `catch` 子句处理掉。
`throw` 语句使程序在当前作用域结束执行,并向外围作用域传播错误。抛出的错误会一直传递,直到被 `do` 语句的 `catch` 子句处理掉。
`throw` 语句由 `throw` 关键字紧跟一个表达式组成,如下所示:
> throw `expression`
> throw `expression`
表达式的结果必须符合 `ErrorType` 协议。
关于如何使用 `throw` 语句的例子,请参阅 [错误处理](../chapter2/18_Error_Handling.md) 一章的 [用 throwing 函数传递错误](../chapter2/18_Error_Handling.md#propagating_errors_using_throwing_functions)。
关于如何使用 `throw` 语句的例子,请参阅 [错误处理](../chapter2/18_Error_Handling.md) 一章的 [用 throwing 函数传递错误](../chapter2/18_Error_Handling.md#propagating_errors_using_throwing_functions)。
> throw 语句语法
>
> throw 语句语法
>
<a name="throw-statement"></a>
> *throw 语句* → **throw** [*表达式*](04_Expressions.md#expression)
<a name="defer_statements"></a>
## Defer 语句
`defer` 语句用于在退出当前作用域之前执行代码。
`defer` 语句用于在退出当前作用域之前执行代码。
`defer` 语句形式如下:
@ -461,9 +461,9 @@ defer {
}
```
`defer` 语句中的语句无论程序控制如何转移都会被执行。在某些情况下,例如,手动管理资源时,比如关闭文件描述符,或者即使抛出了错误也需要执行一些操作时,就可以使用 `defer` 语句。
`defer` 语句中的语句无论程序控制如何转移都会被执行。在某些情况下,例如,手动管理资源时,比如关闭文件描述符,或者即使抛出了错误也需要执行一些操作时,就可以使用 `defer` 语句。
如果多个 `defer` 语句出现在同一作用域内,那么它们执行的顺序与出现的顺序相反。给定作用域中的第一个 `defer` 语句,会在最后执行,这意味着代码中最靠后的 `defer` 语句中引用的资源可以被其他 `defer` 语句清理掉。
如果多个 `defer` 语句出现在同一作用域内,那么它们执行的顺序与出现的顺序相反。给定作用域中的第一个 `defer` 语句,会在最后执行,这意味着代码中最靠后的 `defer` 语句中引用的资源可以被其他 `defer` 语句清理掉。
```swift
func f() {
@ -479,55 +479,55 @@ f()
`defer` 语句中的语句无法将控制权转移到 `defer` 语句外部。
> defer 语句语法
>
> defer 语句语法
>
<a name="defer-statement"></a>
> *延迟语句* → **defer** [*代码块*](05_Declarations.md#code-block)
<a name="do_statements"></a>
## Do 语句
`do` 语句用于引入一个新的作用域,该作用域中可以含有一个或多个 `catch` 子句,`catch` 子句中定义了一些匹配错误条件的模式。`do` 语句作用域内定义的常量和变量只能在 `do` 语句作用域内使用。
`do` 语句用于引入一个新的作用域,该作用域中可以含有一个或多个 `catch` 子句,`catch` 子句中定义了一些匹配错误条件的模式。`do` 语句作用域内定义的常量和变量只能在 `do` 语句作用域内使用。
Swift 中的 `do` 语句与 C 中限定代码块界限的大括号(`{}`)很相似,也并不会降低程序运行时的性能。
Swift 中的 `do` 语句与 C 中限定代码块界限的大括号(`{}`)很相似,也并不会降低程序运行时的性能。
`do` 语句的形式如下:
```swift
do {
try expression
statements
} catch pattern 1 {
statements
} catch pattern 2 where condition {
statements
do {
try expression
statements
} catch pattern 1 {
statements
} catch pattern 2 where condition {
statements
}
```
如同 `switch` 语句,编译器会判断 `catch` 子句是否有遗漏。如果 `catch` 子句没有遗漏,则认为错误已被处理。否则,错误会自动传递到外围作用域,被某个 `catch` 子句处理掉或者被用 `throws` 关键字声明的抛出函数继续向外抛出。
如同 `switch` 语句,编译器会判断 `catch` 子句是否有遗漏。如果 `catch` 子句没有遗漏,则认为错误已被处理。否则,错误会自动传递到外围作用域,被某个 `catch` 子句处理掉或者被用 `throws` 关键字声明的抛出函数继续向外抛出。
为了确保错误已经被处理,可以让 `catch` 子句使用匹配所有错误的模式,如通配符模式(`_`)。如果一个 `catch` 子句不指定一种具体模式,`catch` 子句会匹配任何错误,并绑定到名为 `error` 的局部常量。有关在 `catch` 子句中使用模式的更多信息,请参阅 [模式](07_Patterns.md)。
关于如何在 `do` 语句中使用一系列 `catch` 子句的例子,请参阅 [错误处理](../chapter2/18_Error_Handling.md#handling_errors)。
关于如何在 `do` 语句中使用一系列 `catch` 子句的例子,请参阅 [错误处理](../chapter2/18_Error_Handling.md#handling_errors)。
> do 语句语法
>
> do 语句语法
>
<a name="do-statement"></a>
> *do 语句* → **do** [*代码块*](05_Declarations.md#code-block) [*多条 catch子句*](#catch-clauses)<sub>可选</sub>
> *do 语句* → **do** [*代码块*](05_Declarations.md#code-block) [*多条 catch 子句*](#catch-clauses)<sub>可选</sub>
<a name="catch-clauses"></a>
> *多条 catch 子句* → [*catch子句*](#catch-clause) [*多条 catch子句*](#catch-clauses)<sub>可选</sub>
> *多条 catch 子句* → [*catch 子句*](#catch-clause) [*多条 catch 子句*](#catch-clauses)<sub>可选</sub>
<a name="catch-clause"></a>
> *catch 子句* → **catch** [*模式*](07_Patterns.md#pattern)<sub>可选</sub> [*where子句*](#where-clause)<sub>可选</sub> [*代码块*](05_Declarations.md#code-block)
> *catch 子句* → **catch** [*模式*](07_Patterns.md#pattern)<sub>可选</sub> [*where 子句*](#where-clause)<sub>可选</sub> [*代码块*](05_Declarations.md#code-block)
<a name="compiler_control_statements"></a>
## 编译器控制语句
编译器控制语句允许程序改变编译器的行为。Swift 有两种编译器控制语句:编译配置语句和线路控制语句。
> 编译器控制语句语法
>
> 编译器控制语句语法
>
<a name="compiler-control-statement"></a>
> *编译器控制语句* → [*编译配置语句*](#build-config-statement)
> *编译器控制语句* → [*编译配置语句*](#build-config-statement)
> *编译器控制语句* → [*线路控制语句*](#line-control-statement)
<a name="build_config_statements"></a>
@ -538,8 +538,8 @@ do {
每一个编译配置语句都以 `#if` 开始,`#endif` 结束。如下是一个简单的编译配置语句:
```swift
#if compilation condition
statements
#if compilation condition
statements
#endif
```
@ -555,7 +555,7 @@ statements
`swift()`(语言版本检测函数)的版本号参数主要由主版本号和次版本号组成并且使用点号(`.`)分隔开,`>=` 和版本号之间不能有空格。
> 注意
> 注意
>
> `arch(arm)` 平台检测函数在 ARM 64 位设备上不会返回 `true`。如果代码在 32 位的 iOS 模拟器上编译,`arch(i386)` 平台检测函数会返回 `true`。
@ -564,50 +564,50 @@ statements
就像 `if` 语句一样,你可以使用 `#elseif` 子句来添加任意多个条件分支来测试不同的编译配置。你也可以使用 `#else` 子句来添加最终的条件分支。包含多个分支的编译配置语句例子如下:
```swift
#if compilation condition 1
statements to compile if compilation condition 1 is true
#elseif compilation condition 2
statements to compile if compilation condition 2 is true
#else
statements to compile if both compilation conditions are false
#if compilation condition 1
statements to compile if compilation condition 1 is true
#elseif compilation condition 2
statements to compile if compilation condition 2 is true
#else
statements to compile if both compilation conditions are false
#endif
```
> 注意
> 注意
>
> 即使没有被编译,编译配置中的语句仍然会被解析。然而,唯一的例外是编译配置语句中包含语言版本检测函数:仅当 `Swift` 编译器版本和语言版本检测函数中指定的版本号匹配时,语句才会被解析。这种设定能确保旧的编译器不会尝试去解析新 Swift 版本的语法。
<a name="build-config-statement"></a>
> 编译配置语句语法
> 编译配置语句语法
>
<a name="build-configuration-statement"></a>
> *单个编译配置语句* → **#if** [*编译配置*](#build-configuration) [*语句*](#statements)<sub>可选</sub> [*多个编译配置elseif子句*](#build-configuration-elseif-clauses)<sub>可选</sub> **-** [*单个编译配置else子句*](#build-configuration-else-clause)<sub>可选</sub> **#endif**
> *单个编译配置语句* → **#if** [*编译配置*](#build-configuration) [*语句*](#statements)<sub>可选</sub> [*多个编译配置 elseif 子句*](#build-configuration-elseif-clauses)<sub>可选</sub> **-** [*单个编译配置 else 子句*](#build-configuration-else-clause)<sub>可选</sub> **#endif**
<a name="build-configuration-elseif-clauses"></a>
> *多个编译配置 elseif 子句* → [*单个编译配置elseif子句*](#build-configuration-elseif-clause) [*多个编译配置elseif子句*](build-configuration-elseif-clauses)<sub>可选</sub>
> *多个编译配置 elseif 子句* → [*单个编译配置 elseif 子句*](#build-configuration-elseif-clause) [*多个编译配置 elseif 子句*](build-configuration-elseif-clauses)<sub>可选</sub>
<a name="build-configuration-elseif-clause"></a>
> *单个编译配置 elseif 子句* → **#elseif** [*编译配置*](#build-configuration) [*语句*](#statements)<sub>可选</sub>
> *单个编译配置 elseif 子句* → **#elseif** [*编译配置*](#build-configuration) [*语句*](#statements)<sub>可选</sub>
<a name="build-configuration-else-clause"></a>
> *单个编译配置 else 子句* → **#else** [*语句*](#statements)<sub>可选</sub>
<a name="build-configuration"></a>
> *编译配置* → [*平台检测函数*](#platform-testing-function)
> *编译配置* → [*语言版本检测函数*](#language-version-testing-function)
> *编译配置* → [*标识符*](02_Lexical_Structure.md#identifier)
> *编译配置* → [*布尔值字面量*](02_Lexical_Structure.md#boolean-literal)
> *编译配置* → **(** [*编译配置*](#build-configuration) **)**
> *编译配置* → **!** [*编译配置*](#build-configuration)
> *编译配置* → [*编译配置*](#build-configuration) **&&** [*编译配置*](#build-configuration)
> *编译配置* → [*编译配置*](#build-configuration) **||** [*编译配置*](#build-configuration)
> *编译配置* → [*平台检测函数*](#platform-testing-function)
> *编译配置* → [*语言版本检测函数*](#language-version-testing-function)
> *编译配置* → [*标识符*](02_Lexical_Structure.md#identifier)
> *编译配置* → [*布尔值字面量*](02_Lexical_Structure.md#boolean-literal)
> *编译配置* → **(** [*编译配置*](#build-configuration) **)**
> *编译配置* → **!** [*编译配置*](#build-configuration)
> *编译配置* → [*编译配置*](#build-configuration) **&&** [*编译配置*](#build-configuration)
> *编译配置* → [*编译配置*](#build-configuration) **||** [*编译配置*](#build-configuration)
<a name="platform-testing-function"></a>
> *平台检测函数* → **os** **(** [*操作系统*](#operating-system) **)**
> *平台检测函数* → **arch** **(** [*架构*](#architecture) **)**
> *平台检测函数* → **os** **(** [*操作系统*](#operating-system) **)**
> *平台检测函数* → **arch** **(** [*架构*](#architecture) **)**
<a name="language-version-testing-function"></a>
> *语言版本检测函数* → **swift** **(** **>=** [*swift版本*](#swift-version) **)**
> *语言版本检测函数* → **swift** **(** **>=** [*swift 版本*](#swift-version) **)**
<a name="operating-system"></a>
> *操作系统* → **OSX** | **iOS** | **watchOS** | **tvOS**
> *操作系统* → **OSX** | **iOS** | **watchOS** | **tvOS**
<a name="architecture"></a>
> *架构* → **i386** | **x86_64** | **arm** | **arm64**
> *架构* → **i386** | **x86_64** | **arm** | **arm64**
<a name="swift-version"></a>
> *swift 版本* → [*十进制数字*](02_Lexical_Structure.md#decimal-digit) ­**.** ­[*十进制数字*](02_Lexical_Structure.md#decimal-digit)
@ -627,28 +627,28 @@ statements to compile if both compilation conditions are false
第二种的行控制语句,`#sourceLocation()`,会将源代码的定位信息重置回默认的行号和文件名。
<a name="line-control-statement"></a>
> 行控制语句语法
>
> *行控制语句* → **#sourceLocation(file:[*文件名*](#file-name),line:[*行号*](#line-number))**
> *行控制语句* → **#sourceLocation()**
> 行控制语句语法
>
> *行控制语句* → **#sourceLocation(file:[*文件名*](#file-name),line:[*行号*](#line-number))**
> *行控制语句* → **#sourceLocation()**
<a name="line-number"></a>
> *行号* → 大于 0 的十进制整数
> *行号* → 大于 0 的十进制整数
<a name="file-name"></a>
> *文件名* → [*静态字符串字面量*](02_Lexical_Structure.md#static-string-literal)
<a name="availability_condition"></a>
### 可用性条件
可用性条件可作为 `if``while``guard` 语句的条件,可以在运行时基于特定的平台参数来查询 API 的可用性。
可用性条件可作为 `if``while``guard` 语句的条件,可以在运行时基于特定的平台参数来查询 API 的可用性。
可用性条件的形式如下:
```swift
if #available(platform name version, ..., *) {
statements to execute if the APIs are available
} else {
fallback statements to execute if the APIs are unavailable
}
if #available(platform name version, ..., *) {
statements to execute if the APIs are available
} else {
fallback statements to execute if the APIs are unavailable
}
```
使用可用性条件来执行一个代码块时,取决于使用的 API 在运行时是否可用,编译器会根据可用性条件提供的信息来决定是否执行相应的代码块。
@ -657,21 +657,21 @@ if #available(platform name version, ..., *) {
与布尔类型的条件不同,不能用逻辑运算符 `&&``||` 组合可用性条件。
> 可用性条件语法
> 可用性条件语法
>
<a name="availability-condition"></a>
> *可用性条件* → **#available** **(** [*可用性参数列表*](#availability-arguments) **)**
> *可用性条件* → **#available** **(** [*可用性参数列表*](#availability-arguments) **)**
<a name="availability-arguments"></a>
> *可用性参数列表* → [*可用性参数*](#availability-argument) | [*可用性参数*](#availability-argument) **,** [*可用性参数列表*](#availability-arguments)
> *可用性参数列表* → [*可用性参数*](#availability-argument) | [*可用性参数*](#availability-argument) **,** [*可用性参数列表*](#availability-arguments)
<a name="availability-argument"></a>
> *可用性参数* → [平台名称](#platform-name) [平台版本](#platform-version)
> *可用性参数* → [平台名称](#platform-name) [平台版本](#platform-version)
> *可用性条件* → __*__
<a name="platform-name"></a>
> *平台名称* → **iOS** | **iOSApplicationExtension**
> *平台名称* → **OSX** | **OSXApplicationExtension**
> *平台名称* → **watchOS**
> *平台名称* → **iOS** | **iOSApplicationExtension**
> *平台名称* → **OSX** | **OSXApplicationExtension**
> *平台名称* → **watchOS**
<a name="platform-version"></a>
> *平台版本* → [十进制数字](02_Lexical_Structure.md#decimal-digits)
> *平台版本* → [十进制数字](02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](02_Lexical_Structure.md#decimal-digits)
> *平台版本* → [十进制数字](02_Lexical_Structure.md#decimal-digits)
> *平台版本* → [十进制数字](02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](02_Lexical_Structure.md#decimal-digits)
> *平台版本* → [十进制数字](02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](02_Lexical_Structure.md#decimal-digits)

View File

@ -65,22 +65,22 @@
在 Swift 中,大多数声明在某种意义上讲也是定义,因为声明往往伴随着实现或初始化。由于协议并不提供实现,大多数协议成员仅仅只是声明而已。为了方便起见,也是因为这些区别在 Swift 中并不是很重要,“声明”这个术语同时包含了声明和定义两种含义。
> 声明语法
> 声明语法
> <a name="declaration"></a>
> *声明* → [*导入声明*](#import-declaration)
> *声明* → [*常量声明*](#constant-declaration)
> *声明* → [*变量声明*](#variable-declaration)
> *声明* → [*类型别名声明*](#typealias-declaration)
> *声明* → [*函数声明*](#function-declaration)
> *声明* → [*枚举声明*](#enum-declaration)
> *声明* → [*结构体声明*](#struct-declaration)
> *声明* → [*类声明*](#class-declaration)
> *声明* → [*协议声明*](#protocol-declaration)
> *声明* → [*构造器声明*](#initializer-declaration)
> *声明* → [*析构器声明*](#deinitializer-declaration)
> *声明* → [*扩展声明*](#extension-declaration)
> *声明* → [*下标声明*](#subscript-declaration)
> *声明* → [*运算符声明*](#operator-declaration)
> *声明* → [*导入声明*](#import-declaration)
> *声明* → [*常量声明*](#constant-declaration)
> *声明* → [*变量声明*](#variable-declaration)
> *声明* → [*类型别名声明*](#typealias-declaration)
> *声明* → [*函数声明*](#function-declaration)
> *声明* → [*枚举声明*](#enum-declaration)
> *声明* → [*结构体声明*](#struct-declaration)
> *声明* → [*类声明*](#class-declaration)
> *声明* → [*协议声明*](#protocol-declaration)
> *声明* → [*构造器声明*](#initializer-declaration)
> *声明* → [*析构器声明*](#deinitializer-declaration)
> *声明* → [*扩展声明*](#extension-declaration)
> *声明* → [*下标声明*](#subscript-declaration)
> *声明* → [*运算符声明*](#operator-declaration)
> <a name="declarations"></a>
> *多条声明* → [*声明*](#declaration) [*多条声明*](#declarations)<sub>可选</sub>
@ -89,7 +89,7 @@
Swift 的源文件中的顶级代码 (top-level code) 由零个或多个语句、声明和表达式组成。默认情况下,在一个源文件的顶层声明的变量,常量和其他具有命名的声明可以被同模块中的每一个源文件中的代码访问。可以使用一个访问级别修饰符来标记声明来覆盖这种默认行为,请参阅 [访问控制级别](#access_control_levels)。
> 顶级声明语法
> 顶级声明语法
> *顶级声明* → [*多条语句*](10_Statements.md#statements)<sub>可选</sub>
<a name="code_blocks"></a>
@ -104,9 +104,9 @@ Swift 的源文件中的顶级代码 (top-level code) 由零个或多个语句
```
代码块中的“语句”包括声明、表达式和各种其他类型的语句,它们按照在源码中的出现顺序被依次执行。
> 代码块语法
> 代码块语法
> <a name="code-block"></a>
> *代码块* → **{** [*多条语句*](10_Statements.md#statements)<sub>可选</sub> **}**
> *代码块* → **{** [*多条语句*](10_Statements.md#statements)<sub>可选</sub> **}**
<a name="import_declaration"></a>
## 导入声明
@ -125,15 +125,15 @@ import 模块.子模块
```
<a name="grammer_of_an_import_declaration"></a>
> 导入声明语法
> 导入声明语法
> <a name="import-declaration"></a>
> *导入声明* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **import** [*导入类型*](#import-kind)<sub>可选</sub> [*导入路径*](#import-path)
> *导入声明* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **import** [*导入类型*](#import-kind)<sub>可选</sub> [*导入路径*](#import-path)
> <a name="import-kind"></a>
> *导入类型* → **typealias** | **struct** | **class** | **enum** | **protocol** | **var** | **func**
> *导入类型* → **typealias** | **struct** | **class** | **enum** | **protocol** | **var** | **func**
> <a name="import-path"></a>
> *导入路径* → [*导入路径标识符*](#import-path-identifier) | [*导入路径标识符*](#import-path-identifier) **.** [*导入路径*](#import-path)
> *导入路径* → [*导入路径标识符*](#import-path-identifier) | [*导入路径标识符*](#import-path-identifier) **.** [*导入路径*](#import-path)
> <a name="import-path-identifier"></a>
> *导入路径标识符* → [*标识符*](02_Lexical_Structure.md#identifier) | [*运算符*](02_Lexical_Structure.md#operator)
> *导入路径标识符* → [*标识符*](02_Lexical_Structure.md#identifier) | [*运算符*](02_Lexical_Structure.md#operator)
<a name="constant_declaration"></a>
## 常量声明
@ -170,15 +170,15 @@ print("The second number is \(secondNumber).")
如果还想获得更多关于常量的信息或者想在使用中获得帮助,请参阅 [常量和变量](../chapter2/01_The_Basics.md#constants_and_variables) 和 [存储属性](../chapter2/10_Properties.md#stored_properties)。
<a name="grammer_of_a_constant_declaration"></a>
> 常量声明语法
> 常量声明语法
> <a name="constant-declaration"></a>
> *常量声明* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*声明修饰符列表*](#declaration-modifiers)<sub>可选</sub> **let** [*模式构造器列表*](pattern-initializer-list)
> *常量声明* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*声明修饰符列表*](#declaration-modifiers)<sub>可选</sub> **let** [*模式构造器列表*](pattern-initializer-list)
> <a name="pattern-initializer-list"></a>
> *模式构造器列表* → [*模式构造器*](#pattern-initializer) | [*模式构造器*](#pattern-initializer) **,** [*模式构造器列表*](#pattern-initializer-list)
> *模式构造器列表* → [*模式构造器*](#pattern-initializer) | [*模式构造器*](#pattern-initializer) **,** [*模式构造器列表*](#pattern-initializer-list)
> <a name="pattern-initializer"></a>
> *模式构造器* → [*模式*](07_Patterns.md#pattern) [*构造器*](#initializer)<sub>可选</sub>
> *模式构造器* → [*模式*](07_Patterns.md#pattern) [*构造器*](#initializer)<sub>可选</sub>
> <a name="initializer"></a>
> *构造器* → **=** [*表达式*](04_Expressions.md#expression)
> *构造器* → **=** [*表达式*](04_Expressions.md#expression)
<a name="variable_declaration"></a>
## 变量声明
@ -187,7 +187,8 @@ print("The second number is \(secondNumber).")
变量声明有几种不同的形式,可以声明不同种类的命名值和可变值,如存储型和计算型变量和属性,属性观察器,以及静态变量属性。所使用的声明形式取决于变量声明的适用范围和打算声明的变量类型。
> 注意
> 注意
>
> 也可以在协议声明中声明属性,详情请参阅 [协议属性声明](#protocol_property_declaration)。
可以在子类中重写继承来的变量属性,使用 `override` 声明修饰符标记属性的声明即可,详情请参阅 [重写](../chapter2/13_Inheritance.md#overriding)。
@ -215,14 +216,14 @@ var 变量名称: 类型 = 表达式
使用如下形式声明一个计算型变量或计算型属性:
```swift
var 变量名称: 类型 {
get {
var 变量名称: 类型 {
get {
语句
}
set(setter 名称) {
}
set(setter 名称) {
语句
}
}
}
}
```
可以在全局范围、函数内部,以及类、结构、枚举、扩展的声明中使用这种形式的声明。当变量以这种形式在全局范围或者函数内部被声明时,它表示一个计算型变量。当它在类、结构、枚举、扩展声明的上下文中被声明时,它表示一个*计算型属性 (computed property)*。
@ -241,14 +242,14 @@ setter 的圆括号以及 setter 名称是可选的。如果提供了 setter 名
可以在声明存储型变量或属性时提供 `willSet``didSet` 观察器。一个包含观察器的存储型变量或属性以如下形式声明:
```swift
var 变量名称: 类型 = 表达式 {
willSet(setter 名称) {
var 变量名称: 类型 = 表达式 {
willSet(setter 名称) {
语句
}
didSet(setter 名称) {
}
didSet(setter 名称) {
语句
}
}
}
}
```
可以在全局范围、函数内部,或者类、结构的声明中使用这种形式的声明。当变量以这种形式在全局范围或者函数内部被声明时,观察器表示一个存储型变量观察器。当它在类和结构的声明中被声明时,观察器表示一个属性观察器。
@ -273,51 +274,52 @@ var 变量名称: 类型 = 表达式 {
要声明一个类型变量属性,用 `static` 声明修饰符标记该声明。类可以改用 `class` 声明修饰符标记类的类型计算型属性从而允许子类重写超类的实现。类型属性在 [类型属性](../chapter2/10_Properties.md#type_properties) 章节有详细讨论。
> 注意
> 注意
>
> 在一个类声明中,使用关键字 `static` 与同时使用 `class` 和 `final` 去标记一个声明的效果相同。
<a name="grammer_of_a_variable_declaration"></a>
> 变量声明语法
> 变量声明语法
<a name="variable-declaration"></a>
> *变量声明* → [*变量声明头*](#variable-declaration-head) [*模式构造器列表*](#pattern-initializer-list)
> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*代码块*](#code-block)
> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*getter-setter代码块*](#getter-setter-block)
> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*getter-setter关键字代码块*](#getter-setter-keyword-block)
> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*构造器*](#initializer) [*willSet-didSet代码块*](#willSet-didSet-block)
> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*构造器*](#initializer)<sub>可选</sub> [*willSet-didSet代码块*](#willSet-didSet-block)
> *变量声明* → [*变量声明头*](#variable-declaration-head) [*模式构造器列表*](#pattern-initializer-list)
> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*代码块*](#code-block)
> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*getter-setter 代码块*](#getter-setter-block)
> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*getter-setter 关键字代码块*](#getter-setter-keyword-block)
> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*构造器*](#initializer) [*willSet-didSet 代码块*](#willSet-didSet-block)
> *变量声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*构造器*](#initializer)<sub>可选</sub> [*willSet-didSet 代码块*](#willSet-didSet-block)
<a name="variable-declaration-head"></a>
> *变量声明头* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*声明修饰符列表*](#declaration-modifiers)<sub>可选</sub> **var**
> *变量声明头* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*声明修饰符列表*](#declaration-modifiers)<sub>可选</sub> **var**
> <a name="variable-name"></a>
> *变量名称* → [*标识符*](02_Lexical_Structure.md#identifier)
> *变量名称* → [*标识符*](02_Lexical_Structure.md#identifier)
<a name="getter-setter-block"></a>
> *getter-setter 代码块* → [*代码块*](#code-block)
> *getter-setter 代码块* → **{** [*getter子句*](#getter-clause) [*setter子句*](#setter-clause)<sub>可选</sub> **}**
> *getter-setter 代码块* → **{** [*setter子句*](#setter-clause) [*getter子句*](#getter-clause) **}**
> *getter-setter 代码块* → [*代码块*](#code-block)
> *getter-setter 代码块* → **{** [*getter 子句*](#getter-clause) [*setter 子句*](#setter-clause)<sub>可选</sub> **}**
> *getter-setter 代码块* → **{** [*setter 子句*](#setter-clause) [*getter 子句*](#getter-clause) **}**
> <a name="getter-clause"></a>
> *getter 子句* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **get** [*代码块*](#code-block)
> *getter 子句* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **get** [*代码块*](#code-block)
> <a name="setter-clause"></a>
> *setter 子句* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **set** [*setter名称*](#setter-name)<sub>可选</sub> [*代码块*](#code-block)
> *setter 子句* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **set** [*setter 名称*](#setter-name)<sub>可选</sub> [*代码块*](#code-block)
> <a name="setter-name"></a>
> *setter 名称* → **(** [*标识符*](02_Lexical_Structure.md#identifier) **)**
> *setter 名称* → **(** [*标识符*](02_Lexical_Structure.md#identifier) **)**
<a name="getter-setter-keyword-block"></a>
> *getter-setter 关键字代码块* → **{** [*getter关键字子句*](#getter-keyword-clause) [*setter关键字子句*](#setter-keyword-clause)<sub>可选</sub> **}**
> *getter-setter 关键字代码块* → **{** [*setter关键字子句*](#setter-keyword-clause) [*getter关键字子句*](#getter-keyword-clause) **}**
> *getter-setter 关键字代码块* → **{** [*getter 关键字子句*](#getter-keyword-clause) [*setter 关键字子句*](#setter-keyword-clause)<sub>可选</sub> **}**
> *getter-setter 关键字代码块* → **{** [*setter 关键字子句*](#setter-keyword-clause) [*getter 关键字子句*](#getter-keyword-clause) **}**
> <a name="getter-keyword-clause"></a>
> *getter 关键字子句* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **get**
> *getter 关键字子句* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **get**
> <a name="setter-keyword-clause"></a>
> *setter 关键字子句* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **set**
> *setter 关键字子句* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **set**
<a name="willSet-didSet-block"></a>
> *willSet-didSet 代码块* → **{** [*willSet子句*](#willSet-clause) [*didSet子句*](#didSet-clause)<sub>可选</sub> **}**
> *willSet-didSet 代码块* → **{** [*didSet子句*](#didSet-clause) [*willSet子句*](#willSet-clause)<sub>可选</sub> **}**
> *willSet-didSet 代码块* → **{** [*willSet 子句*](#willSet-clause) [*didSet 子句*](#didSet-clause)<sub>可选</sub> **}**
> *willSet-didSet 代码块* → **{** [*didSet 子句*](#didSet-clause) [*willSet 子句*](#willSet-clause)<sub>可选</sub> **}**
> <a name="willSet-clause"></a>
> *willSet 子句* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **willSet** [*setter名称*](#setter-name)<sub>可选</sub> [*代码块*](#code-block)
> *willSet 子句* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **willSet** [*setter 名称*](#setter-name)<sub>可选</sub> [*代码块*](#code-block)
> <a name="didSet-clause"></a>
> *didSet 子句* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **didSet** [*setter名称*](#setter-name)<sub>可选</sub> [*代码块*](#code-block)
> *didSet 子句* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **didSet** [*setter 名称*](#setter-name)<sub>可选</sub> [*代码块*](#code-block)
<a name="type_alias_declaration"></a>
## 类型别名声明
@ -353,20 +355,20 @@ func sum<T: Sequence>(_ sequence: T) -> Int where T.Element == Int {
// ...
}
```
假如没有类型别名sum函数将必须引用关联类型通过T.Iterator.Element的形式来替代 T.Element。
假如没有类型别名sum 函数将必须引用关联类型通过 T.Iterator.Element 的形式来替代 T.Element。
另请参阅 [协议关联类型声明](#protocol_associated_type_declaration)。
<a name="grammer_of_a_type_alias_declaration"></a>
> 类型别名声明语法
> 类型别名声明语法
> <a name="typealias-declaration"></a>
> *类型别名声明* → [*类型别名头*](#typealias-head) [*类型别名赋值*](#typealias-assignment)
> *类型别名声明* → [*类型别名头*](#typealias-head) [*类型别名赋值*](#typealias-assignment)
> <a name="typealias-head"></a>
> *类型别名头* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*访问级别修饰符*](#access-level-modifier)<sub>可选</sub> **typealias** [*类型别名名称*](#typealias-name)
> *类型别名头* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*访问级别修饰符*](#access-level-modifier)<sub>可选</sub> **typealias** [*类型别名名称*](#typealias-name)
> <a name="typealias-name"></a>
> *类型别名名称* → [*标识符*](02_Lexical_Structure.md#identifier)
> *类型别名名称* → [*标识符*](02_Lexical_Structure.md#identifier)
> <a name="typealias-assignment"></a>
> *类型别名赋值* → **=** [*类型*](03_Types.md#type)
> *类型别名赋值* → **=** [*类型*](03_Types.md#type)
<a name="function_declaration"></a>
## 函数声明
@ -374,7 +376,7 @@ func sum<T: Sequence>(_ sequence: T) -> Int where T.Element == Int {
使用*函数声明 (function declaration)* 在程序中引入新的函数或者方法。在类、结构体、枚举,或者协议中声明的函数会作为方法。函数声明使用关键字 `func`,遵循如下的形式:
```swift
func 函数名称(参数列表) -> 返回类型 {
func 函数名称(参数列表) -> 返回类型 {
语句
}
```
@ -382,7 +384,7 @@ func 函数名称(参数列表) -> 返回类型 {
如果函数返回 `Void` 类型,返回类型可以省略,如下所示:
```swift
func 函数名称(参数列表) {
func 函数名称(参数列表) {
语句
}
```
@ -409,7 +411,7 @@ f(1, y: 2) // 参数 y 有标签,参数 x 则没有
可以按照如下两种形式之一,重写参数名称的默认行为:
`外部参数名称` `内部参数名称`: `参数类型`
`外部参数名称` `内部参数名称`: `参数类型`
_ `内部参数名称`: `参数类型`
在内部参数名称前的名称会作为这个参数的外部名称,这个名称可以和内部参数的名称不同。外部参数名称在函数被调用时必须被使用,即对应的参数在方法或函数被调用时必须有外部名。
@ -475,8 +477,8 @@ print(x)
```swift
_ : 参数类型
参数名称: 参数类型...
参数名称: 参数类型 = 默认参数值
参数名称: 参数类型...
参数名称: 参数类型 = 默认参数值
```
以下划线(`_`)命名的参数会被显式忽略,无法在函数体内使用。
@ -538,46 +540,46 @@ func someFunction(callback: () throws -> Void) rethrows {
<a name="functions_that_never_return"></a>
### 永不返回的函数
Swift定义了`Never`类型,它表示函数或者方法不会返回给它的调用者。`Never`返回类型的函数或方法可以称为不归,不归函数、方法要么引发不可恢复的错误,要么永远不停地运作,这会使调用后本应执行得代码就不再执行了。但即使是不归函数、方法,抛错函数和重抛出函数也可以将程序控制转移到合适的`catch`代码块。
Swift 定义了 `Never` 类型,它表示函数或者方法不会返回给它的调用者。`Never` 返回类型的函数或方法可以称为不归,不归函数、方法要么引发不可恢复的错误,要么永远不停地运作,这会使调用后本应执行得代码就不再执行了。但即使是不归函数、方法,抛错函数和重抛出函数也可以将程序控制转移到合适的 `catch` 代码块。
不归函数、方法可以在guard语句的else字句中调用具体讨论在[*Guard语句*](10_Statements.md#guard_statements)。
不归函数、方法可以在 guard 语句的 else 字句中调用,具体讨论在[*Guard 语句*](10_Statements.md#guard_statements)。
你可以重载一个不归方法,但是新的方法必须保持原有的返回类型和没有返回的行为。
<a name="grammer_of_a_function_declaration"></a>
> 函数声明语法
> 函数声明语法
<a name="function-declaration"></a>
> *函数声明* → [*函数头*](#function-head) [*函数名*](#function-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可选</sub> [*函数签名*](#function-signature) [*函数体*](#function-body)<sub>可选</sub>
> *函数声明* → [*函数头*](#function-head) [*函数名*](#function-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可选</sub> [*函数签名*](#function-signature) [*函数体*](#function-body)<sub>可选</sub>
<a name="function-head"></a>
> *函数头* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*声明修饰符列表*](#declaration-modifiers)<sub>可选</sub> **func**
> *函数头* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*声明修饰符列表*](#declaration-modifiers)<sub>可选</sub> **func**
> <a name="function-name"></a>
> *函数名* → [*标识符*](02_Lexical_Structure.md#identifier) | [*运算符*](02_Lexical_Structure.md#operator)
> *函数名* → [*标识符*](02_Lexical_Structure.md#identifier) | [*运算符*](02_Lexical_Structure.md#operator)
<a name="function-signature"></a>
> *函数签名* → [*参数子句列表*](#parameter-clauses) **throws**<sub>可选</sub> [*函数结果*](#function-result)<sub>可选</sub>
> *函数签名* → [*参数子句列表*](#parameter-clauses) **rethrows** [*函数结果*](#function-result)<sub>可选</sub>
> *函数签名* → [*参数子句列表*](#parameter-clauses) **throws**<sub>可选</sub> [*函数结果*](#function-result)<sub>可选</sub>
> *函数签名* → [*参数子句列表*](#parameter-clauses) **rethrows** [*函数结果*](#function-result)<sub>可选</sub>
> <a name="function-result"></a>
> *函数结果* → **->** [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*类型*](03_Types.md#type)
> *函数结果* → **->** [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*类型*](03_Types.md#type)
> <a name="function-body"></a>
> *函数体* → [*代码块*](#code-block)
> *函数体* → [*代码块*](#code-block)
<a name="parameter-clauses"></a>
> *参数子句列表* → [*参数子句*](#parameter-clause) [*参数子句列表*](#parameter-clauses)<sub>可选</sub>
> *参数子句列表* → [*参数子句*](#parameter-clause) [*参数子句列表*](#parameter-clauses)<sub>可选</sub>
> <a name="parameter-clause"></a>
> *参数子句* → **(** **)** | **(** [*参数列表*](#parameter-list) **)**
> *参数子句* → **(** **)** | **(** [*参数列表*](#parameter-list) **)**
> <a name="parameter-list"></a>
> *参数列表* → [*参数*](#parameter) | [*参数*](#parameter) **,** [*参数列表*](#parameter-list)
> *参数列表* → [*参数*](#parameter) | [*参数*](#parameter) **,** [*参数列表*](#parameter-list)
> <a name="parameter"></a>
> *参数* → **let**<sub>可选</sub> [*外部参数名*](#external-parameter-name)<sub>可选</sub> [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.md#type-annotation) [*默认参数子句*](#default-argument-clause)<sub>可选</sub>
> *参数* → **inout** [*外部参数名*](#external-parameter-name)<sub>可选</sub> [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.md#type-annotation)
> *参数* → [*外部参数名*](#external-parameter-name)<sub>可选</sub> [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.md#type-annotation) **...**
> *参数* → **let**<sub>可选</sub> [*外部参数名*](#external-parameter-name)<sub>可选</sub> [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.md#type-annotation) [*默认参数子句*](#default-argument-clause)<sub>可选</sub>
> *参数* → **inout** [*外部参数名*](#external-parameter-name)<sub>可选</sub> [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.md#type-annotation)
> *参数* → [*外部参数名*](#external-parameter-name)<sub>可选</sub> [*内部参数名*](#local-parameter-name) [*类型标注*](03_Types.md#type-annotation) **...**
> <a name="external-parameter-name"></a>
> *外部参数名* → [*标识符*](02_Lexical_Structure.md#identifier) | **_**
> *外部参数名* → [*标识符*](02_Lexical_Structure.md#identifier) | **_**
> <a name="local-parameter-name"></a>
> *内部参数名* → [*标识符*](02_Lexical_Structure.md#identifier) | **_**
> *内部参数名* → [*标识符*](02_Lexical_Structure.md#identifier) | **_**
> <a name="default-argument-clause"></a>
> *默认参数子句* → **=** [*表达式*](04_Expressions.md#expression)
> *默认参数子句* → **=** [*表达式*](04_Expressions.md#expression)
<a name="enumeration_declaration"></a>
## 枚举声明
@ -651,10 +653,10 @@ enum Tree<T> {
以下形式声明了一种枚举类型,其中各个枚举用例的类型均为同一种基本类型:
```swift
enum 枚举名称: 原始值类型, 采纳的协议 {
enum 枚举名称: 原始值类型, 采纳的协议 {
case 枚举用例1 = 原始值1
case 枚举用例2 = 原始值2
}
}
```
在这种形式中,每一个用例块由 `case` 关键字开始,后面紧跟一个或多个以逗号分隔的枚举用例。和第一种形式的枚举用例不同,这种形式的枚举用例包含一个基础值,叫做原始值,各个枚举用例的原始值的类型必须相同。这些原始值的类型通过原始值类型指定,必须表示一个整数、浮点数、字符串或者字符。原始值类型必须符合 `Equatable` 协议和下列字面量转换协议中的一种:整型字面量需符合 `IntergerLiteralConvertible` 协议,浮点型字面量需符合 `FloatingPointLiteralConvertible` 协议,包含任意数量字符的字符串型字面量需符合 `StringLiteralConvertible` 协议,仅包含一个单一字符的字符串型字面量需符合 `ExtendedGraphemeClusterLiteralConvertible` 协议。每一个用例的名字和原始值必须唯一。
@ -692,40 +694,40 @@ enum WeekendDay: String {
> 枚举声明语法
<a name="enum-declaration"></a>
> *枚举声明* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*访问级别修饰符*](#access-level-modifier)<sub>可选</sub> [*联合风格枚举*](#union-style-enum)
> *枚举声明* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*访问级别修饰符*](#access-level-modifier)<sub>可选</sub> [*联合风格枚举*](#union-style-enum)
> *枚举声明* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*访问级别修饰符*](#access-level-modifier) <sub>可选</sub> [*原始值风格枚举*](#raw-value-style-enum)
<a name="union-style-enum"></a>
> *联合风格枚举* → **indirect**<sub>可选</sub> **enum** [*枚举名称*](#enum-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可选</sub> [类型继承子句](03_Types.md#type-inheritance-clause)<sub>可选</sub> **{** [*多个联合风格枚举成员*](#union-style-enum-members)<sub>可选</sub> **}**
> *联合风格枚举* → **indirect**<sub>可选</sub> **enum** [*枚举名称*](#enum-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可选</sub> [类型继承子句](03_Types.md#type-inheritance-clause)<sub>可选</sub> **{** [*多个联合风格枚举成员*](#union-style-enum-members)<sub>可选</sub> **}**
> <a name="union-style-enum-members"></a>
> *多个联合风格枚举成员* → [*联合风格枚举成员*](#union-style-enum-member) [*多个联合风格枚举成员*](#union-style-enum-members)<sub>可选</sub>
> *多个联合风格枚举成员* → [*联合风格枚举成员*](#union-style-enum-member) [*多个联合风格枚举成员*](#union-style-enum-members)<sub>可选</sub>
> <a name="union-style-enum-member"></a>
> *联合风格枚举成员* → [*声明*](#declaration) | [*联合风格枚举用例子句*](#union-style-enum-case-clause)
> *联合风格枚举成员* → [*声明*](#declaration) | [*联合风格枚举用例子句*](#union-style-enum-case-clause)
> <a name="union-style-enum-case-clause"></a>
> *联合风格枚举用例子句* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **indirect**<sub>可选</sub> **case** [*联合风格枚举用例列表*](#union-style-enum-case-list)
> *联合风格枚举用例子句* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **indirect**<sub>可选</sub> **case** [*联合风格枚举用例列表*](#union-style-enum-case-list)
> <a name="union-style-enum-case-list"></a>
> *联合风格枚举用例列表* → [*联合风格枚举用例*](#union-style-enum-case) | [*联合风格枚举用例*](#union-style-enum-case) **,** [*联合风格枚举用例列表*](#union-style-enum-case-list)
> *联合风格枚举用例列表* → [*联合风格枚举用例*](#union-style-enum-case) | [*联合风格枚举用例*](#union-style-enum-case) **,** [*联合风格枚举用例列表*](#union-style-enum-case-list)
> <a name="union-style-enum-case"></a>
> *联合风格枚举用例* → [*枚举用例名称*](#enum-case-name) [*元组类型*](03_Types.md#tuple-type)<sub>可选</sub>
> *联合风格枚举用例* → [*枚举用例名称*](#enum-case-name) [*元组类型*](03_Types.md#tuple-type)<sub>可选</sub>
> <a name="enum-name"></a>
> *枚举名称* → [*标识符*](02_Lexical_Structure.md#identifier)
> *枚举名称* → [*标识符*](02_Lexical_Structure.md#identifier)
> <a name="enum-case-name"></a>
> *枚举用例名称* → [*标识符*](02_Lexical_Structure.md#identifier)
> *枚举用例名称* → [*标识符*](02_Lexical_Structure.md#identifier)
<a name="raw-value-style-enum"></a>
> *原始值风格枚举* → **enum** [*枚举名称*](#enum-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可选</sub> [类型继承子句](03_Types.md#type-inheritance-clause) **{** [*多个原始值风格枚举成员*](#raw-value-style-enum-members) **}**
> *原始值风格枚举* → **enum** [*枚举名称*](#enum-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可选</sub> [类型继承子句](03_Types.md#type-inheritance-clause) **{** [*多个原始值风格枚举成员*](#raw-value-style-enum-members) **}**
> <a name="raw-value-style-enum-members"></a>
> *多个原始值风格枚举成员* → [*原始值风格枚举成员*](#raw-value-style-enum-member) [*多个原始值风格枚举成员*](#raw-value-style-enum-members)<sub>可选</sub>
> *多个原始值风格枚举成员* → [*原始值风格枚举成员*](#raw-value-style-enum-member) [*多个原始值风格枚举成员*](#raw-value-style-enum-members)<sub>可选</sub>
> <a name="raw-value-style-enum-member"></a>
> *原始值风格枚举成员* → [*声明*](#declaration) | [*原始值风格枚举用例子句*](#raw-value-style-enum-case-clause)
> *原始值风格枚举成员* → [*声明*](#declaration) | [*原始值风格枚举用例子句*](#raw-value-style-enum-case-clause)
> <a name="raw-value-style-enum-case-clause"></a>
> *原始值风格枚举用例子句* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **case** [*原始值风格枚举用例列表*](#raw-value-style-enum-case-list)
> *原始值风格枚举用例子句* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **case** [*原始值风格枚举用例列表*](#raw-value-style-enum-case-list)
> <a name="raw-value-style-enum-case-list"></a>
> *原始值风格枚举用例列表* → [*原始值风格枚举用例*](#raw-value-style-enum-case) | [*原始值风格枚举用例*](#raw-value-style-enum-case) **,** [*原始值风格枚举用例列表*](#raw-value-style-enum-case-list)
> *原始值风格枚举用例列表* → [*原始值风格枚举用例*](#raw-value-style-enum-case) | [*原始值风格枚举用例*](#raw-value-style-enum-case) **,** [*原始值风格枚举用例列表*](#raw-value-style-enum-case-list)
> <a name="raw-value-style-enum-case"></a>
> *原始值风格枚举用例* → [*枚举用例名称*](#enum-case-name) [*原始值赋值*](#raw-value-assignment)<sub>可选</sub>
> *原始值风格枚举用例* → [*枚举用例名称*](#enum-case-name) [*原始值赋值*](#raw-value-assignment)<sub>可选</sub>
> <a name="raw-value-assignment"></a>
> *原始值赋值* → **=** [*原始值字面量*](#raw-value-literal)
> *原始值赋值* → **=** [*原始值字面量*](#raw-value-literal)
> <a name="raw-value-literal"></a>
> *原始值字面量* → [数字型字面量](02_Lexical_Structure.md#numeric-literal) | [字符串型字面量](02_Lexical_Structure.md#static-string-literal) | [布尔型字面量](02_Lexical_Structure.md#boolean-literal)
@ -735,7 +737,7 @@ enum WeekendDay: String {
使用*结构体声明 (structure declaration)* 可以在程序中引入一个结构体类型。结构体声明使用 `struct` 关键字,遵循如下的形式:
```swift
struct 结构体名称: 采纳的协议 {
struct 结构体名称: 采纳的协议 {
多条声明
}
```
@ -762,13 +764,13 @@ struct 结构体名称: 采纳的协议 {
可以使用扩展声明来扩展结构体类型的行为,请参阅 [扩展声明](#extension_declaration)。
<a name="grammer_of_a_structure_declaration"></a>
> 结构体声明语法
> 结构体声明语法
> <a name="struct-declaration"></a>
> *结构体声明* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*访问级别修饰符*](#access-level-modifier) <sub>可选</sub> **struct** [*结构体名称*](#struct-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可选</sub> [类型继承子句](03_Types.md#type-inheritance-clause)<sub>可选</sub> [*结构体主体*](#struct-body)
> *结构体声明* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*访问级别修饰符*](#access-level-modifier) <sub>可选</sub> **struct** [*结构体名称*](#struct-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可选</sub> [类型继承子句](03_Types.md#type-inheritance-clause)<sub>可选</sub> [*结构体主体*](#struct-body)
> <a name="struct-name"></a>
> *结构体名称* → [*标识符*](02_Lexical_Structure.md#identifier)
> *结构体名称* → [*标识符*](02_Lexical_Structure.md#identifier)
> <a name="struct-body"></a>
> *结构体主体* → **{** [*多条声明*](#declarations)<sub>可选</sub> **}**
> *结构体主体* → **{** [*多条声明*](#declarations)<sub>可选</sub> **}**
<a name="class_declaration"></a>
## 类声明
@ -776,7 +778,7 @@ struct 结构体名称: 采纳的协议 {
可以在程序中使用*类声明 (class declaration)* 来引入一个类。类声明使用关键字 `class`,遵循如下的形式:
```swift
class 类名: 超类, 采纳的协议 {
class 类名: 超类, 采纳的协议 {
多条声明
}
```
@ -806,13 +808,13 @@ class 类名: 超类, 采纳的协议 {
可以使用扩展声明来扩展类的行为,请参阅 [扩展声明](#extension_declaration)。
<a name="grammer_of_a_class_declaration"></a>
> 类声明语法
> 类声明语法
> <a name="class-declaration"></a>
> *类声明* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [访问级别修饰符](#access-level-modifier)<sub>可选</sub> **class** [*类名*](#class-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可选</sub> [*类型继承子句*](03_Types.md#type-inheritance-clause)<sub>可选</sub> [*类主体*](#class-body)
> *类声明* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [访问级别修饰符](#access-level-modifier)<sub>可选</sub> **class** [*类名*](#class-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可选</sub> [*类型继承子句*](03_Types.md#type-inheritance-clause)<sub>可选</sub> [*类主体*](#class-body)
> <a name="class-name"></a>
> *类名* → [*标识符*](02_Lexical_Structure.md#identifier)
> *类名* → [*标识符*](02_Lexical_Structure.md#identifier)
> <a name="class-body"></a>
> *类主体* → **{** [*多条声明*](#declarations)<sub>可选</sub> **}**
> *类主体* → **{** [*多条声明*](#declarations)<sub>可选</sub> **}**
<a name="protocol_declaration"></a>
## 协议声明
@ -820,16 +822,17 @@ class 类名: 超类, 采纳的协议 {
*协议声明 (protocol declaration)* 可以为程序引入一个命名的协议类型。协议声明只能在全局区域使用 `protocol` 关键字来进行声明,并遵循如下形式:
```swift
protocol 协议名称: 继承的协议 {
protocol 协议名称: 继承的协议 {
协议成员声明
}
}
```
协议的主体包含零个或多个协议成员声明,这些成员描述了任何采纳该协议的类型必须满足的一致性要求。一个协议可以声明采纳者必须实现的某些属性、方法、构造器以及下标。协议也可以声明各种各样的类型别名,叫做关联类型,它可以指定协议的不同声明之间的关系。协议声明不能包括类、结构体、枚举或者其它协议的声明。协议成员声明会在后面进行讨论。
协议类型可以继承自任意数量的其它协议。当一个协议类型继承自其它协议的时候,来自其它协议的所有要求会聚合在一起,而且采纳当前协议的类型必须符合所有的这些要求。关于如何使用协议继承的例子,请参阅 [协议继承](../chapter2/22_Protocols.md#protocol_inheritance)。
> 注意
> 注意
>
> 也可以使用协议合成类型来聚合多个协议的一致性要求,请参阅 [协议合成类型](03_Types.md#protocol_composition_type) 和 [协议合成](../chapter2/22_Protocols.md#protocol_composition)。
可以通过类型的扩展声明来采纳协议,从而为之前声明的类型添加协议一致性。在扩展中,必须实现所有采纳协议的要求。如果该类型已经实现了所有的要求,可以让这个扩展声明的主体留空。
@ -846,7 +849,8 @@ protocol SomeProtocol: class {
任何继承自标记有 `class` 关键字的协议的协议也仅能被类类型采纳。
> 注意
> 注意
>
> 如果协议已经用 `objc` 特性标记了,`class` 要求就隐式地应用于该协议,无需显式使用 `class` 关键字。
协议类型是命名的类型,因此它们可以像其他命名类型一样使用,正如 [协议作为类型](../chapter2/22_Protocols.md#protocols_as_types) 所讨论的。然而,不能构造一个协议的实例,因为协议实际上不提供它们指定的要求的实现。
@ -854,23 +858,23 @@ protocol SomeProtocol: class {
可以使用协议来声明作为代理的类或者结构体应该实现的方法,正如 [委托(代理)模式](../chapter2/22_Protocols.md#delegation) 中所述。
<a name="grammer_of_a_protocol_declaration"></a>
> 协议声明语法
> 协议声明语法
<a name="protocol-declaration"></a>
> *协议声明* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [访问级别修饰符](#access-level-modifier)<sub>可选</sub> **protocol** [*协议名称*](#protocol-name) [*类型继承子句*](03_Types.html#type-inheritance-clause)<sub>可选</sub> [*协议主体*](#protocol-body)
> *协议声明* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [访问级别修饰符](#access-level-modifier)<sub>可选</sub> **protocol** [*协议名称*](#protocol-name) [*类型继承子句*](03_Types.html#type-inheritance-clause)<sub>可选</sub> [*协议主体*](#protocol-body)
> <a name="protocol-name"></a>
> *协议名称* → [*标识符*](02_Lexical_Structure.md#identifier)
> *协议名称* → [*标识符*](02_Lexical_Structure.md#identifier)
> <a name="protocol-body"></a>
> *协议主体* → **{** [*协议成员声明列表*](#protocol-member-declarations)<sub>可选</sub> **}**
> *协议主体* → **{** [*协议成员声明列表*](#protocol-member-declarations)<sub>可选</sub> **}**
<a name="protocol-member-declaration"></a>
> *协议成员声明* → [*协议属性声明*](#protocol-property-declaration)
> *协议成员声明* → [*协议方法声明*](#protocol-method-declaration)
> *协议成员声明* → [*协议构造器声明*](#protocol-initializer-declaration)
> *协议成员声明* → [*协议下标声明*](#protocol-subscript-declaration)
> *协议成员声明* → [*协议关联类型声明*](#protocol-associated-type-declaration)
> *协议成员声明* → [*协议属性声明*](#protocol-property-declaration)
> *协议成员声明* → [*协议方法声明*](#protocol-method-declaration)
> *协议成员声明* → [*协议构造器声明*](#protocol-initializer-declaration)
> *协议成员声明* → [*协议下标声明*](#protocol-subscript-declaration)
> *协议成员声明* → [*协议关联类型声明*](#protocol-associated-type-declaration)
> <a name="protocol-member-declarations"></a>
> *协议成员声明列表* → [*协议成员声明*](#protocol-member-declaration) [*协议成员声明列表*](#protocol-member-declarations)<sub>可选</sub>
> *协议成员声明列表* → [*协议成员声明*](#protocol-member-declaration) [*协议成员声明列表*](#protocol-member-declarations)<sub>可选</sub>
<a name="protocol_property_declaration"></a>
### 协议属性声明
@ -888,9 +892,9 @@ var 属性名: 类型 { get set }
另请参阅 [变量声明](#variable_declaration)。
<a name="grammer_of_an_import_declaration"></a>
> 协议属性声明语法
> 协议属性声明语法
> <a name="protocol-property-declaration"></a>
> *协议属性声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*getter-setter关键字代码块*](#getter-setter-keyword-block)
> *协议属性声明* → [*变量声明头*](#variable-declaration-head) [*变量名称*](#variable-name) [*类型标注*](03_Types.md#type-annotation) [*getter-setter 关键字代码块*](#getter-setter-keyword-block)
<a name="protocol_method_declaration"></a>
### 协议方法声明
@ -902,9 +906,9 @@ var 属性名: 类型 { get set }
另请参阅 [函数声明](#function_declaration)。
<a name="grammer_of_a_protocol_declaration"></a>
> 协议方法声明语法
> 协议方法声明语法
> <a name="protocol-method-declaration"></a>
> *协议方法声明* → [*函数头*](#function-head) [*函数名*](#function-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可选</sub> [*函数签名*](#function-signature)
> *协议方法声明* → [*函数头*](#function-head) [*函数名*](#function-name) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可选</sub> [*函数签名*](#function-signature)
<a name="protocol_initializer_declaration"></a>
### 协议构造器声明
@ -919,9 +923,9 @@ var 属性名: 类型 { get set }
另请参阅 [构造器声明](#initializer_declaration)。
<a name="grammer_of_a_protocol_initializer_declaration"></a>
> 协议构造器声明语法
> 协议构造器声明语法
> <a name="protocol-initializer-declaration"></a>
> *协议构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可选</sub> [*参数子句*](#parameter-clause) **throws**<sub>可选</sub>
> *协议构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可选</sub> [*参数子句*](#parameter-clause) **throws**<sub>可选</sub>
> *协议构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可选</sub> [*参数子句*](#parameter-clause) **rethrows**
<a name="protocol_subscript_declaration"></a>
@ -938,9 +942,9 @@ subscript (参数列表) -> 返回类型 { get set }
另请参阅 [下标声明](#subscript_declaration)。
<a name="grammer_of_a_protocol_subscript_declaration"></a>
> 协议下标声明语法
> 协议下标声明语法
> <a name="protocol-subscript-declaration"></a>
> *协议下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*getter-setter关键字代码块*](#getter-setter-keyword-block)
> *协议下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*getter-setter 关键字代码块*](#getter-setter-keyword-block)
<a name="protocol_associated_type_declaration"></a>
### 协议关联类型声明
@ -950,9 +954,9 @@ subscript (参数列表) -> 返回类型 { get set }
另请参阅 [类型别名声明](#type_alias_declaration)。
<a name="grammer_of_a_protocol_associated_type_declaration"></a>
> 协议关联类型声明语法
> 协议关联类型声明语法
> <a name="protocol-associated-type-declaration"></a>
> *协议关联类型声明* → [*类型别名头*](#typealias-head) [*类型继承子句*](03_Types.md#type-inheritance-clause)<sub>可选</sub> [*类型别名赋值*](#typealias-assignment)<sub>可选</sub>
> *协议关联类型声明* → [*类型别名头*](#typealias-head) [*类型继承子句*](03_Types.md#type-inheritance-clause)<sub>可选</sub> [*类型别名赋值*](#typealias-assignment)<sub>可选</sub>
<a name="initializer_declaration"></a>
## 构造器声明
@ -964,9 +968,9 @@ subscript (参数列表) -> 返回类型 { get set }
采用如下形式声明结构体和枚举的构造器,以及类的指定构造器:
```swift
init(参数列表) {
init(参数列表) {
构造语句
}
}
```
类的指定构造器直接将类的所有属性初始化。它不能调用类中的其他构造器,如果类有超类,则必须调用超类的一个指定构造器。如果该类从它的超类继承了属性,必须在调用超类的指定构造器后才能修改这些属性。
@ -978,9 +982,9 @@ init(参数列表) {
要为类声明一个便利构造器,用 `convenience` 声明修饰符来标记构造器声明:
```swift
convenience init(参数列表) {
convenience init(参数列表) {
构造语句
}
}
```
便利构造器可以将构造过程委托给另一个便利构造器或一个指定构造器。但是,类的构造过程必须以一个将类中所有属性完全初始化的指定构造器的调用作为结束。便利构造器不能调用超类的构造器。
@ -991,7 +995,8 @@ convenience init(参数列表) {
和方法、属性和下标一样,需要使用 `override` 声明修饰符标记重写的指定构造器。
> 注意
> 注意
>
> 如果使用 `required` 声明修饰符标记一个构造器,在子类中重写这种构造器时,无需使用 `override` 修饰符。
就像函数和方法,构造器也可以抛出或者重抛错误,你可以在构造器参数列表的圆括号之后使用 `throws``rethrows` 关键字来表明相应的抛出行为。
@ -1040,16 +1045,16 @@ if let actualInstance = SomeStruct(input: "Hello") {
更多关于可失败构造器的信息和例子,请参阅 [可失败构造器](../chapter2/14_Initialization.md#failable_initializers)。
<a name="grammer_of_an_initializer_declaration"></a>
> 构造器声明语法
> 构造器声明语法
> <a name="initializer-declaration"></a>
> *构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可选</sub> [*参数子句*](#parameter-clause) **throws**<sub>可选</sub> [*构造器主体*](#initializer-body)
> *构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可选</sub> [*参数子句*](#parameter-clause) **rethrows**<sub>可选</sub> [*构造器主体*](#initializer-body)
> *构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可选</sub> [*参数子句*](#parameter-clause) **throws**<sub>可选</sub> [*构造器主体*](#initializer-body)
> *构造器声明* → [*构造器头*](#initializer-head) [*泛型形参子句*](08_Generic_Parameters_and_Arguments.md#generic-parameter-clause)<sub>可选</sub> [*参数子句*](#parameter-clause) **rethrows**<sub>可选</sub> [*构造器主体*](#initializer-body)
> <a name="initializer-head"></a>
> *构造器头* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*声明修饰符列表*](#declaration-modifiers)<sub>可选</sub> **init**
> *构造器头* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*声明修饰符列表*](#declaration-modifiers)<sub>可选</sub> **init** **?**
> *构造器头* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*声明修饰符列表*](#declaration-modifiers)<sub>可选</sub> **init** **!**
> *构造器头* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*声明修饰符列表*](#declaration-modifiers)<sub>可选</sub> **init**
> *构造器头* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*声明修饰符列表*](#declaration-modifiers)<sub>可选</sub> **init** **?**
> *构造器头* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*声明修饰符列表*](#declaration-modifiers)<sub>可选</sub> **init** **!**
> <a name="initializer-body"></a>
> *构造器主体* → [*代码块*](#code-block)
> *构造器主体* → [*代码块*](#code-block)
<a name="deinitializer_declaration"></a>
## 析构器声明
@ -1057,7 +1062,7 @@ if let actualInstance = SomeStruct(input: "Hello") {
*析构器声明 (deinitializer declaration)* 可以为类声明一个析构器。析构器没有参数,遵循如下格式:
```swift
deinit {
deinit {
语句
}
```
@ -1071,9 +1076,9 @@ deinit {
关于如何在类声明中使用析构器的例子,请参阅 [析构过程](../chapter2/15_Deinitialization.md)。
<a name="grammer_of_a_deinitializer_declaration"></a>
> 析构器声明语法
> 析构器声明语法
> <a name="deinitializer-declaration"></a>
> *析构器声明* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **deinit** [*代码块*](#code-block)
> *析构器声明* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> **deinit** [*代码块*](#code-block)
<a name="extension_declaration"></a>
## 扩展声明
@ -1081,18 +1086,19 @@ deinit {
*扩展声明 (extension declaration)* 可以扩展一个现存的类型的行为。扩展声明使用关键字 `extension`,遵循如下格式:
```swift
extension 类型名称: 采纳的协议 {
extension 类型名称: 采纳的协议 {
声明语句
}
```
```swift
extension 类型名称 where 要求 {
extension 类型名称 where 要求 {
声明语句
}
```
扩展声明体可包含零个或多个声明语句。这些声明语句可以包括计算型属性、计算型类型属性、实例方法、类型方法、构造器、下标声明,甚至是类、结构体和枚举声明。扩展声明不能包含析构器、协议声明、存储型属性、属性观察器或其他扩展声明。关于扩展声明的详细讨论,以及各种扩展声明的例子,请参阅 [扩展](../chapter2/21_Extensions.md)。
如果类型为类,结构体,或枚举类型,则扩展声明会扩展相应的类型。如果类型为协议类型,则扩展声明会扩展所有遵守这个协议的类型。在扩展的协议体中声明语句不能使用`final`标识符。
如果类型为类,结构体,或枚举类型,则扩展声明会扩展相应的类型。如果类型为协议类型,则扩展声明会扩展所有遵守这个协议的类型。在扩展的协议体中声明语句不能使用 `final` 标识符。
扩展声明可以为现存的类、结构体、枚举添加协议一致性,但是不能为类添加超类,因此在扩展声明的类型名称的冒号后面仅能指定一个协议列表。
@ -1101,13 +1107,13 @@ extension 类型名称 where 要求 {
扩展声明可以包含构造器声明。这意味着,如果被扩展的类型在其他模块中定义,构造器声明必须委托另一个在那个模块中声明的构造器,以确保该类型能被正确地初始化。
<a name="grammer_of_an_extension_declaration"></a>
> 扩展声明语法
> 扩展声明语法
> <a name="extension-declaration"></a>
> *扩展声明* → [特性](06_Attributes.md#type_attributes)<sub>可选</sub> [访问级别修饰符](#access-level-modifier)<sub>可选</sub> **extension** [*类型标识符*](03_Types.md#type-identifier) [*类型-继承-子句*](03_Types.md#type-inheritance-clause)<sub>可选</sub> [*扩展主体*](#extension-body)
> *扩展声明* → [特性](06_Attributes.md#type_attributes)<sub>可选</sub> [访问级别修饰符](#access-level-modifier)<sub>可选</sub> **extension** [*类型标识符*](03_Types.md#type-identifier) [*类型-继承-子句*](03_Types.md#type-inheritance-clause)<sub>可选</sub> [*扩展主体*](#extension-body)
> <a name="extension-body"></a>
> *扩展声明* → [特性](06_Attributes.md#type_attributes)<sub>可选</sub> [访问级别修饰符](#access-level-modifier)<sub>可选</sub> **extension** [*类型标识符*](03_Types.md#type-identifier) [*泛型-where-子句*](03_Types.md#type-inheritance-clause) [*扩展主体*](#extension-body)
> *扩展主体* → **{** [*多条声明*](#declarations)<sub>可选</sub> **}**
> *多条声明* → [单条声明](#subscript_declaration) [多条声明](#declarations) <sub>可选</sub>
> *扩展声明* → [特性](06_Attributes.md#type_attributes)<sub>可选</sub> [访问级别修饰符](#access-level-modifier)<sub>可选</sub> **extension** [*类型标识符*](03_Types.md#type-identifier) [*泛型-where-子句*](03_Types.md#type-inheritance-clause) [*扩展主体*](#extension-body)
> *扩展主体* → **{** [*多条声明*](#declarations)<sub>可选</sub> **}**
> *多条声明* → [单条声明](#subscript_declaration) [多条声明](#declarations) <sub>可选</sub>
> *单条声明* → [声明语句](#declarations) | [编译器-控制-语句](10_Statements.md#compiler-control-statement)
<a name="subscript_declaration"></a>
@ -1141,15 +1147,15 @@ subscript (参数列表) -> 返回类型 {
更多关于下标的信息和例子,请参阅 [下标](../chapter2/12_Subscripts.md)。
<a name="grammer_of_a_subscript_declaration"></a>
> 下标声明语法
> 下标声明语法
> <a name="subscript-declaration"></a>
> *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*代码块*](#code-block)
> *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*getter-setter代码块*](#getter-setter-block)
> *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*getter-setter关键字代码块*](#getter-setter-keyword-block)
> *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*代码块*](#code-block)
> *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*getter-setter 代码块*](#getter-setter-block)
> *下标声明* → [*下标头*](#subscript-head) [*下标结果*](#subscript-result) [*getter-setter 关键字代码块*](#getter-setter-keyword-block)
> <a name="subscript-head"></a>
> *下标头* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*声明修饰符列表*](#declaration-modifiers)<sub>可选</sub> **subscript** [*参数子句*](#parameter-clause)
> *下标头* → [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*声明修饰符列表*](#declaration-modifiers)<sub>可选</sub> **subscript** [*参数子句*](#parameter-clause)
> <a name="subscript-result"></a>
> *下标结果* → **->** [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*类型*](03_Types.md#type)
> *下标结果* → **->** [*特性列表*](06_Attributes.md#attributes)<sub>可选</sub> [*类型*](03_Types.md#type)
<a name="operator_declaration"></a>
## 运算符声明
@ -1168,7 +1174,7 @@ infix operator 运算符名称: 优先级组
中缀运算符是二元运算符,置于两个运算对象之间,例如加法运算符(`+`)位于表达式 `1 + 2` 的中间。
中缀运算符可以选择指定优先级组。如果没有为运算符设置优先级组Swift会设置默认优先级组`DefaultPrecedence`,它的优先级比三目优先级`TernaryPrecedence`要高,更多内容参考[*优先级组声明*](#precedence_group_declaration_modifiers)
中缀运算符可以选择指定优先级组。如果没有为运算符设置优先级组Swift 会设置默认优先级组 `DefaultPrecedence`,它的优先级比三目优先级 `TernaryPrecedence` 要高,更多内容参考[*优先级组声明*](#precedence_group_declaration_modifiers)
下面的形式声明了一个新的前缀运算符:
@ -1193,17 +1199,17 @@ postfix operator 运算符名称 {}
声明了一个新的运算符以后,需要实现一个跟这个运算符同名的函数来实现这个运算符。如果是实现一个前缀或者后缀运算符,也必须使用相符的 `prefix` 或者 `postfix` 声明修饰符标记函数声明。如果是实现中缀运算符,则不需要使用 `infix` 声明修饰符标记函数声明。关于如何实现一个新的运算符的例子,请参阅 [自定义运算符](../chapter2/25_Advanced_Operators.md#custom_operators)。
<a name="grammer_of_an_operator_declaration"></a>
> 运算符声明语法
> 运算符声明语法
<a name="operator-declaration"></a>
> *运算符声明* → [*前缀运算符声明*](#prefix-operator-declaration) | [*后缀运算符声明*](#postfix-operator-declaration) | [*中缀运算符声明*](#infix-operator-declaration)
> *运算符声明* → [*前缀运算符声明*](#prefix-operator-declaration) | [*后缀运算符声明*](#postfix-operator-declaration) | [*中缀运算符声明*](#infix-operator-declaration)
<a name="prefix-operator-declaration"></a>
> *前缀运算符声明* → **prefix** **运算符** [*运算符*](02_Lexical_Structure.md#operator) **{** **}**
> *前缀运算符声明* → **prefix** **运算符** [*运算符*](02_Lexical_Structure.md#operator) **{** **}**
> <a name="postfix-operator-declaration"></a>
> *后缀运算符声明* → **postfix** **运算符** [*运算符*](02_Lexical_Structure.md#operator) **{** **}**
> *后缀运算符声明* → **postfix** **运算符** [*运算符*](02_Lexical_Structure.md#operator) **{** **}**
> <a name="infix-operator-declaration"></a>
> *中缀运算符声明* → **infix** **运算符** [*运算符*](02_Lexical_Structure.md#operator) **{** [*中缀运算符属性*](#infix-operator-attributes)<sub>可选</sub> **}**
> *中缀运算符声明* → **infix** **运算符** [*运算符*](02_Lexical_Structure.md#operator) **{** [*中缀运算符属性*](#infix-operator-attributes)<sub>可选</sub> **}**
<a name="infix-operator-group"></a>
> *中缀运算符组* → [*优先级组名称*](#precedence-group-name)
@ -1212,7 +1218,7 @@ postfix operator 运算符名称 {}
## 优先级组声明
*优先级组声明 (A precedence group declaration)* 会向程序的中缀运算符引入一个全新的优先级组。当没有用圆括号分组时,运算符优先级反应了运算符与它的操作数的关系的紧密程度。
*优先级组声明 (A precedence group declaration)* 会向程序的中缀运算符引入一个全新的优先级组。当没有用圆括号分组时,运算符优先级反应了运算符与它的操作数的关系的紧密程度。
优先级组的声明如下所示:
```swift
@ -1222,38 +1228,40 @@ precedencegroup 优先级组名称{
associativity: 结合性
assignment: 赋值性
}
```
较低优先级组和较高优先级组的名称说明了新建的优先级组是依赖于现存的优先级组的。 `lowerThan`优先级组的属性只可以引用当前模块外的优先级组。当两个运算符为同一个操作数竞争时,比如表达式`2 + 3 * 5`,优先级更高的运算符将优先参与运算。
```
> 注意
> 使用较低和较高优先级组相互联系的优先级组必须保持单一层次关系,但它们不必是线性关系。这意味着优先级组也许会有未定义的相关优先级。这些优先级组的运算符在没有用圆括号分组的情况下是不能紧邻着使用的。
较低优先级组和较高优先级组的名称说明了新建的优先级组是依赖于现存的优先级组的。`lowerThan` 优先级组的属性只可以引用当前模块外的优先级组。当两个运算符为同一个操作数竞争时,比如表达式 `2 + 3 * 5`,优先级更高的运算符将优先参与运算。
Swift定义了大量的优先级组来与标准库的运算符配合使用例如相加(`+`)和相减(`-`)属于`AdditionPrecedence`组,相乘(`*`)和相除(`/`)属于` MultiplicationPrecedence`组详细关于Swift标准库中一系列运算符和优先级组内容参阅[Swift标准库操作符参考](https://developer.apple.com/reference/swift/1851035-swift_standard_library_operators)。
> 注意
>
> 使用较低和较高优先级组相互联系的优先级组必须保持单一层次关系,但它们不必是线性关系。这意味着优先级组也许会有未定义的相关优先级。这些优先级组的运算符在没有用圆括号分组的情况下是不能紧邻着使用的。
运算符的结合性表示在没有圆括号分组的情况下,同样优先级的一系列运算符是如何被分组的。你可以指定运算符的结合性通过上下文关键字`left``right`或者`none`,如果没有指定结合性,默认是`none`关键字。左关联性的运算符是从左至右分组的,例如,相减操作符(-)是左关联性的,所以表达式`4 - 5 - 6`被分组为`(4 - 5) - 6`,得出结果-7。右关联性的运算符是从右往左分组的指定为`none`结合性的运算符就没有结合性。同样优先级没有结合性的运算符不能相邻出现,例如`<`运算符是`none`结合性,那表示`1 < 2 < 3`就不是一个有效表达式。
Swift 定义了大量的优先级组来与标准库的运算符配合使用,例如相加(`+`)和相减(`-`)属于 `AdditionPrecedence` 组,相乘(`*`)和相除(`/`)属于 `MultiplicationPrecedence` 组,详细关于 Swift 标准库中一系列运算符和优先级组内容,参阅[Swift 标准库操作符参考](https://developer.apple.com/reference/swift/1851035-swift_standard_library_operators)。
优先级组的赋值性表示在包含可选链操作时的运算符优先级。当设为true时与优先级组对应的运算符在可选链操作中使用和标准库中赋值运算符同样的分组规则当设为false或者不设置该优先级组的运算符与不赋值的运算符遵循同样的可选链规则
运算符的结合性表示在没有圆括号分组的情况下,同样优先级的一系列运算符是如何被分组的。你可以指定运算符的结合性通过上下文关键字 `left``right` 或者 `none`,如果没有指定结合性,默认是 `none` 关键字。左关联性的运算符是从左至右分组的,例如,相减操作符(-)是左关联性的,所以表达式 `4 - 5 - 6` 被分组为 `(4 - 5) - 6`,得出结果-7。右关联性的运算符是从右往左分组的指定为 `none` 结合性的运算符就没有结合性。同样优先级没有结合性的运算符不能相邻出现,例如 `<` 运算符是 `none` 结合性,那表示 `1 < 2 < 3` 就不是一个有效表达式
优先级组的赋值性表示在包含可选链操作时的运算符优先级。当设为 true 时,与优先级组对应的运算符在可选链操作中使用和标准库中赋值运算符同样的分组规则,当设为 false 或者不设置,该优先级组的运算符与不赋值的运算符遵循同样的可选链规则。
<a name="grammer_of_a_precedence_group_declaration"></a>
> 优先级组声明语法
> 优先级组声明语法
<a name="precedence-group-declaration"></a>
> *优先级组声明* → **precedence**[*优先级组名称*](#precedence-group-name){[*多优先级组属性*](#precedence-group-attributes)<sub>可选</sub> }
> *优先级组声明* → **precedence**[*优先级组名称*](#precedence-group-name){[*多优先级组属性*](#precedence-group-attributes)<sub>可选</sub> }
<a name="precedence-group-attributes"></a>
> *优先级组属性* → [*优先级组属性*](#precedence-group-attribute)[*多优先级组属性*](#precedence-group-attributes)<sub>可选</sub> **{** **}**
> *优先级组属性* → [*优先级组属性*](#precedence-group-attribute)[*多优先级组属性*](#precedence-group-attributes)<sub>可选</sub> **{** **}**
<a name="precedence-group-attribute"></a>
> *优先级组属性* → [*优先级组关系*](#precedence-group-relation)
> *优先级组属性* → [*优先级组赋值性*](#precedence-group-assignment)
> *优先级组属性* → [*优先级组相关性*](#precedence-group-associativity)
> *优先级组属性* → [*优先级组关系*](#precedence-group-relation)
> *优先级组属性* → [*优先级组赋值性*](#precedence-group-assignment)
> *优先级组属性* → [*优先级组相关性*](#precedence-group-associativity)
> <a name="precedence-group-relation"></a>
> *优先级组关系* → **higherThan:**[*多优先级组名称*](#precedence-group-names)
> *优先级组关系* → **lowerThan:**[*多优先级组名称*](#precedence-group-names)
> *优先级组关系* → **higherThan:**[*多优先级组名称*](#precedence-group-names)
> *优先级组关系* → **lowerThan:**[*多优先级组名称*](#precedence-group-names)
> <a name="precedence-group-assignment"></a>
> *优先级组赋值* → **assignment:**[*布尔字面值*](02_Lexical_Structure.md#boolean-literal)
> *优先级组赋值* → **assignment:**[*布尔字面值*](02_Lexical_Structure.md#boolean-literal)
<a name="precedence-group-associativity"></a>
> *优先级组结合性* → **associativity:left**
> *优先级组结合性* → **associativity:right**
> *优先级组结合性* → **associativity:none**
> *优先级组结合性* → **associativity:left**
> *优先级组结合性* → **associativity:right**
> *优先级组结合性* → **associativity:none**
<a name="precedence-group-names"></a>
> *多优先级组名称* → [*优先级组名称*](#precedence-group-name) | [*优先级组名称*](#precedence-group-name) | [*优先级组名称*](#precedence-group-name)
<a name="precedence-group-name"></a>
@ -1314,12 +1322,12 @@ Swift 提供了三个级别的访问控制:`public`、`internal` 和 `private`
> 声明修饰符的语法
<a name="declaration-modifier"></a>
> *声明修饰符* → **class** | **convenience**| **dynamic** | **final** | **infix** | **lazy** | **mutating** | **nonmutating** | **optional** | **override** | **postfix** | **prefix** | **required** | **static** | **unowned** | **unowned ( safe )** | **unowned ( unsafe )** | **weak**
> 声明修饰符 → [*访问级别修饰符*](#access-level-modifier)
> *声明修饰符* → **class** | **convenience**| **dynamic** | **final** | **infix** | **lazy** | **mutating** | **nonmutating** | **optional** | **override** | **postfix** | **prefix** | **required** | **static** | **unowned** | **unowned ( safe )** | **unowned ( unsafe )** | **weak**
> 声明修饰符 → [*访问级别修饰符*](#access-level-modifier)
> <a name="declaration-modifiers"></a>
> *声明修饰符列表* → [*声明修饰符*](#declaration-modifier) [*声明修饰符列表*](#declaration-modifiers)<sub>可选</sub>
<a name="access-level-modifier"></a>
>访问级别修饰符 → **internal** | **internal ( set )**
>访问级别修饰符 → **private** | **private ( set )**
>访问级别修饰符 → **public** | **public ( set )**
> 访问级别修饰符 → **internal** | **internal ( set )**
> 访问级别修饰符 → **private** | **private ( set )**
> 访问级别修饰符 → **public** | **public ( set )**

View File

@ -9,7 +9,7 @@
> 翻译+校对:[KYawn](https://github.com/KYawn)
> 2.1
> 翻译:[小铁匠Linus](https://github.com/kevin833752)
> 翻译:[小铁匠 Linus](https://github.com/kevin833752)
本页内容包括:
@ -17,9 +17,9 @@
- [Interface Builder 使用的声明特性](#declaration_attributes_used_by_interface_builder)
- [类型特性](#type_attributes)
特性提供了有关声明和类型的更多信息。在Swift中有两种特性分别用于修饰声明和类型。
特性提供了有关声明和类型的更多信息。在 Swift 中有两种特性,分别用于修饰声明和类型。
您可以通过以下方式指定一个特性:符号`@`后跟特性的名称和特性接收的任何参数:
您可以通过以下方式指定一个特性:符号 `@` 后跟特性的名称和特性接收的任何参数:
> @ `特性名`
@ -50,38 +50,38 @@
当然,你也可以用一个星号(*)来表示上面提到的所有平台。
其余的参数,可以按照任何顺序出现,并且可以添加关于声明生命周期的附加信息,包括重要事件。
- `unavailable`参数表示该声明在指定的平台上是无效的。
- `unavailable` 参数表示该声明在指定的平台上是无效的。
- `introduced` 参数表示指定平台从哪一版本开始引入该声明。格式如下:
`introduced`=`版本号`
`introduced`=` 版本号 `
*版本号*由一个或多个正整数构成,由句点分隔的。
- `deprecated`参数表示指定平台从哪一版本开始弃用该声明。格式如下:
- `deprecated` 参数表示指定平台从哪一版本开始弃用该声明。格式如下:
`deprecated`=`版本号`
`deprecated`=` 版本号 `
可选的*版本号*由一个或多个正整数构成,由句点分隔的。省略版本号表示该声明目前已弃用,当弃用出现时无需给出任何有关信息。如果你省略了版本号,冒号(:)也可省略。
- `obsoleted` 参数表示指定平台从哪一版本开始废弃该声明。当一个声明被废弃后,它就从平台中移除,不能再被使用。格式如下:
`obsoleted`=`版本号`
`obsoleted`=` 版本号 `
*版本号*由一个或多个正整数构成,由句点分隔的。
- `message` 参数用来提供文本信息。当使用被弃用或者被废弃的声明时,编译器会抛出警告或错误信息。格式如下:
`message`=`信息内容`
`message`=` 信息内容 `
信息内容由一个字符串构成。
- `renamed` 参数用来提供文本信息,用以表示被重命名的声明的新名字。当使用声明的旧名字时,编译器会报错提示新名字。格式如下:
`renamed`=`新名字`
`renamed`=` 新名字 `
新名字由一个字符串构成。
你可以将`renamed` 参数和 `unavailable` 参数以及类型别名声明组合使用,以此向用户表示某个声明已经被重命名。当某个声明的名字在一个框架或者库的不同发布版本间发生变化时,这会相当有用。
你可以将 `renamed` 参数和 `unavailable` 参数以及类型别名声明组合使用,以此向用户表示某个声明已经被重命名。当某个声明的名字在一个框架或者库的不同发布版本间发生变化时,这会相当有用。
```swift
// 首发版本
@ -120,7 +120,7 @@ class MyClass {
`GKInspectable`
应用此属性暴露一个自定义GameplayKit组件属性给SpriteKit编辑器UI。
应用此属性,暴露一个自定义 GameplayKit 组件属性给 SpriteKit 编辑器 UI。
`objc`
@ -153,7 +153,7 @@ var enabled: Bool {
在类上使用该特性表示该类是应用程序委托类,使用该特性与调用 `NSApplicationMain`(\_:_:) 函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
如果你不想使用这个特性,可以提供一个 main.swift 文件,并在代码**顶层**调用`NSApplicationMain`(\_:_:) 函数,如下所示:
如果你不想使用这个特性,可以提供一个 main.swift 文件,并在代码**顶层**调用 `NSApplicationMain`(\_:_:) 函数,如下所示:
```swift
import AppKit
@ -167,26 +167,26 @@ NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
`NSManaged`
该特性用于修饰 `NSManagedObject` 子类中的实例方法或存储型变量属性,表明它们的实现由 `Core Data` 在运行时基于相关实体描述动态提供。对于标记了 `NSManaged` 特性的属性,`Core Data` 也会在运行时为其提供存储。应用这个特性也意味着`objc`特性。
该特性用于修饰 `NSManagedObject` 子类中的实例方法或存储型变量属性,表明它们的实现由 `Core Data` 在运行时基于相关实体描述动态提供。对于标记了 `NSManaged` 特性的属性,`Core Data` 也会在运行时为其提供存储。应用这个特性也意味着 `objc` 特性。
`testable`
在导入允许测试的编译模块时,该特性用于修饰 `import` 声明,这样就能访问被导入模块中的任何标有 `internal` 访问级别修饰符的实体,犹如它们被标记了 `public` 访问级别修饰符。测试也可以访问使用`internal`或者`public`访问级别修饰符标记的类和类成员,就像它们是`open`访问修饰符声明的。
在导入允许测试的编译模块时,该特性用于修饰 `import` 声明,这样就能访问被导入模块中的任何标有 `internal` 访问级别修饰符的实体,犹如它们被标记了 `public` 访问级别修饰符。测试也可以访问使用 `internal` 或者 `public` 访问级别修饰符标记的类和类成员,就像它们是 `open` 访问修饰符声明的。
`UIApplicationMain`
在类上使用该特性表示该类是应用程序委托类,使用该特性与调用 `UIApplicationMain`函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
在类上使用该特性表示该类是应用程序委托类,使用该特性与调用 `UIApplicationMain` 函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
如果你不想使用这个特性,可以提供一个 main.swift 文件,并在代码顶层调用 `UIApplicationMain`(\_:\_:\_:) 函数。比如,如果你的应用程序使用一个继承于 UIApplication 的自定义子类作为主要类,你可以调用 `UIApplicationMain`(\_:\_:\_:) 函数而不是使用该特性。
<a name="declaration_attributes_used_by_interface_builder"></a>
###Interface Builder 使用的声明特性
`Interface Builder` 特性是 `Interface Builder` 用来与 Xcode 同步的声明特性。`Swift` 提供了以下的 `Interface Builder` 特性:`IBAction``IBOutlet``IBDesignable`,以及`IBInspectable` 。这些特性与 Objective-C 中对应的特性在概念上是相同的。
`Interface Builder` 特性是 `Interface Builder` 用来与 Xcode 同步的声明特性。`Swift` 提供了以下的 `Interface Builder` 特性:`IBAction``IBOutlet``IBDesignable`,以及 `IBInspectable` 。这些特性与 Objective-C 中对应的特性在概念上是相同的。
`IBOutlet``IBInspectable` 用于修饰一个类的属性声明,`IBAction` 特性用于修饰一个类的方法声明,`IBDesignable` 用于修饰类的声明。
`IBAction``IBOutlet` 特性都意味着`objc`特性。
`IBAction``IBOutlet` 特性都意味着 `objc` 特性。
<a name="type_attributes"></a>
##类型特性
@ -213,7 +213,7 @@ convention 特性总是与下面的参数之一一起出现。
`escaping`
在函数或者方法声明上使用该特性,它表示参数将不会被存储以供延迟执行,这将确保参数不会超出函数调用的生命周期。在使用 `escaping` 声明特性的函数类型中访问属性和方法时不需要显式地使用 `self.`。关于如何使用 `escaping` 特性的例子,请参阅 [逃逸闭包](http://wiki.jikexueyuan.com/project/swift/chapter2/07_Closures.html)。
>特性语法
> 特性语法
> *特性 *→ @ <font color = 0x3386c8>特性名 特性参数子句</font><sub>可选</sub>

View File

@ -30,16 +30,16 @@ Swift 中的模式分为两类:一种能成功匹配任何类型的值,另
第二类模式用于全模式匹配,这种情况下你试图匹配的值在运行时可能不存在。此类模式包括枚举用例模式、可选模式、表达式模式和类型转换模式。你在 `switch` 语句的 `case` 标签中,`do` 语句的 `catch` 子句中,或者在 `if``while``guard``for-in` 语句的 `case` 条件句中使用这类模式。
> 模式语法
> 模式语法
<a name="pattern"></a>
> *模式* → [*通配符模式*](#wildcard_pattern) [*类型标注*](03_Types.md#type-annotation)<sub>可选</sub>
> *模式* → [*标识符模式*](#identifier_pattern) [*类型标注*](03_Types.md#type-annotation)<sub>可选</sub>
> *模式* → [*值绑定模式*](#value-binding-pattern)
> *模式* → [*元组模式*](#tuple-pattern) [*类型标注*](03_Types.md#type-annotation)<sub>可选</sub>
> *模式* → [*枚举用例模式*](#enum-case-pattern)
> *模式* → [*可选模式*](#optional-pattern)
> *模式* → [*类型转换模式*](#type-casting-pattern)
> *模式* → [*表达式模式*](#expression-pattern)
> *模式* → [*通配符模式*](#wildcard_pattern) [*类型标注*](03_Types.md#type-annotation)<sub>可选</sub>
> *模式* → [*标识符模式*](#identifier_pattern) [*类型标注*](03_Types.md#type-annotation)<sub>可选</sub>
> *模式* → [*值绑定模式*](#value-binding-pattern)
> *模式* → [*元组模式*](#tuple-pattern) [*类型标注*](03_Types.md#type-annotation)<sub>可选</sub>
> *模式* → [*枚举用例模式*](#enum-case-pattern)
> *模式* → [*可选模式*](#optional-pattern)
> *模式* → [*类型转换模式*](#type-casting-pattern)
> *模式* → [*表达式模式*](#expression-pattern)
<a name="wildcard_pattern"></a>
## 通配符模式Wildcard Pattern
@ -52,9 +52,9 @@ for _ in 1...3 {
}
```
> 通配符模式语法
> 通配符模式语法
<a name="wildcard-pattern"></a>
> *通配符模式* → **_**
> *通配符模式* → **_**
<a name="identifier_pattern"></a>
## 标识符模式Identifier Pattern
@ -69,9 +69,9 @@ let someValue = 42
如果一个变量或常量声明的左边是一个标识符模式,那么这个标识符模式是值绑定模式的子模式。
> 标识符模式语法
> 标识符模式语法
<a name="identifier-pattern"></a>
> *标识符模式* → [*标识符*](02_Lexical_Structure.md#identifier)
> *标识符模式* → [*标识符*](02_Lexical_Structure.md#identifier)
<a name="value-binding_pattern"></a>
## 值绑定模式Value-Binding Pattern
@ -92,9 +92,9 @@ case let (x, y):
在上面这个例子中,`let` 会分配到元组模式 `(x, y)` 中的各个标识符模式。因此,`switch` 语句中 `case let (x, y):``case (let x, let y):` 的匹配效果是一样的。
> 值绑定模式语法
> 值绑定模式语法
<a name="value-binding-pattern"></a>
> *值绑定模式* → **var** [*模式*](#pattern) | **let** [*模式*](#pattern)
> *值绑定模式* → **var** [*模式*](#pattern) | **let** [*模式*](#pattern)
<a name="tuple_pattern"></a>
## 元组模式
@ -121,13 +121,13 @@ let (a) = 2 // a: Int = 2
let (a): Int = 2 // a: Int = 2
```
> 元组模式语法
> 元组模式语法
<a name="tuple-pattern"></a>
> *元组模式* → **(** [*元组模式元素列表*](#tuple-pattern-element-list)<sub>可选</sub> **)**
> *元组模式* → **(** [*元组模式元素列表*](#tuple-pattern-element-list)<sub>可选</sub> **)**
<a name="tuple-pattern-element-list"></a>
> *元组模式元素列表* → [*元组模式元素*](#tuple-pattern-element) | [*元组模式元素*](#tuple-pattern-element) **,** [*元组模式元素列表*](#tuple-pattern-element-list)
> *元组模式元素列表* → [*元组模式元素*](#tuple-pattern-element) | [*元组模式元素*](#tuple-pattern-element) **,** [*元组模式元素列表*](#tuple-pattern-element-list)
<a name="tuple-pattern-element"></a>
> *元组模式元素* → [*模式*](#pattern)
> *元组模式元素* → [*模式*](#pattern)
<a name="enumeration_case_pattern"></a>
## 枚举用例模式Enumeration Case Pattern
@ -136,9 +136,9 @@ let (a): Int = 2 // a: Int = 2
如果你准备匹配的枚举用例有任何关联的值,则相应的枚举用例模式必须指定一个包含每个关联值元素的元组模式。关于使用 `switch` 语句来匹配包含关联值的枚举用例的例子,请参阅 [关联值](../chapter2/08_Enumerations.md#associated_values)。
> 枚举用例模式语法
> 枚举用例模式语法
<a name="enum-case-pattern"></a>
> *枚举用例模式* → [*类型标识*](03_Types.md#type-identifier)<sub>可选</sub> **.** [*枚举用例名*](05_Declarations.md#enum-case-name) [*元组模式*](#tuple-pattern)<sub>可选</sub>
> *枚举用例模式* → [*类型标识*](03_Types.md#type-identifier)<sub>可选</sub> **.** [*枚举用例名*](05_Declarations.md#enum-case-name) [*元组模式*](#tuple-pattern)<sub>可选</sub>
<a name="optional_pattern"></a>
## 可选模式Optional Pattern
@ -173,7 +173,7 @@ for case let number? in arrayOfOptinalInts {
// Found a 5
```
> 可选模式语法
> 可选模式语法
<a name="optional-pattern"></a>
> *可选模式* → [*标识符模式*](03_Types.md#type-identifier) **?**
@ -182,7 +182,7 @@ for case let number? in arrayOfOptinalInts {
有两种类型转换模式,`is` 模式和 `as` 模式。`is` 模式只出现在 `switch` 语句中的 `case` 标签中。`is` 模式和 `as` 模式形式如下:
> is `类型`
> is `类型`
> `模式` as `类型`
`is` 模式仅当一个值的类型在运行时和 `is` 模式右边的指定类型一致,或者是其子类的情况下,才会匹配这个值。`is` 模式和 `is` 运算符有相似表现,它们都进行类型转换,但是 `is` 模式没有返回类型。
@ -191,13 +191,13 @@ for case let number? in arrayOfOptinalInts {
关于使用 `switch` 语句配合 `is` 模式和 `as` 模式来匹配值的例子,请参阅 [Any 和 AnyObject 的类型转换](../chapter2/19_Type_Casting.md#type_casting_for_any_and_anyobject)。
> 类型转换模式语法
> 类型转换模式语法
<a name="type-casting-pattern"></a>
> *类型转换模式* → [*is模式*](#is-pattern) | [*as模式*](#as-pattern)
> *类型转换模式* → [*is 模式*](#is-pattern) | [*as 模式*](#as-pattern)
<a name="is-pattern"></a>
> *is模式* → **is** [*类型*](03_Types.md#type)
> *is 模式* → **is** [*类型*](03_Types.md#type)
<a name="as-pattern"></a>
> *as模式* → [*模式*](#pattern) **as** [*类型*](03_Types.md#type)
> *as 模式* → [*模式*](#pattern) **as** [*类型*](03_Types.md#type)
<a name="expression_pattern"></a>
## 表达式模式Expression Pattern
@ -236,6 +236,6 @@ default:
// 打印 “The point is at (1, 2).”
```
> 表达式模式语法
> 表达式模式语法
<a name="expression-pattern"></a>
> *表达式模式* → [*表达式*](04_Expressions.md#expression)
> *表达式模式* → [*表达式*](04_Expressions.md#expression)

View File

@ -26,7 +26,7 @@
泛型形参子句指定泛型类型或函数的类型形参,以及这些参数相关的约束和要求。泛型形参子句用尖括号(`<>`)包住,形式如下:
> <`泛型形参列表`>
> <`泛型形参列表`>
泛型形参列表中泛型形参用逗号分开,其中每一个采用以下形式:
@ -45,7 +45,7 @@ func simpleMax<T: Comparable>(_ x: T, _ y: T) -> T {
}
```
例如,因为 `Int``Double` 均满足`Comparable`协议,所以该函数可以接受这两种类型。与泛型类型相反,调用泛型函数或构造器时不需要指定泛型实参子句。类型实参由传递给函数或构造器的实参推断而出。
例如,因为 `Int``Double` 均满足 `Comparable` 协议,所以该函数可以接受这两种类型。与泛型类型相反,调用泛型函数或构造器时不需要指定泛型实参子句。类型实参由传递给函数或构造器的实参推断而出。
```swift
simpleMax(17, 42) // T 被推断为 Int 类型
@ -59,39 +59,39 @@ simpleMax(3.14159, 2.71828) // T 被推断为 Double 类型
> `where` : `类型要求`
`where` 子句中的要求用于指明该类型形参继承自某个类或符合某个协议或协议组合。尽管 `where` 子句提供了语法糖使其有助于表达类型形参上的简单约束(如 `<T: Comparable>` 等同于 `<T> where T: Comparable`,等等),但是依然可以用来对类型形参及其关联类型提供更复杂的约束,例如你可以强制形参的关联类型遵守协议,如,` <S: Sequence> where S.Iterator.Element: Equatable` 表示泛型类型 `S` 遵守`Sequence`协议并且关联类型`S.Iterator.Element`遵守`Equatable`协议,这个约束确保队列的每一个元素都是符合 `Equatable` 协议的。
`where` 子句中的要求用于指明该类型形参继承自某个类或符合某个协议或协议组合。尽管 `where` 子句提供了语法糖使其有助于表达类型形参上的简单约束(如 `<T: Comparable>` 等同于 `<T> where T: Comparable`,等等),但是依然可以用来对类型形参及其关联类型提供更复杂的约束,例如你可以强制形参的关联类型遵守协议,如,`<S: Sequence> where S.Iterator.Element: Equatable` 表示泛型类型 `S` 遵守 `Sequence` 协议并且关联类型 `S.Iterator.Element` 遵守 `Equatable` 协议,这个约束确保队列的每一个元素都是符合 `Equatable` 协议的。
也可以用操作符 `==` 来指定两个类型必须相同。例如,泛型形参子句 ` <S1: Sequence, S2: Sequence> where S1.Iterator.Element == S2.Iterator.Element` 表示 `S1``S2` 必须都符合 `SequenceType` 协议,而且两个序列中的元素类型必须相同。
也可以用操作符 `==` 来指定两个类型必须相同。例如,泛型形参子句 `<S1: Sequence, S2: Sequence> where S1.Iterator.Element == S2.Iterator.Element` 表示 `S1``S2` 必须都符合 `SequenceType` 协议,而且两个序列中的元素类型必须相同。
当然,替代类型形参的类型实参必须满足所有的约束和要求。
泛型函数或构造器可以重载,但在泛型形参子句中的类型形参必须有不同的约束或要求,抑或二者皆不同。当调用重载的泛型函数或构造器时,编译器会根据这些约束来决定调用哪个重载函数或构造器。
更多关于泛型where从句的信息和关于泛型函数声明的例子可以看一看 [泛型where子句](https://github.com/numbbbbb/the-swift-programming-language-in-chinese/blob/gh-pages/source/chapter2/23_Generics.md#where_clauses)
更多关于泛型 where 从句的信息和关于泛型函数声明的例子,可以看一看 [泛型 where 子句](https://github.com/numbbbbb/the-swift-programming-language-in-chinese/blob/gh-pages/source/chapter2/23_Generics.md#where_clauses)
> 泛型形参子句语法
> 泛型形参子句语法
<a name="generic-parameter-clause"></a>
> *泛型形参子句* → **<** [*泛型形参列表*](#generic-parameter-list) [*约束子句*](#requirement-clause)<sub>可选</sub> **>**
> *泛型形参子句* → **<** [*泛型形参列表*](#generic-parameter-list) [*约束子句*](#requirement-clause)<sub>可选</sub> **>**
<a name="generic-parameter-list"></a>
> *泛型形参列表* → [*泛形形参*](#generic-parameter) | [*泛形形参*](#generic-parameter) **,** [*泛型形参列表*](#generic-parameter-list)
> *泛型形参列表* → [*泛形形参*](#generic-parameter) | [*泛形形参*](#generic-parameter) **,** [*泛型形参列表*](#generic-parameter-list)
<a name="generic-parameter"></a>
> *泛形形参* → [*类型名称*](03_Types.html#type-name)
> *泛形形参* → [*类型名称*](03_Types.html#type-name) **:** [*类型标识符*](03_Types.html#type-identifier)
> *泛形形参* → [*类型名称*](03_Types.html#type-name) **:** [*协议合成类型*](03_Types.html#protocol-composition-type)
> *泛形形参* → [*类型名称*](03_Types.html#type-name)
> *泛形形参* → [*类型名称*](03_Types.html#type-name) **:** [*类型标识符*](03_Types.html#type-identifier)
> *泛形形参* → [*类型名称*](03_Types.html#type-name) **:** [*协议合成类型*](03_Types.html#protocol-composition-type)
<a name="requirement-clause"></a>
> *约束子句* → **where** [*约束列表*](#requirement-list)
> *约束子句* → **where** [*约束列表*](#requirement-list)
<a name="requirement-list"></a>
> *约束列表* → [*约束*](#requirement) | [*约束*](#requirement) **,** [*约束列表*](#requirement-list)
> *约束列表* → [*约束*](#requirement) | [*约束*](#requirement) **,** [*约束列表*](#requirement-list)
<a name="requirement"></a>
> *约束* → [*一致性约束*](#conformance-requirement) | [*同类型约束*](#same-type-requirement)
> *约束* → [*一致性约束*](#conformance-requirement) | [*同类型约束*](#same-type-requirement)
<a name="conformance-requirement"></a>
> *一致性约束* → [*类型标识符*](03_Types.html#type-identifier) **:** [*类型标识符*](03_Types.html#type-identifier)
> *一致性约束* → [*类型标识符*](03_Types.html#type-identifier) **:** [*协议合成类型*](03_Types.html#protocol-composition-type)
> *一致性约束* → [*类型标识符*](03_Types.html#type-identifier) **:** [*类型标识符*](03_Types.html#type-identifier)
> *一致性约束* → [*类型标识符*](03_Types.html#type-identifier) **:** [*协议合成类型*](03_Types.html#protocol-composition-type)
<a name="same-type-requirement"></a>
> *同类型约束* → [*类型标识符*](03_Types.html#type-identifier) **==** [*类型*](03_Types.html#type)
> *同类型约束* → [*类型标识符*](03_Types.html#type-identifier) **==** [*类型*](03_Types.html#type)
<a name="generic_argument"></a>
## 泛型实参子句
@ -118,10 +118,10 @@ let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
如 [泛型形参子句](#generic_parameter) 所述,不能用泛型实参子句来指定泛型函数或构造器的类型实参。
> 泛型实参子句语法
> 泛型实参子句语法
<a name="generic-argument-clause"></a>
> *泛型实参子句* → **<** [*泛型实参列表*](#generic-argument-list) **>**
> *泛型实参子句* → **<** [*泛型实参列表*](#generic-argument-list) **>**
<a name="generic-argument-list"></a>
> *泛型实参列表* → [*泛型实参*](#generic-argument) | [*泛型实参*](#generic-argument) **,** [*泛型实参列表*](#generic-argument-list)
> *泛型实参列表* → [*泛型实参*](#generic-argument) | [*泛型实参*](#generic-argument) **,** [*泛型实参列表*](#generic-argument-list)
<a name="generic-argument"></a>
> *泛型实参* → [*类型*](03_Types.html#type)
> *泛型实参* → [*类型*](03_Types.html#type)

File diff suppressed because it is too large Load Diff

View File

@ -5,15 +5,15 @@
如果您之前没有接触过权限控制,先来听一个小故事:
> 小明是五道口工业学院的一个大一新生,最近他有点烦恼,因为同屋经常用他的热水壶,好像那是自己家的一样,可是碍于同学情面,又不好意思说。直到有一天,他和学姐小K吐槽。
> 小明是五道口工业学院的一个大一新生,最近他有点烦恼,因为同屋经常用他的热水壶,好像那是自己家的一样,可是碍于同学情面,又不好意思说。直到有一天,他和学姐小 K 吐槽。
> 学姐听了之后大学集体生活里面大部分东西都是默认室友可以共用的。如果你不想别人拿我可以帮你做封印只要打上private标记它们就看不到你的东西更加用不了你的东西了。
> 学姐听了之后,说:大学集体生活里面,大部分东西都是默认室友可以共用的。如果你不想别人拿,我可以帮你做封印,只要打上 private 标记,它们就看不到你的东西,更加用不了你的东西了。
> 小明说哇靠学姐你还会妖法......
Swift语言从Xcode 6 beta 5版本起加入了对权限控制Access Control的支持。其实权限控制和小明的物品一样你可以设定水壶是只有自己能用还是只有宿舍里的人能用还是全校都可以用。
Swift 语言从 Xcode 6 beta 5版本起加入了对权限控制Access Control的支持。其实权限控制和小明的物品一样你可以设定水壶是只有自己能用还是只有宿舍里的人能用还是全校都可以用。
从此以后你可以好像神盾局局长一样完全掌控自己的代码块的”保密级别“哪些是只能在本文件引用哪些能用在整个项目里你还可以发挥大爱精神把它开源成只要导入你的框架大家都可以使用的API。
从此以后,你可以好像神盾局局长一样,完全掌控自己的代码块的”保密级别“,哪些是只能在本文件引用,哪些能用在整个项目里,你还可以发挥大爱精神,把它开源成只要导入你的框架,大家都可以使用的 API。
这三种权限分别是:
@ -23,29 +23,29 @@ Swift语言从Xcode 6 beta 5版本起加入了对权限控制Access Contro
- #####internal 内部的
标记为internal的代码块在整个应用App bundle或者框架framework的范围内都是可以访问的。
标记为 internal 的代码块在整个应用App bundle或者框架framework的范围内都是可以访问的。
- #####public 公开的
标记为public的代码块一般用来建立API这是最开放的权限使得任何人只要导入这个模块都可以访问使用。
标记为 public 的代码块一般用来建立 API这是最开放的权限使得任何人只要导入这个模块都可以访问使用。
如果要把所有的爱加上一个期限噢不是给所有的代码块都标记上权限不累死才怪。还好swift里面所有代码实体的默认权限都是最常用的internal。所以当你开发自己的App时可能完全不用管权限控制的事情。
如果要把所有的爱加上一个期限,噢不,是给所有的代码块都标记上权限,不累死才怪。还好 swift 里面所有代码实体的默认权限,都是最常用的 internal。所以当你开发自己的 App 时,可能完全不用管权限控制的事情。
但当你需要写一个公开API的时候就必须对里面的代码块进行“隐身对其可见”的public标记要么其他人是用不到的。
但当你需要写一个公开 API 的时候,就必须对里面的代码块进行“隐身对其可见”的 public 标记,要么其他人是用不到的。
Private私有级别的权限最严格它可以用来隐藏某些功能的细节实现方式。合理构筑你的代码你就可以安全地使用extension和高级功能又不把它们暴露给项目内的其他文件。
Private私有级别的权限最严格它可以用来隐藏某些功能的细节实现方式。合理构筑你的代码你就可以安全地使用 extension 和高级功能,又不把它们暴露给项目内的其他文件。
除了可以给整个声明设权限Swift还允许大家在需要的时候把某个属性property的取值权限比赋值权限设得更加开放。
除了可以给整个声明设权限Swift 还允许大家在需要的时候把某个属性property的取值权限比赋值权限设得更加开放。
#####举个例子:
```swift
public class ListItem {
// ListItem这个类有两个公开的属性
// ListItem 这个类,有两个公开的属性
public var text: String
public var isComplete: Bool
// 下面的代码表示把变量UUID的赋值权限设为private对整个app可读但值只能在本文件里写入
// 下面的代码表示把变量 UUID 的赋值权限设为 private对整个 app 可读,但值只能在本文件里写入
private(set) var UUID: NSUUID
public init(text: String, completed: Bool, UUID: NSUUID) {
@ -54,7 +54,7 @@ Private私有级别的权限最严格它可以用来隐藏某些功能
self.UUID = UUID
}
// 这段没有特别标记权限因此属于默认的internal级别。在框架目标内可用但对于其他目标不可用
// 这段没有特别标记权限,因此属于默认的 internal 级别。在框架目标内可用,但对于其他目标不可用
func refreshIdentity() {
self.UUID = NSUUID()
}
@ -68,15 +68,15 @@ Private私有级别的权限最严格它可以用来隐藏某些功能
}
```
当我们使用Objective-CSwift混合开发时需要注意
当我们使用 Objective-CSwift 混合开发时,需要注意:
- 如果你在写的是一个应用Xcode会生成一个头文件来保证两者的可互访性而这个生成的头文件会包含publicinternal级别的声明。
- 如果你在写的是一个应用Xcode 会生成一个头文件来保证两者的可互访性,而这个生成的头文件会包含 publicinternal 级别的声明。
- 如果你的最终产品是一个Swift框架头文件里只会出现标记为public级别的声明。因为框架的头文件属于公开的Objective-C接口的一部分只有public部分对Objective-C可用。
- 如果你的最终产品是一个 Swift 框架,头文件里只会出现标记为 public 级别的声明。(因为框架的头文件,属于公开的 Objective-C 接口的一部分,只有 public 部分对 Objective-C 可用。)
虽然Swift不推荐大家传播和使用第三方的框架但对于建立和分享源文件形式的框架是支持的。对于需要写框架方便应用与多个项目的开发者来说要记得把API标记为public级别。
虽然 Swift 不推荐大家传播和使用第三方的框架,但对于建立和分享源文件形式的框架是支持的。对于需要写框架,方便应用与多个项目的开发者来说,要记得把 API 标记为 public 级别。
如果您想了解更多关于权限控制的内容可以查看苹果官方最新的《The Swift Language》和《Using Swift with Cocoa and Objective-C》指南
这两本指南在iBooks里面可以下载更新喔。
这两本指南在 iBooks 里面可以下载更新喔。
本文由翻译自Apple Swift Blog https://developer.apple.com/swift/blog/?id=5
本文由翻译自 Apple Swift Blog https://developer.apple.com/swift/blog/?id=5

View File

@ -1,4 +1,4 @@
# 造个类型不是梦-白话Swift类型创建
# 造个类型不是梦-白话 Swift 类型创建
-----------------
> 翻译:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
@ -9,17 +9,17 @@
- [自定义原型](#prototype)
- [实现默认值](#imp-default)
- [支持基本布尔型初始化](#init-by-bool)
- [支持Bool类型判断](#condition-by-bool)
- [支持 Bool 类型判断](#condition-by-bool)
- [支持兼容各们各派的类型](#support-all-type)
- [完善OCBool的布尔基因体系](#make-up-type)
- [完善 OCBool 的布尔基因体系](#make-up-type)
小伙伴们Swift中的Bool类型有着非常重要的语法功能并支撑起了整个Swift体系中的逻辑判断体系经过老码的研究和学习 Bool类型本身其实是对基础Boolean类型封装小伙伴们可能咬着手指头问老码怎么一会Bool类型一会Boolean类型其区别在于前者是基于枚举的组合类型而后者则是基本类型只有两种truefalse。
小伙伴们Swift 中的 Bool 类型有着非常重要的语法功能,并支撑起了整个 Swift 体系中的逻辑判断体系,经过老码的研究和学习, Bool 类型本身其实是对基础 Boolean 类型封装,小伙伴们可能咬着手指头问老码,怎么一会 Bool 类型,一会 Boolean 类型,其区别在于,前者是基于枚举的组合类型,而后者则是基本类型,只有两种 truefalse。
<a name="prefix_expressions"></a>
####自定义原型
接下老码根据Bool的思想来创建一个OCBool类型来让小伙伴们了解一下Swift中到底是怎么玩儿的。
来我们先看一下OCBool的定义。
接下老码根据 Bool 的思想来创建一个 OCBool 类型,来让小伙伴们了解一下 Swift 中到底是怎么玩儿的。
来我们先看一下 OCBool 的定义。
#####代码示例如下:
@ -32,13 +32,13 @@ case ocFalse
#####注意:
- 代码中第2行和第3行可以合并到一行写如苹果官方Blog所写的一样
- 代码中命名需要注意OCBool是类型名所以首字母必须大写而case中的ocTrueocFalse是小类型则需要首字母小写。
- 代码中第2行和第3行可以合并到一行写如苹果官方 Blog 所写的一样
- 代码中命名需要注意OCBool 是类型名,所以首字母必须大写,而 case 中的 ocTrueocFalse 是小类型则需要首字母小写。
<a name="imp-default"></a>
####实现默认值
我们给了一个漂亮的定义不过按照传统语言的经验Bool值默认情况下是假 所以我们的OCBool也应该如此我们使用类型扩展技术增加这个默认特性
我们给了一个漂亮的定义不过按照传统语言的经验Bool 值默认情况下是假, 所以我们的 OCBool 也应该如此,我们使用类型扩展技术增加这个默认特性:
```swift
extension OCBool{
init(){
@ -49,9 +49,9 @@ extension OCBool{
#####注意:
- 代码中第1行extension关键字非常强大小伙伴们可以通过此创造出许多好玩的东西建议各位去Github上看一个名为“Swiftz”的项目它将扩展用到了极致。
- 代码中第3行self = .ocFalse语法刚入门的小伙伴们很迷糊为什么会有奇怪的点语法因为大牛ChrisSwift中增加了类型智能推断功能在苹果Blog中提到了“Context”概念就是这个意思因为这行语句是在枚举OCBool中的其上下文就是OCBool的定义体编译器当然知道.ocFalse就是OCBool.ocFalse了所以这里直接点语法非常整齐。
现在我们可以使用如下方法使用这个Bool类型。
- 代码中第1行extension 关键字,非常强大,小伙伴们可以通过此创造出许多好玩的东西,建议各位去 Github 上看一个名为“Swiftz”的项目它将扩展用到了极致。
- 代码中第3行self = .ocFalse 语法,刚入门的小伙伴们很迷糊,为什么会有奇怪的点语法,因为大牛 ChrisSwift 中增加了类型智能推断功能,在苹果 Blog 提到了“Context”概念就是这个意思因为这行语句是在枚举 OCBool 中的,其上下文就是 OCBool 的定义体,编译器当然知道.ocFalse 就是 OCBool.ocFalse 了,所以这里直接点语法,非常整齐。
现在我们可以使用如下方法使用这个 Bool 类型。
#####代码示例如下:
@ -63,7 +63,7 @@ var result1:OCBool = .ocTrue
<a name="init-by-bool"></a>
####支持基本布尔型初始化
正如上述代码所述我们只能通过类型或者枚举项目赋值这是组合类型的用法但是编码的日子里我们总是希望和truefalse直接打交道也就是说我们希望这么做
正如上述代码所述,我们只能通过类型或者枚举项目赋值,这是组合类型的用法,但是编码的日子里,我们总是希望和 truefalse 直接打交道,也就是说,我们希望这么做,
代码示例如下:
```swift
var isSuccess:OCBool = true
@ -97,7 +97,7 @@ var isSuccess:OCBool = true
#####注意:
- 代码中的第11行是重点我的类型OCBool支持了BooleanLiteralConvertible协议这个协到底是干什么的呢小伙伴们在Xcode代码编辑器按住Command键然后点击第11行中的BooleanLiteralConvertible协议名则会进入它的定义
- 代码中的第11行是重点我的类型 OCBool 支持了 BooleanLiteralConvertible 协议,这个协到底是干什么的呢,小伙伴们在 Xcode 代码编辑器,按住 Command 然后点击第11行中的 BooleanLiteralConvertible 协议名,则会进入它的定义,
#####其定义如下:
```swift
protocol BooleanLiteralConvertible {
@ -106,10 +106,10 @@ protocol BooleanLiteralConvertible {
}
```
- 这个定义中有个类方法convertFromBooleanLiteral它的参数为BooleanLiteralType类型也就是我传入的Bool类型 且返回值为实现这个协议的类型本身在我们的OCBool类型中其返回值就是OCBool本身。经过这个定义我们可以直接对OCBool类型直接进行布尔字面量初始化了。
- 这个定义中有个类方法 convertFromBooleanLiteral它的参数为 BooleanLiteralType 类型,也就是我传入的 Bool 类型, 且返回值为实现这个协议的类型本身,在我们的 OCBool 类型中,其返回值就是 OCBool 本身。经过这个定义,我们可以直接对 OCBool 类型直接进行布尔字面量初始化了。
<a name="condition-by-bool"></a>
####支持Bool类型判断
####支持 Bool 类型判断
小伙伴们不安分, 肯定想着我怎么用它实现逻辑判断,所以如果你这么写,
@ -127,7 +127,7 @@ if isSuccess {
```
/Users/tyrion-OldCoder/Documents/Learning/BoolType/BoolType/main.swift:27:4: Type 'OCBool' does not conform to protocol 'LogicValue'
```
OCBool现在只能用bool类型初始化而不能直接返回bool型小火把们还记得在《老码说编程之白话Swift江湖》中老码多次提到妈妈再也不担心我们 if a = 1{}的写法了, 因为等号不支持值返回了, 所以在if判断是后面的条件必须有返回值OCBool没有所以编译器哭了。我们解决这个问题。
OCBool 现在只能用 bool 类型初始化,而不能直接返回 bool 型,小火把们还记得在《老码说编程之白话 Swift 江湖》中,老码多次提到,妈妈再也不担心我们 if a = 1{}的写法了, 因为等号不支持值返回了, 所以在 if 判断是后面的条件必须有返回值OCBool 没有,所以编译器哭了。我们解决这个问题。
#####代码示例如下:
@ -178,14 +178,14 @@ Program ended with exit code: 0
#####注意:
- 如果小伙伴们现在用的是Beta版的Xcode注意苹果官方Blog中在代码第17行如果在Xcode Beta4下是错误的这里的协议是LogicValue而不是BooleanVue所以记得看错误提示才是好习惯。
- 注意代码第34行完美支持if判断且输出结果为“老码请你吃火锅”老码也是说说而已请不要当真。
- 如果小伙伴们现在用的是 Beta 版的 Xcode注意苹果官方 Blog 在代码第17行如果在 Xcode Beta4下是错误的这里的协议是LogicValue 而不是 BooleanVue所以记得看错误提示才是好习惯。
- 注意代码第34行完美支持 if 判断,且输出结果为“老码请你吃火锅”,老码也是说说而已,请不要当真。
<a name="support-all-type"></a>
####支持兼容各们各派的类型
小伙伴们江湖风险门派众多老码有自己的OCBool类型可能嵩山少林有自己的SSBool类型甚至连郭美美都可能有自己的MMBool类型所以OCBool必须能够识别这些类型这些各门各派的类型只要支持LogicValue协议就应该可以被识别看老码怎么做
小伙伴们,江湖风险,门派众多,老码有自己的 OCBool 类型,可能嵩山少林有自己的 SSBool 类型,甚至连郭美美都可能有自己的 MMBool 类型,所以 OCBool 必须能够识别这些类型,这些各门各派的类型,只要支持 LogicValue 协议,就应该可以被识别,看老码怎么做,
#####代码示例如下:
@ -218,16 +218,16 @@ Hello, World!
老码没钱,郭美美请你吃火锅!
Program ended with exit code: 0
```
漂亮我们的OCBool类型现在支持了所有的逻辑变量初始化。
漂亮!我们的 OCBool 类型现在支持了所有的逻辑变量初始化。
#####注意:
- 代码中第2行“_”下横杠的用法这是一个功能强大的小强在此的目的是屏蔽外部参数名所以小伙伴们可以直接var ocResult:OCBool = OCBool(mmResult)而不是var ocResult:OCBool = OCBool(v: mmResult)小伙伴们惊呆了这个init函数中本来就没有外部参数名啊还记得老码在书里说过没Swift的初始化函数会默认使用内部参数名作为外部参数名。
- 代码中第2行“_”下横杠的用法这是一个功能强大的小强在此的目的是屏蔽外部参数名所以小伙伴们可以直接var ocResult:OCBool = OCBool(mmResult)而不是var ocResult:OCBool = OCBool(v: mmResult),小伙伴们惊呆了!这个 init 函数中本来就没有外部参数名啊还记得老码在书里说过没Swift 的初始化函数会默认使用内部参数名,作为外部参数名。
<a name="make-up-type"></a>
####完善OCBool的布尔基因体系
####完善 OCBool 的布尔基因体系:
小伙伴们bool类型的价值就是在于各种判断诸如==!=, &|,^,!以及各种组合逻辑运算我们OCBool也要具备这些功能否则就会基因缺陷且看老码如何实现
小伙伴们bool 类型的价值就是在于各种判断,诸如==!=, &|,^,!,以及各种组合逻辑运算,我们 OCBool 也要具备这些功能,否则就会基因缺陷,且看老码如何实现:
```swift
extension OCBool: Equatable{
@ -294,6 +294,6 @@ if (isHasMoney | isHasHealty) & isHasHealty{
}
```
好了到这里就到这里了窗外的雷声叫醒了老码现在应该去吃饭了以上老码给大家展示了如果制造一个自己的类型记得老码的示例是在Xcode6 Beta4下测试的至于Beta5的改变还没有涉及小伙伴们要好生练习以后各种自定类型都是基于这个思想。还有这个章节不是老码的原创老码认真的阅读了苹果的官方博客且自己的练习总结如果小伙伴们费了吃奶的劲还是看不懂请找度娘谷歌还是看不懂请到老码官方微博http://weibo.com/u/5241713117咆哮。
好了,到这里就到这里了,窗外的雷声叫醒了老码,现在应该去吃饭了,以上老码给大家展示了如果制造一个自己的类型,记得老码的示例是在 Xcode6 Beta4下测试的至于 Beta5的改变还没有涉及小伙伴们要好生练习以后各种自定类型都是基于这个思想。还有这个章节不是老码的原创老码认真的阅读了苹果的官方博客且自己的练习总结如果小伙伴们费了吃奶的劲还是看不懂请找度娘谷歌还是看不懂请到老码官方微博http://weibo.com/u/5241713117咆哮。
本文由翻译自Apple Swift Blog https://developer.apple.com/swift/blog/?id=8
本文由翻译自 Apple Swift Blog https://developer.apple.com/swift/blog/?id=8

View File

@ -1,16 +1,16 @@
# WWDC里面的那个“大炮打气球”
# WWDC 里面的那个“大炮打气球”
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 校对:[老码团队翻译组-](Jame)
![Ballon playground](https://devimages.apple.com.edgekey.net/swift/images/swift-screenshot.jpg)
很多小伙伴说对WWDC上介绍Swift语言时演示的那个“大炮打气球”的Ballons项目很感兴趣。
很多小伙伴说,对 WWDC 上介绍 Swift 语言时,演示的那个“大炮打气球”的 Ballons 项目很感兴趣。
Ballons不但展现了playgrounds许多很赞的特性还让我们看到写代码的过程原来可以这么互动这么好玩。
Ballons 不但展现了 playgrounds 许多很赞的特性,还让我们看到写代码的过程,原来可以这么互动,这么好玩。
现在你可以下载这个[Ballons.playground](https://developer.apple.com/swift/blog/downloads/Balloons.zip)的教学版本,学习这些有趣的效果是怎么实现的。教学版本里除了源文件,还有相关说明文档,我们还出了一些小小的实验题,你可以动手修改代码,然后在右侧马上看到效果。
这个playground文件用到了SpriteKit的新特性因此需要最新beta版本的Xcode 6和Yosemite系统来支持它运行。
这个 playground 文件用到了 SpriteKit 的新特性,因此需要最新 beta 版本的 Xcode 6和 Yosemite 系统来支持它运行。
本文由翻译自Apple Swift Blog的博文[Ballons](https://developer.apple.com/swift/blog/?id=9)
本文由翻译自 Apple Swift Blog 的博文:[Ballons](https://developer.apple.com/swift/blog/?id=9)

View File

@ -1,7 +1,7 @@
# Swift与C语言指针友好合作
# Swift 与 C 语言指针友好合作
-----------------
> 翻译:[老码团队翻译组-Relly](http://weibo.com/penguinliong/)
> 翻译:[老码团队翻译组-Relly](http://weibo.com/penguinliong/)
> 校对:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
本页包含内容:
@ -11,16 +11,16 @@
- [用作字符串参数的指针](#string-as-para-pointer)
- [指针参数转换的安全性](#security-of-pointer-cast)
Objective-C和C的API常常会需要用到指针。Swift中的数据类型都原生支持基于指针的Cocoa API不仅如此Swift会自动处理部分最常用的将指针作为参数传递的情况。这篇文章中我们将着眼于在Swift中让C语言指针与变量、数组和字符串共同工作。
Objective-C 和 C 的 API 常常会需要用到指针。Swift 中的数据类型都原生支持基于指针的 Cocoa API不仅如此Swift 会自动处理部分最常用的将指针作为参数传递的情况。这篇文章中,我们将着眼于在 Swift 中让 C 语言指针与变量、数组和字符串共同工作。
####用以输入/输出的参数指针
CObjective-C并不支持多返回值所以Cocoa API中常常将指针作为一种在方法间传递额外数据的方式。Swift允许指针被当作`inout`参数使用,所以你可以用符号`&`将对一个变量的引用作为指针参数传递。举例来说:`UIColor`中的`getRed(_:green:blue:alpha:)`方法需要四个`CGFloat*`指针来接收颜色的组成信息,我们使用`&`来将这些组成信息捕获为本地变量:
CObjective-C 并不支持多返回值,所以 Cocoa API 中常常将指针作为一种在方法间传递额外数据的方式。Swift 允许指针被当作 `inout` 参数使用,所以你可以用符号 `&` 将对一个变量的引用作为指针参数传递。举例来说:`UIColor` 中的 `getRed(_:green:blue:alpha:)` 方法需要四个 `CGFloat*` 指针来接收颜色的组成信息,我们使用 `&` 来将这些组成信息捕获为本地变量:
```swift
var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
color.getRed(&r, green: &g, blue: &b, alpha: &a)
```
另一种常见的情况是Cocoa`NSError`的习惯用法。许多方法会使用一个`NSError**`参数来储存可能的错误的信息。举例来说:我们用`NSFileManager``contentOfDirectoryAtPath(_:error:)`方法来将目录下的内容列表,并将潜在的错误指向一个`NSError?`变量:
另一种常见的情况是 Cocoa`NSError` 的习惯用法。许多方法会使用一个 `NSError**` 参数来储存可能的错误的信息。举例来说:我们用 `NSFileManager``contentOfDirectoryAtPath(_:error:)` 方法来将目录下的内容列表,并将潜在的错误指向一个 `NSError?` 变量:
```swift
var maybeError: NSError?
if let contents = NSFileManager.defaultManager()
@ -30,11 +30,11 @@ if let contents = NSFileManager.defaultManager()
// Handle the error
}
```
为了安全性Swift要求被使用`&`传递的变量已经初始化。因为无法确定这个方法会不会在写入数据前尝试从指针中读取数据。
为了安全性Swift 要求被使用 `&` 传递的变量已经初始化。因为无法确定这个方法会不会在写入数据前尝试从指针中读取数据。
####作为数组使用的参数指针
C语言中数组和指针的联系十分紧密而Swift允许数组能够作为指针使用从而与基于数组的C语言API协同工作更加简单。一个固定的数组可以使用一个常量指针直接传递一个变化的数组可以用`&`运算符将一个非常量指针传递。就和输入/输出参数指针一样。举例来说我们可以用Accelerate框架中的`vDSP_vadd`方法让两个数组`a``b`相加,并将结果写入第三个数组`result`
C 语言中,数组和指针的联系十分紧密,而 Swift 允许数组能够作为指针使用,从而与基于数组的 C 语言 API 协同工作更加简单。一个固定的数组可以使用一个常量指针直接传递,一个变化的数组可以用 `&` 运算符将一个非常量指针传递。就和输入/输出参数指针一样。举例来说:我们可以用 Accelerate 框架中的 `vDSP_vadd` 方法让两个数组 `a``b` 相加,并将结果写入第三个数组 `result`
```swift
import Accelerate
@ -49,7 +49,7 @@ vDSP_vadd(a, 1, b, 1, &result, 1, 4)
## 用作字符串参数的指针
C语言中用`cont char*`指针来作为传递字符串的基本方式。Swift中的`String`可以被当作一个无限长度UTF-8编码的`const char*`指针来传递给方法。举例来说:我们可以直接传递一个字符串给一个标准C和POSIX库方法
C 语言中用 `cont char*` 指针来作为传递字符串的基本方式。Swift 中的 `String` 可以被当作一个无限长度 UTF-8编码的 `const char*` 指针来传递给方法。举例来说:我们可以直接传递一个字符串给一个标准 C 和 POSIX 库方法
```swift
puts("Hello from libc")
let fd = open("/tmp/scratch.txt", O_WRONLY|O_CREAT, 0o666)
@ -65,9 +65,9 @@ if fd < 0 {
## 指针参数转换的安全性
Swift很努力地使与C语言指针的交互更加便利因为它们广泛地存在于Cocoa之中同时保持一定的安全性。然而相比你的其他Swift代码与C语言的指针交互具有潜在的不安全性,所以务必要小心使用。其中特别要注意:
- 如果被调用者为了在其返回值之后再次使用而保存了C指针的数据那么这些转换使用起来并不安全。转换后的指针仅在调用期间保证有效。甚至你将同样的变量、数组或字符串作为多指针参数再次传递你每次都会收到一个不同的指针。这个异常将全局或静态地储存为变量。你可以安全地将这段地址当作永久唯一的指针使用。例如作为一个KVO上下文参数使用的时候。
Swift 很努力地使与 C 语言指针的交互更加便利,因为它们广泛地存在于 Cocoa 之中,同时保持一定的安全性。然而,相比你的其他 Swift 代码与 C 语言的指针交互具有潜在的不安全性,所以务必要小心使用。其中特别要注意:
- 如果被调用者为了在其返回值之后再次使用而保存了 C 指针的数据,那么这些转换使用起来并不安全。转换后的指针仅在调用期间保证有效。甚至你将同样的变量、数组或字符串作为多指针参数再次传递,你每次都会收到一个不同的指针。这个异常将全局或静态地储存为变量。你可以安全地将这段地址当作永久唯一的指针使用。例如:作为一个 KVO 上下文参数使用的时候。
- 当指针类型为`Array``String`时,溢出检查不是强制进行的。 基于C语言的API无法增加数组和字符串大小所以在你将其传递到基于C语言的API之前你必须确保数组或字符的大小正确。
- 当指针类型为 `Array``String` 时,溢出检查不是强制进行的。 基于 C 语言的 API 无法增加数组和字符串大小,所以在你将其传递到基于 C 语言的 API 之前,你必须确保数组或字符的大小正确。
如果你需要使用基于指针的API时没有遵守以上指导或是你重写了接受指针参数的Cocoa方法于是你可以在Swift中直接用不安全的指针来使用未经处理的内存。在未来的文章中我们将着眼于更加高级的情况。
如果你需要使用基于指针的 API 时没有遵守以上指导,或是你重写了接受指针参数的 Cocoa 方法,于是你可以在 Swift 中直接用不安全的指针来使用未经处理的内存。在未来的文章中我们将着眼于更加高级的情况。

View File

@ -1,7 +1,7 @@
# Swift里的值类型与引用类型
# Swift 里的值类型与引用类型
-----------------
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 校对:[老码团队翻译组-Jame](http://weibo.com/u/5241713117)
本页包含内容:
@ -10,10 +10,10 @@
- [Mutation修改在安全中扮演的角色](#act-in=mutation)
- [如何选择类型](#how-to-choose)
### Swift里面的类型分为两种
### Swift 里面的类型分为两种:
* **值类型(Value Types)**:每个实例都保留了一分独有的数据拷贝,一般以结构体 `struct``枚举enum` 或者`元组tuple`的形式出现。
* **引用类型(Reference Type)**:每个实例共享同一份数据来源,一般以`类class`的形式出现。
* **值类型(Value Types)**:每个实例都保留了一分独有的数据拷贝,一般以结构体 `struct`` 枚举enum` 或者 `元组tuple`的形式出现。
* **引用类型(Reference Type)**:每个实例共享同一份数据来源,一般以 `类class`的形式出现。
在这篇博文里面,我们会介绍两种类型各自的优点,以及应该怎么选择使用。
@ -26,8 +26,8 @@
// 下面是一个值类型的例子
struct S { var data: Int = -1 }
var a = S()
var b = a // b是a的拷贝
a.data = 42 // 更改a的数据b的不受影响
var b = a // b 是 a 的拷贝
a.data = 42 // 更改 a 的数据b 的不受影响
println("\(a.data), \(b.data)") // 输出结果 "42, -1"
```
@ -39,24 +39,24 @@
// 下面是一个引用类型的例子
class C { var data: Int = -1 }
var x = C()
var y = x // y是x的拷贝
x.data = 42 // 更改x的数据等于同时修改了y
var y = x // y 是 x 的拷贝
x.data = 42 // 更改 x 的数据,等于同时修改了 y
println("\(x.data), \(y.data)") // 输出结果 "42, 42"
```
<a name="act-in=mutation"></a>
#### Mutation修改在安全中扮演的角色
值类型较引用类型来说会让你更容易在大量代码中理清状况。如果你总是得到一个独立的拷贝出来的实例你就可以放心它不会被你app里面的其他部分代码默默地修改。这在多线程的环境里面是尤为重要的因为另外一个线程可能会在暗地里修改你的数据。因此可能会造成严重的程序错误这在调试过程中非常难以排除。
值类型较引用类型来说,会让你更容易在大量代码中理清状况。如果你总是得到一个独立的拷贝出来的实例,你就可以放心它不会被你 app 里面的其他部分代码默默地修改。这在多线程的环境里面是尤为重要的,因为另外一个线程可能会在暗地里修改你的数据。因此可能会造成严重的程序错误,这在调试过程中非常难以排除。
由于差别主要在于修改数据的后果,那么当实例的数据只读,不存在需要更改的情况下,用哪种类型都是没有分别的。
你可能在想,有的时候我可能也需要一个完全不变的类。这样使用`Cocoa NSObject`对象的时候会比较容易又可以保留值语义的好处。在今天你可以通过只使用不可变的存储属性和避开任何可以修改状态的API用Swift写出一个不可变类`immutable class`。实际上很多基本的Cocoa类例如`NSURL`都是设计成不可变类的。然而Swift语言目前只强制`struct``enum`这种值类型的不可变性,对类这种引用类型则没有。(例如还不支持强制将子类的限制为不可变类)
你可能在想,有的时候我可能也需要一个完全不变的类。这样使用 `Cocoa NSObject` 对象的时候会比较容易,又可以保留值语义的好处。在今天,你可以通过只使用不可变的存储属性,和避开任何可以修改状态的 API Swift 写出一个不可变类 `immutable class`。实际上,很多基本的 Cocoa 类,例如 `NSURL`都是设计成不可变类的。然而Swift 语言目前只强制 `struct``enum` 这种值类型的不可变性,对类这种引用类型则没有。(例如还不支持强制将子类的限制为不可变类)
<a name="how-to-choose"></a>
#### 如何选择类型?
所以当我们想要建立一个新的类型的时候怎么决定用值类型还是引用类型呢当你使用Cocoa框架的时候很多API都要通过NSObject的子类使用所以这时候必须要用到引用类型class。在其他情况下有下面几个准则
所以当我们想要建立一个新的类型的时候,怎么决定用值类型还是引用类型呢?当你使用 Cocoa 框架的时候,很多 API 都要通过 NSObject 的子类使用,所以这时候必须要用到引用类型 class。在其他情况下有下面几个准则
* **什么时候该用值类型**
* 要用==运算符来比较实例的数据时
@ -67,9 +67,9 @@
* 要用==运算符来比较实例身份的时候
* 你希望有创建一个共享的、可变对象的时候
在Swift里面数组(Array)、字符串(String)、字典(Dictionary)都属于值类型。它们就像C语言里面简单的int值是一个个独立的数据个体。你不需要花任何功夫来防范其他代码在暗地里修改它们。更重要的是你可以在线程之间安全的传递变量而不需要特地去同步。在Swift高安全性的精神下这个模式会帮助你用Swift写出更可控的代码。
Swift 里面,数组(Array)、字符串(String)、字典(Dictionary)都属于值类型。它们就像 C 语言里面简单的 int 值,是一个个独立的数据个体。你不需要花任何功夫来防范其他代码在暗地里修改它们。更重要的是,你可以在线程之间安全的传递变量,而不需要特地去同步。在 Swift 高安全性的精神下,这个模式会帮助你用 Swift 写出更可控的代码。
-----------------
本章节不是老码的原创,老码认真的阅读了苹果的官方博客,且自己的练习总结,如果小伙伴们费了吃奶的劲还是看不懂,请找度娘谷歌,还是看不懂请到老码[官方微博](http://weibo.com/u/5241713117)咆哮。
本章节不是老码的原创,老码认真的阅读了苹果的官方博客,且自己的练习总结,如果小伙伴们费了吃奶的劲还是看不懂,请找度娘谷歌,还是看不懂请到老码[官方微博](http://weibo.com/u/5241713117)咆哮。
##### 本文由翻译自Apple Swift Blog [Value and Reference Types](https://developer.apple.com/swift/blog/?id=10)
##### 本文由翻译自 Apple Swift Blog [Value and Reference Types](https://developer.apple.com/swift/blog/?id=10)

View File

@ -1,33 +1,33 @@
# 访问控制和protected
# 访问控制和 protected
-----------------
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 校对:[老码团队翻译组-Jame](http://weibo.com/u/5241713117)
原文再续,书折第一回。
很多其他编程语言都有一种”protected“设定可以限制某些类方法只能被它的子类所使用。
Swift支持了访问控制后大家给我们的反馈都很不错。而有的开发者问我们“为什么Swift没有类似protected的选项
Swift 支持了访问控制后,大家给我们的反馈都很不错。而有的开发者问我们:“为什么 Swift 没有类似 protected 的选项?”
**当我们在设计Swift访问控制的不同等级时我们认为有两种主要场景**
**当我们在设计 Swift 访问控制的不同等级时,我们认为有两种主要场景:**
* 在一个APP里隐藏某个类的私密细节。
* 在一个开源框架里不让导入这个框架的APP随便接触框架的内部实现细节。
* 在一个 APP 里:隐藏某个类的私密细节。
* 在一个开源框架里:不让导入这个框架的 APP随便接触框架的内部实现细节。
上面的两种常见情况对应着privateinternal这两个等级。
上面的两种常见情况,对应着 privateinternal 这两个等级。
而protected相当于把访问控制和继承特性混在一起把访问控制的等级设定增加了一个维度使之复杂化。即使设定了protected子类还是可以通过新的公开方法、新的属性来接触到所谓“protected”了的API。另一方面我们可以在各种地方重写一个方法所谓的保护却没有提供优化机制。这种设定往往在做不必要的限制 一 protected允许了子类但又禁止所有其他别的类包括那些帮助子类实现某些功能的类接触父类的成员。
protected 相当于把访问控制和继承特性混在一起,把访问控制的等级设定增加了一个维度,使之复杂化。即使设定了 protected子类还是可以通过新的公开方法、新的属性来接触到所谓“protected”了的 API。另一方面我们可以在各种地方重写一个方法所谓的保护却没有提供优化机制。这种设定往往在做不必要的限制 一 protected 允许了子类,但又禁止所有其他别的类(包括那些帮助子类实现某些功能的类)接触父类的成员。
有的开发者指出apple的框架有时候也会把给子类用的API分隔出来。这时候protected不就有用了吗我们研究后发现这些方法一般属于下面两种情况一是这些方法对子类以外的类没啥用所以不需要严格保护例如上面说的协助实现某些功能的类。二是这些方法就是设计出来被重写而不是直接用的。举个例子`drawRect(_:) `就是在UIKit基础上使用的方法但它不能在UIKit以外应用。
有的开发者指出apple 的框架有时候也会把给子类用的 API 分隔出来。这时候 protected 不就有用了吗?我们研究后发现,这些方法一般属于下面两种情况:一是这些方法对子类以外的类没啥用,所以不需要严格保护(例如上面说的协助实现某些功能的类)。二是这些方法就是设计出来被重写,而不是直接用的。举个例子,`drawRect(_:)` 就是在 UIKit 基础上使用的方法,但它不能在 UIKit 以外应用。
除此之外如果有了protected它要怎么样和extension相互作用呢一个类的extension能接触它的protected成员吗一个子类的extension可以接触父类的protected成员吗extension声明的位置对访问控制等级有没有影响呢复杂到要哭了是不是
除此之外,如果有了 protected它要怎么样和 extension 相互作用呢?一个类的 extension 能接触它的 protected 成员吗?一个子类的 extension 可以接触父类的 protected 成员吗extension 声明的位置对访问控制等级有没有影响呢?(复杂到要哭了是不是?)
对访问控制的设计也依循了ObjectiveC开发者包括apple内外的的常规做法。ObjectiveC方法和属性一般在.h头文件里声明但也可以写在.m实现文件里。假如有一个公开的类想把里面某些部分设为只有框架内可以获取时开发者一般会创建另一个头文件给内部使用。以上三种访问级别就对应了Swift里面的publicprivateinternal。
对访问控制的设计,也依循了 ObjectiveC 开发者(包括 apple 内外的的常规做法。ObjectiveC 方法和属性一般在.h 头文件里声明,但也可以写在.m 实现文件里。假如有一个公开的类,想把里面某些部分设为只有框架内可以获取时,开发者一般会创建另一个头文件给内部使用。以上三种访问级别,就对应了 Swift 里面的 publicprivateinternal。
Swift的访问控制等级和继承无关是单维度、非常清楚明了的。我们认为这样的模式更简洁同时满足了最主要的需求将一个类、或一个框架的实现细节隔离保护起来。这可能和你以前用过的不同但我们鼓励你试试看。
Swift 的访问控制等级和继承无关,是单维度、非常清楚明了的。我们认为这样的模式更简洁,同时满足了最主要的需求:将一个类、或一个框架的实现细节隔离保护起来。这可能和你以前用过的不同,但我们鼓励你试试看。
-----------------
本章节不是老码的原创,是老码认真的阅读了苹果的官方博客,自己的练习总结,如果小伙伴们费了吃奶的劲还是看不懂,请找度娘谷歌。还是看不懂?请到老码[官方微博](http://weibo.com/u/5241713117)咆哮。
本章节不是老码的原创,是老码认真的阅读了苹果的官方博客,自己的练习总结,如果小伙伴们费了吃奶的劲还是看不懂,请找度娘谷歌。还是看不懂?请到老码[官方微博](http://weibo.com/u/5241713117)咆哮。
##### 本文由翻译自Apple Swift Blog [Access Control and Protected](原文地址https://developer.apple.com/swift/blog/?id=11)
##### 本文由翻译自 Apple Swift Blog [Access Control and Protected](原文地址https://developer.apple.com/swift/blog/?id=11)

View File

@ -1,24 +1,24 @@
# 可选类型完美解决占位问题
-----------------
> 翻译:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
> 翻译:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
> 校对:[老码团队翻译组-Ayra](http://weibo.com/littlekok/)
本页包含内容:
- [为Dictionary增加objectsForKeys函数](#add-function)
- [Swift中更简便的方法](##easy-function)
- [ Dictionary 增加 objectsForKeys 函数](#add-function)
- [Swift 中更简便的方法](##easy-function)
- [内嵌可选类型](#nested-optional)
- [提供一个默认值](#provide-default)
可选类型是Swift中新引入的功能很强大。在这篇博文里讨论的是在Swift里如何通过可选类型来保证强类型的安全性。作为例子我们来创建一个Objective-C APISwift版本但实际上Swift本身并不需要这样的API。
可选类型是 Swift 中新引入的,功能很强大。在这篇博文里讨论的,是在 Swift 里,如何通过可选类型来保证强类型的安全性。作为例子,我们来创建一个 Objective-C APISwift 版本,但实际上 Swift 本身并不需要这样的 API。
<a name="#add-function"></a>
#### 为Dictionary增加objectsForKeys函数
#### 为 Dictionary 增加 objectsForKeys 函数
在Objective-C中```NSDictionary```有一个方法```-objectsForKeys:NoFoundMarker:```, 这个方法需要一个```NSArray```数组作为键值参数,然后返回一个包含相关值的数组。文档里写到:"返回数组中的第N个值,和输入数组中的第N个值相对应",那如果有某个键值在字典里不存在呢?于是就有了```notFoundMarker```作为返回提示。比如第三个键值没有找到,那么在返回数组中第三个值就是这个```notFoundMarker```,而不是字典中的第三个值,但是这个值只是用来提醒原字典中没有找到对应值,但在返回数组中该元素存在,且用```notFoundMarker```作为占位符因为这个对象不能直接使用所以在Foundation框架中有个专门的类处理这个情况```NSNull```。
Objective-C 中,```NSDictionary```有一个方法```-objectsForKeys:NoFoundMarker:```, 这个方法需要一个```NSArray```数组作为键值参数,然后返回一个包含相关值的数组。文档里写到:"返回数组中的第 N 个值,和输入数组中的第 N 个值相对应",那如果有某个键值在字典里不存在呢?于是就有了```notFoundMarker```作为返回提示。比如第三个键值没有找到,那么在返回数组中第三个值就是这个```notFoundMarker```,而不是字典中的第三个值,但是这个值只是用来提醒原字典中没有找到对应值,但在返回数组中该元素存在,且用```notFoundMarker```作为占位符,因为这个对象不能直接使用,所以在 Foundation 框架中有个专门的类处理这个情况:```NSNull```。
在Swift中```Dictionary```类没有类似```objectsForKeys```的函数,为了说明问题,我们动手加一个,并且使其成为操作字典值的通用方法。我们可以用```extension```来实现:
Swift 中,```Dictionary```类没有类似```objectsForKeys```的函数,为了说明问题,我们动手加一个,并且使其成为操作字典值的通用方法。我们可以用```extension```来实现:
```swift
extension Dictionary{
@ -28,7 +28,7 @@ extension Dictionary{
}
```
以上就是我们实现的Swift版本这个和Objective-C版本有很大区别。在Swift中因为其强类型的原因限制了返回的结果数组只能包含单一类型的元素所以我们不能放```NSNull```在字符串数组中但是Swift有更好的选择我们可以返回一个可选类型数据。我们所有的值都封包在可选类型中而不是```NSNull```, 我们只用```nil```就可以了。
以上就是我们实现的 Swift 版本,这个和 Objective-C 版本有很大区别。在 Swift 中,因为其强类型的原因限制了返回的结果数组只能包含单一类型的元素,所以我们不能放```NSNull```在字符串数组中但是Swift 有更好的选择,我们可以返回一个可选类型数据。我们所有的值都封包在可选类型中,而不是```NSNull```, 我们只用```nil```就可以了。
```swift
extension Dictionary{
@ -44,9 +44,9 @@ extension Dictionary{
```
<a name="#easy-function"></a>
#### Swift中更简便的方法
#### Swift 中更简便的方法
小伙伴们可能会问为什么Swift中不需要实现这么一个API呢其实其有更简单的实现如下面代码所示
小伙伴们可能会问,为什么 Swift 中不需要实现这么一个 API 呢?其实其有更简单的实现,如下面代码所示:
```swift
extension Dictionary {
@ -56,7 +56,7 @@ extension Dictionary {
}
```
上述方式实现的功能和最开始的方法实现的功能相同,虽然核心的功能是封装了```map```的调用这个例子也说明了为什么Swift没有提供轻量级的API接口因为小伙伴们简单的调用```map```就可以实现。
上述方式实现的功能和最开始的方法实现的功能相同,虽然核心的功能是封装了```map```的调用,这个例子也说明了为什么 Swift 没有提供轻量级的 API 接口,因为小伙伴们简单的调用```map```就可以实现。
接下来,我们实验几个例子:
@ -102,7 +102,7 @@ var last:T? { get }
很明显```last```属性的类型是数组元素类型的可选类型,这种情况下,因为元素类型是```(String?)```,那么再结合返回的类型,于是其结果就是```String??```了,这就是所谓的嵌套可选类型。但嵌套可选类型本质是什么意思呢?
如果在Objective-C中重新调用上述方法我们将使用```NSNull```作为占位符Objective-C的调用语法如下所示
如果在 Objective-C 中重新调用上述方法,我们将使用```NSNull```作为占位符Objective-C 的调用语法如下所示:
```swift
[dict valuesForKeys:@[@"1", @"4"] notFoundMarker:[NSNull null]].lastObject
@ -113,7 +113,7 @@ var last:T? { get }
// nil
```
不管是Swift版本还是Objective-C版本返回值为```nil```都意味数组是空的,所以它就没有最后一个元素。 但是如果返回是```Optional(nil)```或者Objective-C中的```NSNull```都表示数组中的最后一个元素存在但是元素的内容是空的。在Objective-C中只能借助```NSNull```作为占位符来达到这个目的但是Swift却可以语言系统类型的角度的实现。
不管是 Swift 版本还是 Objective-C 版本,返回值为```nil```都意味数组是空的,所以它就没有最后一个元素。 但是如果返回是```Optional(nil)```或者 Objective-C 中的```NSNull```都表示数组中的最后一个元素存在,但是元素的内容是空的。在 Objective-C 中只能借助```NSNull```作为占位符来达到这个目的,但是 Swift 却可以语言系统类型的角度的实现。
<a name="#provide-default"></a>
#### 提供一个默认值
@ -132,9 +132,9 @@ extension Dictionary {
dict.valuesForKeys(["1", "5"], notFoundMarker: "Anonymous")
```
和Objective-C相比其需要占位符来达到占位的目的但是Swift却已经从语言类型系统的层面原生的支持了这种用法同时提供了丰富的语法功能。这就是Swift可选类型的强大之处。同时注意上述例子中用到了空合运算符```??```。
Objective-C 相比,其需要占位符来达到占位的目的,但是 Swift 却已经从语言类型系统的层面原生的支持了这种用法,同时提供了丰富的语法功能。这就是 Swift 可选类型的强大之处。同时注意上述例子中用到了空合运算符```??```。
-----------------
本章节不是老码的原创,是老码认真的阅读了苹果的官方博客,自己的练习总结,如果小伙伴们费了吃奶的劲还是看不懂,请找度娘谷歌。还是看不懂?请到老码[官方微博](http://weibo.com/u/5241713117)咆哮。
本章节不是老码的原创,是老码认真的阅读了苹果的官方博客,自己的练习总结,如果小伙伴们费了吃奶的劲还是看不懂,请找度娘谷歌。还是看不懂?请到老码[官方微博](http://weibo.com/u/5241713117)咆哮。
##### 本文由翻译自Apple Swift Blog [Optionals Case Study: valuesForKeys](https://developer.apple.com/swift/blog/?id=12)
##### 本文由翻译自 Apple Swift Blog [Optionals Case Study: valuesForKeys](https://developer.apple.com/swift/blog/?id=12)

View File

@ -2,29 +2,29 @@
> [Swift 开发者社区](http://swiftist.org)
<!-- -->
> 如果你觉得这个项目不错,请[点击Star一下](https://github.com/numbbbbb/the-swift-programming-language-in-chinese),您的支持是我们最大的动力。
> 如果你觉得这个项目不错,请[点击 Star 一下](https://github.com/numbbbbb/the-swift-programming-language-in-chinese),您的支持是我们最大的动力。
<!-- -->
> 关于文档中翻译错误,逻辑错误以及疑难问题答疑,请关注["@老码团队"](http://weibo.com/u/5241713117
)官方微博,会有技术人员统一收集答疑
# The Swift Programming Language 中文版####
# The Swift Programming Language 中文版
###这一次,让中国和世界同步
现在是6月12日凌晨4:38我用了整整一晚上的时间来进行最后的校对终于可以在12日拿出一个可以发布的版本。
9天时间1317个 Star310个 Fork超过30人参与翻译和校对工作项目最高排名GitHub总榜第4。
9天时间1317个 Star310个 Fork超过30人参与翻译和校对工作项目最高排名 GitHub 总榜第4。
设想过很多遍校对完成时的场景,仰天大笑还是泪流满面?真正到了这一刻才发现,疲倦已经不允许我有任何情绪。
说实话,刚开始发起项目的时候完全没想到会发展成今天这样,我一度计划自己一个人翻译完整本书。万万没想到,会有这么多的人愿意加入并贡献出自己的力量。
coverxit发给我最后一份文档的时候说我要去背单词了我问他周末要考六级他说是的。
coverxit 发给我最后一份文档的时候说,我要去背单词了,我问他,周末要考六级?他说是的。
pp-prog告诉我这几天太累了校对到一半睡着了醒来又继续做。2点17分发给我校对完成的文档。
pp-prog 告诉我这几天太累了校对到一半睡着了醒来又继续做。2点17分发给我校对完成的文档。
lifedim说他平时12点就会睡1点47分发给我校对后的文档。
lifedim 说他平时12点就会睡1点47分发给我校对后的文档。
团队里每个人都有自己的事情上班、上学、创业但是我们只用了9天就完成整本书的翻译。我不知道大家付出了多少牺牲了多少但是我知道他们的付出必将被这些文字记录下来即使再过10年20年依然熠熠生辉永不被人遗忘。