Merge remote-tracking branch 'numbbbbb/gh-pages' into gh-pages

This commit is contained in:
Realank
2016-09-13 14:31:51 +08:00
37 changed files with 3065 additions and 3105 deletions

2
CNAME
View File

@ -1 +1 @@
www.swiftguide.cn gg.swiftguide.cn

View File

@ -1,7 +1,7 @@
《The Swift Programming Language》in Chinese 《The Swift Programming Language》in Chinese
============================================= =============================================
中文版Apple官方Swift教程《The Swift Programming Language》 中文版 Apple 官方 Swift 教程《The Swift Programming Language》
[英文原版](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/index.html#//apple_ref/doc/uid/TP40014097-CH3-ID0) [英文原版](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/index.html#//apple_ref/doc/uid/TP40014097-CH3-ID0)
@ -9,17 +9,17 @@
# 在线阅读 # 在线阅读
使用Gitbook制作可以直接[在线阅读](http://swiftguide.cn/)。 使用 Gitbook 制作,可以直接[在线阅读](http://swiftguide.cn/)。
# 当前阶段 # 当前阶段
已经更新到Swift 2.1 已经更新到 Swift 2.2
# 2.1译者记录 # 2.1 & 2.2 译者记录
详见各章节开头位置。 详见各章节开头位置。
# 2.0译者记录 # 2.0 译者记录
- About Swift [xtymichael](https://github.com/xtymichael) - About Swift [xtymichael](https://github.com/xtymichael)
- A Swift Tour[xtymichael](https://github.com/xtymichael) - A Swift Tour[xtymichael](https://github.com/xtymichael)
@ -59,7 +59,7 @@
- Generic Parameters and Arguments[wardenNScaiyi](https://github.com/wardenNScaiyi) - Generic Parameters and Arguments[wardenNScaiyi](https://github.com/wardenNScaiyi)
- Summary of the Grammar[miaosiqi](https://github.com/miaosiqi) - Summary of the Grammar[miaosiqi](https://github.com/miaosiqi)
# 1.0译者记录 # 1.0 译者记录
* 欢迎使用 Swift * 欢迎使用 Swift
* 关于 Swift ([numbbbbb]) * 关于 Swift ([numbbbbb])

View File

@ -1,4 +1,4 @@
> 已同步更新到 Swift 2.1 > 已同步更新到 Swift 2.2
# 2.0 新的开始 # 2.0 新的开始
@ -73,4 +73,4 @@ Swift 2.0 参与者名单(按照章节顺序):
- [miaosiqi](https://github.com/miaosiqi) - [miaosiqi](https://github.com/miaosiqi)
- [Realank](https://github.com/Realank) - [Realank](https://github.com/Realank)
最后,感谢[极客学院](http://wiki.jikexueyuan.com)提供的wiki系统在国内访问起来速度很快优化后的样式看起来也更舒服。 最后,感谢[极客学院](http://wiki.jikexueyuan.com)提供的wiki系统在国内访问起来速度很快优化后的样式看起来也更舒服。

View File

@ -9,6 +9,9 @@
> 2.0 > 2.0
> 翻译+校对:[xtymichael](https://github.com/xtymichael) > 翻译+校对:[xtymichael](https://github.com/xtymichael)
> 2.2
> 翻译:[175](https://github.com/Brian175)2016-04-09 校对:[SketchK](https://github.com/SketchK)2016-05-11
本页内容包括: 本页内容包括:
- [简单值Simple Values](#simple_values) - [简单值Simple Values](#simple_values)
@ -17,6 +20,7 @@
- [对象和类Objects and Classes](#objects_and_classes) - [对象和类Objects and Classes](#objects_and_classes)
- [枚举和结构体Enumerations and Structures](#enumerations_and_structures) - [枚举和结构体Enumerations and Structures](#enumerations_and_structures)
- [协议和扩展Protocols and Extensions](#protocols_and_extensions) - [协议和扩展Protocols and Extensions](#protocols_and_extensions)
- [错误处理Error Handling](#error_handling)
- [泛型Generics](#generics) - [泛型Generics](#generics)
通常来说编程语言教程中的第一个程序应该在屏幕上打印“Hello, world”。在 Swift 中,可以用一行代码实现: 通常来说编程语言教程中的第一个程序应该在屏幕上打印“Hello, world”。在 Swift 中,可以用一行代码实现:
@ -30,8 +34,7 @@ print("Hello, world!")
这个教程会通过一系列编程例子来让你对 Swift 有初步了解,如果你有什么不理解的地方也不用担心——任何本章介绍的内容都会在后面的章节中详细讲解。 这个教程会通过一系列编程例子来让你对 Swift 有初步了解,如果你有什么不理解的地方也不用担心——任何本章介绍的内容都会在后面的章节中详细讲解。
> 注意: > 注意:
> 为了获得最好的体验,在 Xcode 当中使用代码预览功能。代码预览功能可以让你编辑代码并实时看到运行结果。 > 在 Mac 上,下载 Playground 并双击文件在 Xcode 里打开:[https://developer.apple.com/go/?id=swift-tour](https://developer.apple.com/go/?id=swift-tour)
> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.playground.zip">下载Playground</a>
<a name="simple_values"></a> <a name="simple_values"></a>
## 简单值 ## 简单值
@ -194,7 +197,7 @@ print(largest)
``` ```
> 练习: > 练习:
> 添加另一个变量来记录现在和之前最大数字的类型 > 添加另一个变量来记录最大数字的种类(kind),同时仍然记录这个最大数字的值
使用`while`来重复运行一段代码直到不满足条件。循环条件也可以在结尾,保证能至少循环一次。 使用`while`来重复运行一段代码直到不满足条件。循环条件也可以在结尾,保证能至少循环一次。
@ -212,20 +215,14 @@ repeat {
print(m) print(m)
``` ```
你可以在循环中使用`..<`来表示范围,也可以使用传统的写法,两者是等价的: 你可以在循环中使用`..<`来表示范围
```swift ```swift
var firstForLoop = 0 var total = 0
for i in 0..<4 { for i in 0..<4 {
firstForLoop += i total += i
} }
print(firstForLoop) print(total)
var secondForLoop = 0
for var i = 0; i < 4; ++i {
secondForLoop += i
}
print(secondForLoop)
``` ```
使用`..<`创建的范围不包含上界,如果想包含的话需要使用`...` 使用`..<`创建的范围不包含上界,如果想包含的话需要使用`...`
@ -540,7 +537,7 @@ let aceRawValue = ace.rawValue
> 练习: > 练习:
> 写一个函数,通过比较它们的原始值来比较两个`Rank`值。 > 写一个函数,通过比较它们的原始值来比较两个`Rank`值。
在上面的例子中,枚举原始值的类型是`Int`,所以你只需要设置第一个原始值。剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。使用`rawValue`属性来访问一个枚举成员的原始值。 默认情况下Swift 按照从 0 开始每次加 1 的方式为原始值进行赋值,不过你可以通过显式赋值进行改变。在上面的例子中,`Ace`被显式赋值为 1并且剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。使用`rawValue`属性来访问一个枚举成员的原始值。
使用`init?(rawValue:)`初始化构造器在原始值和枚举值之间进行转换。 使用`init?(rawValue:)`初始化构造器在原始值和枚举值之间进行转换。
@ -550,7 +547,7 @@ if let convertedRank = Rank(rawValue: 3) {
} }
``` ```
枚举的成员值是实际值,并不是原始值的另一种表达方法。实际上,以防原始值没有意义,你不需要设置 枚举的成员值是实际值,并不是原始值的另一种表达方法。实际上,如果没有比较有意义的原始值,你不需要提供原始值
```swift ```swift
enum Suit { enum Suit {
@ -601,24 +598,24 @@ let threeOfSpadesDescription = threeOfSpades.simpleDescription()
```swift ```swift
enum ServerResponse { enum ServerResponse {
case Result(String, String) case Result(String, String)
case Error(String) case Failure(String)
} }
let success = ServerResponse.Result("6:00 am", "8:09 pm") let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.") let failure = ServerResponse.Failure("Out of cheese.")
switch success { switch success {
case let .Result(sunrise, sunset): case let .Result(sunrise, sunset):
let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)." let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error): case let .Failure(message):
let serverResponse = "Failure... \(error)" print("Failure... \(message)")
} }
``` ```
> 练习: > 练习:
> 给`ServerResponse`和`switch`添加第三种情况。 > 给`ServerResponse`和`switch`添加第三种情况。
注意如何从`ServerResponse`中提取日升和日落时间并用得到的值用来和`switch`的情况作比较 注意日升和日落时间是如何从`ServerResponse`中提取到并与`switch``case`相匹配的
<a name="protocols_and_extensions"></a> <a name="protocols_and_extensions"></a>
## 协议和扩展 ## 协议和扩展
@ -689,6 +686,88 @@ print(protocolValue.simpleDescription)
即使`protocolValue`变量运行时的类型是`simpleClass`,编译器会把它的类型当做`ExampleProtocol`。这表示你不能调用类在它实现的协议之外实现的方法或者属性。 即使`protocolValue`变量运行时的类型是`simpleClass`,编译器会把它的类型当做`ExampleProtocol`。这表示你不能调用类在它实现的协议之外实现的方法或者属性。
<a name="error_handling"></a>
## 错误处理
使用采用`ErrorType`协议的类型来表示错误。
```swift
enum PrinterError: ErrorType {
case OutOfPaper
case NoToner
case OnFire
}
```
使用`throw`来抛出一个错误并使用`throws`来表示一个可以抛出错误的函数。如果在函数中抛出一个错误,这个函数会立刻返回并且调用该函数的代码会进行错误处理。
```swift
func sendToPrinter(printerName: String) throws -> String {
if printerName == "Never Has Toner" {
throw PrinterError.NoToner
}
return "Job sent"
}
```
有多种方式可以用来进行错误处理。一种方式是使用`do-catch`。在`do`代码块中,使用`try`来标记可以抛出错误的代码。在`catch`代码块中,除非你另外命名,否则错误会自动命名为`error`
```swift
do {
let printerResponse = try sendToPrinter("Bi Sheng")
print(printerResponse)
} catch {
print(error)
}
```
> 练习:
> 将 printer name 改为`"Never Has Toner"`使`sendToPrinter(_:)`函数抛出错误。
可以使用多个`catch`块来处理特定的错误。参照 switch 中的`case`风格来写`catch`
```swift
do {
let printerResponse = try sendToPrinter("Gutenberg")
print(printerResponse)
} catch PrinterError.OnFire {
print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
print("Printer error: \(printerError).")
} catch {
print(error)
}
```
> 练习:
> 在`do`代码块中添加抛出错误的代码。你需要抛出哪种错误来使第一个`catch`块进行接收?怎么使第二个和第三个`catch`进行接收呢?
另一种处理错误的方式使用`try?`将结果转换为可选的。如果函数抛出错误,该错误会被抛弃并且结果为`nil`。否则的话,结果会是一个包含函数返回值的可选值。
```swift
let printerSuccess = try? sendToPrinter("Mergenthaler")
let printerFailure = try? sendToPrinter("Never Has Toner")
```
使用`defer`代码块来表示在函数返回前,函数中最后执行的代码。无论函数是否会抛出错误,这段代码都将执行。使用`defer`,可以把函数调用之初就要执行的代码和函数调用结束时的扫尾代码写在一起,虽然这两者的执行时机截然不同。
```swift
var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]
func fridgeContains(itemName: String) -> Bool {
fridgeIsOpen = true
defer {
fridgeIsOpen = false
}
let result = fridgeContent.contains(itemName)
return result
}
fridgeContains("banana")
print(fridgeIsOpen)
```
<a name="generics"></a> <a name="generics"></a>
## 泛型 ## 泛型

View File

@ -1,37 +1,44 @@
# Swift 版本历史记录 # Swift 文档修订历史
--- ---
> 1.0
> 翻译:[成都老码团队翻译组-Arya](http://weibo.com/littlekok/) > 翻译:[成都老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 校对:[成都老码团队翻译组-Oberyn](http://weibo.com/u/5241713117) > 校对:[成都老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)
[changkun](http://changkun.us/about/)
>
> 1.1
> 翻译:[成都老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 校对:[成都老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)
[changkun](http://changkun.us/about/)
>
> 1.2
> 翻译:[成都老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 校对:[成都老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)
[changkun](http://changkun.us/about/)
>
> 2.0
> 翻译+校对:[changkun](http://changkun.us/about/)
>
> 2.1
> 翻译+校对:[changkun](http://changkun.us/about/)
>
> 2.2
> 翻译+校对:[changkun](http://changkun.us/about/)
本页面根据 [Document Revision History](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/RevisionHistory.html) 进行适配更新。
本页内容包括: 本页内容包括:
- [XCode6.4 Beta Swift语法文档更新](#xcode6_4_Beta) - [Swift 2.2 更新](#swift_2_2)
- [XCode6.3正式版 Swift语法文档更新](#xcode6_3) - [Swift 2.1 更新](#swift_2_1)
- [XCode6.2正式版 Swift语法文档更新](#xcode6_2) - [Swift 2.0 更新](#swift_2_0)
- [XCode6.2 Beta3 Swift语法文档更新](#xcode6_2_Beta3) - [Swift 1.2 更新](#swift_1_2)
- [XCode6.2 Beta2 Swift语法文档更新](#xcode6_2_Beta2) - [Swift 1.1 更新](#swift_1_1)
- [XCode6.2 Beta1 Swift语法文档更新](#xcode6_2_Beta1) - [Swift 1.0 更新](#swift_1_0)
- [XCode6.1.1正式版 Swift语法文档更新](#xcode6_1_1)
- [XCode6.1 Swift语法文档更新](#xcode6_1)
- [XCode6.1 Beta2 Swift语法文档更新](#xcode6_1_Beta2)
- [XCode6.1 Beta1 Swift语法文档更新](#xcode6_1_Beta1)
- [XCode6 Beta7 Swift语法文档更新](#xcode6_beta7)
- [XCode6 Beta6 Swift语法文档更新](#xcode6_beta6)
- [XCode6 Beta5 Swift语法文档更新](#xcode6_beta5)
- [XCode6 Beta4 Swift语法文档更新](#xcode6_beta4)
- [XCode6 Beta3 Swift语法文档更新](#xcode6_beta3)
- [XCode6 Beta2 Swift语法文档更新](#xcode6_beta2)
- [XCode6 Beta1 Swift语法文档更新](#xcode6_beta1)
- XCode6下载: [老码云盘下载](http://pan.baidu.com/disk/home#from=share_pan_logo&path=%252F%25E8%2580%2581%25E7%25A0%2581%25E4%25BA%2591%25E7%259B%2598-XCode6%252FXCode6-Beta5)
以下部分是针对XCode6每一次Beta版本直至正式版发布Swift语法部分的更新归类 <a name="swift_2_2"></a>
### Swift 2.2 更新
<a name="xcode6_4_Beta"></a>
### XCode6.4 Beta中Swift语法更新
***注意苹果在这个版本发布后没有及时的更新Swift Programming Language文档,以下是[老码团队](http://weibo.com/u/5241713117)通过XCode6.4 Beta Release Note总结的更改说明***
<table class="graybox" border="0" cellspacing="0" cellpadding="5"> <table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead> <thead>
@ -42,10 +49,55 @@
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td scope="row">2015-04-13</td> <td scope="row">2016-03-21</td>
<td><ul class="list-bullet"> <td><ul class="list-bullet">
<li> <li>
XCode6.4包含了对于构建和调试基于iOS8.4 App的支持 更新至 Swift 2.2。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/doc/uid/TP40014097-CH33-ID539">编译配置语句</a>一节中关于如何根据 Swift 版本进行条件编译。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/doc/uid/TP40014097-CH32-ID400">显示成员表达式</a>一节中关于如何区分只有参数名不同的方法和构造器的信息。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/doc/uid/TP40014097-CH32-ID547">选择器表达式</a>一节中针对 Objective-C 选择器的 <code>#selector</code> 语法。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Generics.html#//apple_ref/doc/uid/TP40014097-CH26-ID189">关联类型</a><a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID374">协议关联类型</a>声明,使用 <code>associatedtype</code> 关键词修改了对于关联类型的讨论。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-ID224">可失败构造器</a>一节中关于当构造器在实例完全初始化之前返回 <code>nil</code>的相关信息。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html#//apple_ref/doc/uid/TP40014097-CH6-ID70">比较运算符</a>一节中关于比较元组的信息。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-ID413">关键字和标点符号</a>一节中关于使用关键字作为外部参数名的信息。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-ID413">声明特性</a>一节中关于<code>@objc</code>特性的讨论,并指出枚举(Enumeration)和枚举用例(Enumaration Case)。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-ID418">操作符</a>一节中对于自定义运算符的讨论包含了<code>.</code>
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID531">重新抛出错误的函数和方法</a>一节中关于重新抛出错误函数不能直接抛出错误的笔记。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html#//apple_ref/doc/uid/TP40014097-CH14-ID262">属性观察器</a>一节中关于当作为 in-out 参数传递属性时,属性观察器的调用行为。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.html#//apple_ref/doc/uid/TP40014097-CH2-ID1">Swift 初见</a>一节中关于错误处理的内容。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-ID53">弱引用</a>一节中的图片用以更清楚的展示重新分配过程。
</li>
<li>
删除了 C 语言风格的 <code>for</code> 循环,<code>++</code> 前缀和后缀运算符,以及<code>--</code> 前缀和后缀运算符。
</li>
<li>
删除了对变量函数参数和柯里化函数的特殊语法的讨论。
</li> </li>
</ul> </ul>
</td> </td>
@ -53,9 +105,195 @@
</tbody> </tbody>
</table> </table>
<a name="swift_2_1"></a>
### Swift 2.1 更新
<table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead>
<tr>
<th scope="col" width="100">发布日期</th>
<th scope="col">语法变更记录</th>
</tr>
</thead>
<tbody>
<tr>
<td scope="row">2015-10-20</td>
<td><ul class="list-bullet">
<li>
更新至 Swift 2.1。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-ID292">字符串插值(String Interprolation)</a><a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html#//apple_ref/doc/uid/TP40014097-CH30-ID417">字符串字面量(String Literals)</a>小节,现在字符串插值可包含字符串字面量。
</li>
<li>
增加了在<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html#//apple_ref/doc/uid/TP40014097-CH11-ID546">非逃逸闭包(Nonescaping Closures)</a>一节中关于 <code>@noescape</code> 属性的相关内容。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Attributes.html#//apple_ref/doc/uid/TP40014097-CH35-ID348">声明特性(Declaration Attributes)</a><a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/doc/uid/TP40014097-CH33-ID539">编译配置语句(Build Configuration Statement)</a>小节中与 tvOS 相关的信息。
</li>
<li>
增加了 <a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID545">In-Out 参数(In-Out Parameters)</a>小节中与 in-out 参数行为相关的信息。
</li>
<li>
增加了在<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/doc/uid/TP40014097-CH32-ID544">捕获列表(Capture Lists)</a>一节中关于指定闭包捕获列表被捕获时捕获值的相关内容。
</li>
<li>
更新了通过<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html#//apple_ref/doc/uid/TP40014097-CH21-ID248">可选链式调用访问属性(Accessing Properties Through Optional Chaining)</a>一节,阐明了如何通过可选链式调用进行赋值。
</li>
<li>
改进了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html#//apple_ref/doc/uid/TP40014097-CH11-ID543">自闭包(Autoclosure)</a>一节中对自闭包的讨论。
</li>
<li>
<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.html#//apple_ref/doc/uid/TP40014097-CH2-ID1">Swift 初见(A Swift Tour)</a>一节中更新了一个使用<code>??</code>操作符的例子。
</li>
</ul>
</td>
</tr>
</tbody>
</table>
<a name="swift_2_0"></a>
### Swift 2.0 更新
<table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead>
<tr>
<th scope="col" width="100">发布日期</th>
<th scope="col">语法变更记录</th>
</tr>
</thead>
<tbody>
<tr>
<td scope="row">2015-09-16</td>
<td><ul class="list-bullet">
<li>
更新至 Swift 2.0。
</li>
<li>
增加了对于错误处理相关内容,包括 <a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html#//apple_ref/doc/uid/TP40014097-CH42-ID508">错误处理</a>一章、<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/doc/uid/TP40014097-CH33-ID533">Do 语句</a><a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/doc/uid/TP40014097-CH33-ID518">Throw 语句</a><a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/doc/uid/TP40014097-CH33-ID532">Defer 语句</a>以及<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html#//apple_ref/doc/uid/TP40014097-CH32-ID516">try 运算符</a> 的多个小节。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html#//apple_ref/doc/uid/TP40014097-CH42-ID509">表示并抛出错误</a>一节,现在所有类型均可遵循 <code>ErrorType</code> 协议。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html#//apple_ref/doc/uid/TP40014097-CH42-ID542">将错误转换成可选值</a>一节 <code>try?</code> 关键字的相关信息。
</li>
<li>
增加了<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 可用性检查的内容。
</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> 语句的内容。
</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>一节中关于协议扩展的内容。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AccessControl.html#//apple_ref/doc/uid/TP40014097-CH41-ID3">访问控制</a>一章中<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AccessControl.html#//apple_ref/doc/uid/TP40014097-CH41-ID519">单元测试 target 的访问级别</a>一节中关于单元测试的访问控制相关的内容。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Patterns.html#//apple_ref/doc/uid/TP40014097-CH36-ID419">模式</a>一章中<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Patterns.html#//apple_ref/doc/uid/TP40014097-CH36-ID520">可选模式</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-ID126">Repeat-While</a> 一节中关于<code>repeat-while</code>循环的信息。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-ID285">字符串和字符</a>一章,现在<code>String</code>在 Swift 标准库中不再遵循<code>CollectionType</code>协议。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-ID314">打印常量和变量</a>一节中关于新 Swift 标准库中关于 <code>print(_:separator:terminator)</code> 的信息。
</li>
<li>
增加了<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-ID535">原始值的隐式赋值</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-ID366">包含原始值类型的枚举</a>一节中关于包含<code>String</code>原始值的枚举用例的行为。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html#//apple_ref/doc/uid/TP40014097-CH11-ID543">自闭包</a>一节中关于<code>@autoclosure</code>特性的相关信息,包括它的<code>@autoclosure(escaping)</code>形式。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Attributes.html#//apple_ref/doc/uid/TP40014097-CH35-ID348">声明特性</a>一节中关于<code>@avaliable</code><code>warn_unused_result</code>特性的相关内容。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Attributes.html#//apple_ref/doc/uid/TP40014097-CH35-ID350">类型特性</a>一节中关于<code>@convention</code>特性的相关信息。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-ID333">可选绑定</a>一节中关于使用<code>where</code>子句进行多可选绑定的内容。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-ID292">字符串字面量</a>一节中关于在编译时使用 <code>+</code> 运算符凭借字符串字面量的相关信息。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Types.html#//apple_ref/doc/uid/TP40014097-CH31-ID455">元类型</a>一节中关于元类型值的比较和使用它们通过构造器表达式构造实例。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-ID336">断言调试</a>一节中关于用户定义断言是被警用的相关内容。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Attributes.html#//apple_ref/doc/uid/TP40014097-CH35-ID348">声明特性</a>一节中,对<code>@NSManaged</code>特性的讨论,现在这个特性可以被应用到一个确定实例方法。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html#//apple_ref/doc/uid/TP40014097-CH10-ID171">可变参数</a>一节,现在可变参数可以声明在函数参数列表的任意位置中。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-ID229">重写可失败构造器</a>一节中,关于非可失败构造器相当于一个可失败构造器通过父类构造器的结果进行强制拆包的相关内容。
</li>
<li>
增加了<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/Expressions.html#//apple_ref/doc/uid/TP40014097-CH32-ID399">构造器表达式</a>一节中关于显式引用一个构造器的内容。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/doc/uid/TP40014097-CH33-ID538">编译控制语句</a>一节中关于编译信息以及行控制语句的相关信息。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Types.html#//apple_ref/doc/uid/TP40014097-CH31-ID455">元类型</a>一节中关于如何从元类型值中构造类实例。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-ID53">弱引用</a>一节中关于弱引用作为缓存的显存的不足。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-ID292">类型特性</a>一节,提到了存储型特性其实是懒加载。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html#//apple_ref/doc/uid/TP40014097-CH14-ID264">捕获类型</a>一节,阐明了变量和常量在闭包中如何被捕获。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Attributes.html#//apple_ref/doc/uid/TP40014097-CH35-ID348">声明特性</a>一节用以描述如何在类中使用<code>@objc</code>关键字。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html#//apple_ref/doc/uid/TP40014097-CH42-ID512">错误处理</a>一节中关于执行<code>throw</code>语句的性能的讨论。增加了 Do 语句一节中相似的信息。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/doc/uid/TP40014097-CH33-ID533">类型特性</a>一节中关于类、结构体和枚举的存储型和计算型特性的信息。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Statements.html#//apple_ref/doc/uid/TP40014097-CH33-ID441">Break 语句</a>一节中关于带标签的 break 语句。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html#//apple_ref/doc/uid/TP40014097-CH14-ID262">属性观察器</a>一节,阐明了<code>willSet</code><code>didSet</code>观察器的行为。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AccessControl.html#//apple_ref/doc/uid/TP40014097-CH41-ID5">访问级</a>一节中关于<code>private</code>作用域访问的相关信息。
</li>
<li>
增加了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-ID53">弱引用</a>一节中关于若应用在垃圾回收系统和 ARC 之间的区别。
</li>
<li>
更新了<a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-ID295">字符串字面量中特殊字符</a>一节中对 Unicode 标量更精确的定义。
</li>
</ul>
</td>
</tr>
</tbody>
</table>
<a name="swift_1_2"></a>
### Swift 1.2 更新
<a name="xcode6_3"></a> <a name="xcode6_3"></a>
### XCode6.3中Swift语法更新
***注意苹果此时发布了统一的版本XCode6.3其中将以前的XCode6.3 Beta系列版本合并, 而XCode6.3共计发布了4次Beta版本[老码团队](http://weibo.com/u/5241713117)通过Release Note总结的详细更改说明请参看:[Swift语法更新记录表格](https://docs.baihui.com/sheet/published.do?rid=mxpis6d36a8b7bc254c36ae2a808c64c2361e)***
<table class="graybox" border="0" cellspacing="0" cellpadding="5"> <table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead> <thead>
@ -68,6 +306,9 @@
<tr> <tr>
<td scope="row">2015-4-8</td> <td scope="row">2015-4-8</td>
<td><ul class="list-bullet"> <td><ul class="list-bullet">
<li>
更新至 Swift 1.2。
</li>
<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>
@ -85,7 +326,7 @@
增加了一个新的指导章节,它是关于<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> 增加了一个新的指导章节,它是关于<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>
</li> </li>
<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-ID37">溢出运算符</a>中移除了溢出除运算符和求余溢出运算符 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-ID37">溢出运算符</a>中移除了溢出除运算符(&/)和求余溢出运算符(&%)。
</li> </li>
<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> 更新了常量和常量属性在声明和构造时的规则,更多信息,请看<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>
@ -111,29 +352,7 @@
<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>
</ul> <li>
</td>
</tr>
</tbody>
</table>
<a name="xcode6_2"></a>
### XCode6.2正式版中Swift语法更新
***注意苹果此时发布了统一的版本XCode6.2其中将以前的XCode6.2 Beta系列版本合并***
<table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead>
<tr>
<th scope="col" width="100">发布日期</th>
<th scope="col">语法变更记录</th>
</tr>
</thead>
<tbody>
<tr>
<td scope="row">2015-02-09</td>
<td><ul class="list-bullet">
<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> 在函数作用域中的常量声明时可以不被初始化,它必须在第一次使用前被赋值。更多的信息,请看<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>
<li> <li>
@ -157,139 +376,9 @@
</tbody> </tbody>
</table> </table>
<a name="xcode6_2_Beta3"></a>
### XCode6.2 Beta3中Swift语法更新
***注意苹果在这个版本发布后没有及时的更新Swift Programming Language文档,以下是[老码团队](http://weibo.com/u/5241713117)通过XCode6.2 Beta3 Release Note总结的更改说明*** <a name="swift_1_1"></a>
### Swift 1.1 更新
<table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead>
<tr>
<th scope="col" width="100">发布日期</th>
<th scope="col">语法变更记录</th>
</tr>
</thead>
<tbody>
<tr>
<td scope="row">2014-12-19</td>
<td>
<ul class="list-bullet">
<li>
在对Watch App做消息通知模拟调试时第一个payload.apns文件将会被默认选择
</li>
<li>
在为Watch App使用asset catalog时38mm和42mm尺寸的图片就会被使用
</li>
<li>
在做Watch App开发时,<code>@IBAction</code>属性支持<code>WKInterfaceSwitch</code><code>WKInterfaceSlider</code> Swift类型了
</li>
<li>
现在可以通过Device窗口安装删除和访问App容器中的数据了。
</li>
</ul>
</td>
</tr>
</tbody>
</table>
<a name="xcode6_2_Beta2"></a>
### XCode6.2 Beta2中Swift语法更新
***注意苹果在这个版本发布后没有及时的更新Swift Programming Language文档,以下是[老码团队](http://weibo.com/u/5241713117)通过XCode6.2 Beta2 Release Note总结的更改说明***
<table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead>
<tr>
<th scope="col" width="100">发布日期</th>
<th scope="col">语法变更记录</th>
</tr>
</thead>
<tbody>
<tr>
<td scope="row">2014-12-10</td>
<td><ul class="list-bullet">
<li>
现在在Interface Builder中可以针对特定的Device设备自定义Watch应用的Layout布局了
</li>
</ul>
</td>
</tr>
</tbody>
</table>
<a name="xcode6_2_Beta1"></a>
### XCode6.2 Beta1中Swift语法更新
***注意苹果在这个版本发布后没有及时的更新Swift Programming Language文档,以下是[老码团队](http://weibo.com/u/5241713117)通过XCode6.2 Beta1 Release Note总结的更改说明***
<table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead>
<tr>
<th scope="col" width="100">发布日期</th>
<th scope="col">语法变更记录</th>
</tr>
</thead>
<tbody>
<tr>
<td scope="row">2014-11-28</td>
<td><ul class="list-bullet">
<li>
XCode6.2包含了iOS8.2 SDK该SDK中包含WatchKit用来开发Apple Watch应用。
</li>
<li>
在工具集中增加了对WatchKit的支持
1UI设计工具增加了Apple Watch应用的界面组件通知和小部件。
2增加了调试和性能统计功能
3增加Apple Watch应用的模拟器帮助调试应用功能
</li>
<li>
为了使Apple Watch应用能够正常工作一些具体的参数必须设置
1WatchKit中扩展配置文件Info.plist中的<code>NSExtensionAttributes</code>配置项WKAppBundleIdentifier必须和WatchKit App中的通用配置文件中的属性<code>CFBundleIdentifier</code>项目保持一致。2WatchKit中的<code>CFBundleIdentifier</code>配置项必须和<code>WKCompanionAppBundleIdentifier</code>中的配置项保持一致
</li>
</ul>
</td>
</tr>
</tbody>
</table>
<a name="xcode6_1_1"></a>
### XCode6.1.1中Swift语法更新
***注意苹果在这个版本发布后没有及时的更新Swift Programming Language文档,以下是[老码团队](http://weibo.com/u/5241713117)通过XCode6.1.1 Release Note总结的更改说明***
<table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead>
<tr>
<th scope="col" width="100">发布日期</th>
<th scope="col">语法变更记录</th>
</tr>
</thead>
<tbody>
<tr>
<td scope="row">2014-12-2</td>
<td><ul class="list-bullet">
<li>
在SourceKit中一些导致Crash的常见问题被修复比如名字冲突和遗留废弃数据的问题等。
</li>
<li>
把纯正的Swift类对象实例赋值给AnyObject量不会再Crash了。
</li>
<li>
在泛型使用场景下,遵循了协议类要求的构造器方法或者类型方法可以直接调用继承类中的方法了。
</li>
<li>
修正了InterfaceBuild中如果图片名字含有“/”时会在OSX10.10上Crash或者无法打开的问题
</li>
</ul>
</td>
</tr>
</tbody>
</table>
<a name="xcode6_1"></a>
### XCode6.1中Swift语法更新
***注意苹果此时发布了统一的版本XCode6.1其中将以前的XCode6.0.1和XCode6.1 Beta系列版本合并***
<table class="graybox" border="0" cellspacing="0" cellpadding="5"> <table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead> <thead>
@ -303,112 +392,32 @@
<td scope="row">2014-10-16</td> <td scope="row">2014-10-16</td>
<td><ul class="list-bullet"> <td><ul class="list-bullet">
<li> <li>
增加了一个完整的关于<a href="http://developer.apple.com/library/etc/redirect/xcode/devtools/419f35/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html">失败构造器(Failable Initializers)</a>的指南文档 更新至 Swift 1.1。
</li> </li>
<li> <li>
增加了一个关于协议的<a href="http://developer.apple.com/library/etc/redirect/xcode/devtools/419f35/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html">失败构造器需求(Failable Initializer Requirements)</a>描述 增加了关于<a href="http://developer.apple.com/library/etc/redirect/xcode/devtools/419f35/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html">失败构造器(Failable Initializers)</a>完整章节。
</li> </li>
<li> <li>
`Any`类型的常量或变量现在可以包含一个函数实例了。同时更新了<a href="http://developer.apple.com/library/etc/redirect/xcode/devtools/419f35/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html">`Any`</a>章节的案例用来演示如何在swith语句中检查和转换一个函数类型 增加了协议中关于失败构造器要求的描述
</li> </li>
</ul>
</td>
</tr>
</tbody>
</table>
<a name="xcode6_1_Beta2"></a>
### XCode6.1 Beta2中Swift语法更新
***注意苹果此时发布了XCode6.0.1版本(也称为XCode6正式版)此版本用于iOS的开发同时也发布子版本XCode6.1 Beta2此版本为OSX开发做准备以下所述的更改仅对XCode6.1 Beta2有效***
<table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead>
<tr>
<th scope="col" width="100">发布日期</th>
<th scope="col">语法变更记录</th>
</tr>
</thead>
<tbody>
<tr>
<td scope="row">2014-09-15</td>
<td><ul class="list-bullet">
<li> <li>
带有原始值的枚举类型增加了一个<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>部分 常量和变量的 <code>Any</code> 类型现可以包含函数实例。更新了关于 <code>Any</code> 相关的示例来展示如何在 <code>switch</code> 语句中如何检查并转换到一个函数类型。
</li> </li>
</ul>
</td>
</tr>
</tbody>
</table>
<a name="xcode6_1_Beta1"></a>
### XCode6.1 Beta1中Swift语法更新
***注意苹果此时发布了XCode6 GM版本此版本用于iOS的开发同时也发布子版本XCode6.1 Beta1此版本为OSX开发做准备以下所述的更改仅对XCode6.1 Beta1有效***
<table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead>
<tr>
<th scope="col" width="100">发布日期</th>
<th scope="col">语法变更记录</th>
</tr>
</thead>
<tbody>
<tr>
<td scope="row">2014-09-09</td>
<td><ul class="list-bullet">
<li> <li>
增加了一个新的关于<a href="http://developer.apple.com/library/etc/redirect/xcode/devtools/419f35/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html">失败构造器(Failable Initializers)</a>的参考章节,失败构造器可以触发失败的构造过程 带有原始值的枚举类型增加了一个<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>
<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> </li>
</ul> </ul>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<a name="xcode6_beta7"></a> <a name="swift_1_0"></a>
### XCode6 Beta7中Swift语法更新 ### Swift 1.0 更新
***注意苹果在这个版本发布后没有及时的更新Swift Programming Language文档,以下是[老码团队](http://weibo.com/u/5241713117)通过XCode Beta7 Release Note总结的更改说明***
<table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead>
<tr>
<th scope="col" width="100">发布日期</th>
<th scope="col">语法变更记录</th>
</tr>
</thead>
<tbody>
<tr>
<td scope="row">2014-09-03</td>
<td><ul class="list-bullet">
<li>
实现了内部库的修改和适配,主要包括如下:
1大量内部类或者函数遵循Optional类型和协议
2移除大部分函数返回类型隐式解封可选类型的使用
</li>
<li>
对于泛型的类库函数或接口统一从<code>T!</code>更换为<code>T</code><code>T</code>,这样使得语法更加严谨,明确了可能返回为空和不为空的情况
</li>
<li>
字符类型不能使用+运算法链接,可以以<code>String(C1)+String(2)</code> 的方式实现字符间链接
</li>
<li>
重写了<code>Sort</code>函数,解决了栈溢出的问题
</li>
</ul>
</td>
</tr>
</tbody>
</table>
<a name="xcode6_beta6"></a>
### XCode6 Beta6中Swift语法更新
<table class="graybox" border="0" cellspacing="0" cellpadding="5"> <table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead> <thead>
@ -421,6 +430,9 @@
<tr> <tr>
<td scope="row">2014-08-18</td> <td scope="row">2014-08-18</td>
<td><ul class="list-bullet"> <td><ul class="list-bullet">
<li>
发布新的文档用以详述 Swift 1.0苹果公司针对iOS和OS X应用的全新开发语言。
</li>
<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> 在章节协议中,增加新的小节:<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>
</li> </li>
@ -436,26 +448,6 @@
<li> <li>
<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>特性的一些信息 <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>
</ul>
</td>
</tr>
</tbody>
</table>
<a name="xcode6_beta5"></a>
### XCode6 Beta5中Swift语法更新
<table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead>
<tr>
<th scope="col" width="100">发布日期</th>
<th scope="col">语法变更记录</th>
</tr>
</thead>
<tbody>
<tr>
<td scope="row">2014-08-04</td>
<td><ul class="list-bullet">
<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>
@ -510,26 +502,6 @@
<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>添加了更多的信息. 为章节<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>添加了更多的信息.
</li> </li>
</ul>
</td>
</tr>
</tbody>
</table>
<a name="xcode6_beta4"></a>
#### XCode6 Beta4中Swift语法更新
<table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead>
<tr>
<th scope="col" width="100">发布日期</th>
<th scope="col">语法变更记录</th>
</tr>
</thead>
<tbody>
<tr>
<td scope="row">2014-07-21</td>
<td><ul class="list-bullet">
<li> <li>
加入新的章节 <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>. 加入新的章节 <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>
@ -572,26 +544,6 @@
<li> <li>
<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>. <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>
</ul>
</td>
</tr>
</tbody>
</table>
<a name="xcode6_beta3"></a>
#### XCode6 Beta3中Swift语法更新
<table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead>
<tr>
<th scope="col" width="100">发布日期</th>
<th scope="col">语法变更记录</th>
</tr>
</thead>
<tbody>
<tr>
<td scope="row">2014-07-7</td>
<td><ul class="list-bullet">
<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, Arrays和Dictionaries进行赋值和拷贝 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, Arrays和Dictionaries进行赋值和拷贝 Assignment and Copy Behavior for Strings, Arrays, and Dictionaries</a>.
</li> </li>
@ -616,52 +568,6 @@
<li> <li>
添加一个例子 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Generics.html#//apple_ref/doc/uid/TP40014097-CH26-XID_285">扩展一个泛型Extending a Generic Type</a> 添加一个例子 <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Generics.html#//apple_ref/doc/uid/TP40014097-CH26-XID_285">扩展一个泛型Extending a Generic Type</a>
</li> </li>
</ul>
</td>
</tr>
</tbody>
</table>
<a name="xcode6_beta2"></a>
#### XCode6 Beta2中Swift语法更新
<table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead>
<tr>
<th scope="col" width="100">发布日期</th>
<th scope="col">语法变更记录</th>
</tr>
</thead>
<tbody>
<tr>
<td scope="row">2014-07-7</td>
<td><ul class="list-bullet">
<li>
发布新的文档用以详述Swift - 苹果公司针对iOS和OS X应用的全新开发语言
</li>
</ul>
</td>
</tr>
</tbody>
</table>
<a name="xcode6_beta1"></a>
#### XCode6 Beta1中Swift语法更新
<table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead>
<tr>
<th scope="col" width="100">发布日期</th>
<th scope="col">语法变更记录</th>
</tr>
</thead>
<tbody>
<tr>
<td scope="row">2014-06-3</td>
<td><ul class="list-bullet">
<li>
苹果全球开发者大会WWDC2014召开发布了苹果最新的开发语言Swift并释放出XCode6 Beta1版本
</li>
</ul> </ul>
</td> </td>
</tr> </tr>

View File

@ -11,6 +11,9 @@
> 2.1 > 2.1
> 翻译:[Prayer](https://github.com/futantan) > 翻译:[Prayer](https://github.com/futantan)
> 校对:[shanks](http://codebuild.me)[overtrue](https://github.com/overtrue) > 校对:[shanks](http://codebuild.me)[overtrue](https://github.com/overtrue)
>
> 2.2
> 校对:[SketchK](https://github.com/SketchK) 2016-05-11
本页包含内容: 本页包含内容:
@ -744,7 +747,7 @@ do {
在此例中,`makeASandwich()`(做一个三明治)函数会抛出一个错误消息如果没有干净的盘子或者某个原料缺失。因为`makeASandwich()`抛出错误,函数调用被包裹在`try`表达式中。将函数包裹在一个`do`语句中,任何被抛出的错误会被传播到提供的`catch`从句中。 在此例中,`makeASandwich()`(做一个三明治)函数会抛出一个错误消息如果没有干净的盘子或者某个原料缺失。因为`makeASandwich()`抛出错误,函数调用被包裹在`try`表达式中。将函数包裹在一个`do`语句中,任何被抛出的错误会被传播到提供的`catch`从句中。
如果没有错误被抛出, `eatASandwich()`函数会被调用。如果一个匹配`Error.OutOfCleanDishes`的错误被抛出,`washDishes`函数会被调用。如果一个匹配`Error.MissingIngredients`的错误被抛出,`buyGroceries(_:)`函数会随着被`catch`所捕捉到的关联值`[String]`被调用 如果没有错误被抛出, `eatASandwich()`函数会被调用。如果一个匹配`Error.OutOfCleanDishes`的错误被抛出,`washDishes`函数会被调用。如果一个匹配`Error.MissingIngredients`的错误被抛出,`buyGroceries(_:)`函数会被调用,并且使用`catch`所捕捉到的关联值`[String]`作为参数
抛出,捕捉,以及传播错误会在[错误处理](./18_Error_Handling.html)章节详细说明。 抛出,捕捉,以及传播错误会在[错误处理](./18_Error_Handling.html)章节详细说明。
@ -759,7 +762,7 @@ do {
如果你的代码在调试环境下触发了一个断言,比如你在 Xcode 中构建并运行一个应用,你可以清楚地看到不合法的状态发生在哪里并检查断言被触发时你的应用的状态。此外,断言允许你附加一条调试信息。 如果你的代码在调试环境下触发了一个断言,比如你在 Xcode 中构建并运行一个应用,你可以清楚地看到不合法的状态发生在哪里并检查断言被触发时你的应用的状态。此外,断言允许你附加一条调试信息。
你可以使用全局`assert(_:_file:line:)`函数来写一个断言。向这个函数传入一个结果为`true`或者`false`的表达式以及一条信息,当表达式的结果为`false`的时候这条信息会被显示: 你可以使用全局`assert(_:_:file:line:)`函数来写一个断言。向这个函数传入一个结果为`true`或者`false`的表达式以及一条信息,当表达式的结果为`false`的时候这条信息会被显示:
```swift ```swift
let age = -3 let age = -3
@ -790,5 +793,5 @@ assert(age >= 0)
> 注意: > 注意:
断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。 断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。

View File

@ -6,46 +6,49 @@
> 校对:[EvilCome](https://github.com/Evilcome) > 校对:[EvilCome](https://github.com/Evilcome)
> 2.0 > 2.0
> 翻译+校对:[JackAlan](https://github.com/AlanMelody) > 翻译+校对:[JackAlan](https://github.com/AlanMelody)
> 2.1 > 2.1
> 校对:[shanks](http://codebuild.me) > 校对:[shanks](http://codebuild.me)
> 2.2
> 翻译+校对:[Cee](https://github.com/Cee) 校对:[SketchK](https://github.com/SketchK)2016-05-11
本页包含内容: 本页包含内容:
- [术语](#terminology) - [术语](#terminology)
- [赋值运算符](#assignment_operator) - [赋值运算符](#assignment_operator)
- [算术运算符](#arithmetic_operators) - [算术运算符](#arithmetic_operators)
- [组合赋值运算符Compound Assignment Operators](#compound_assignment_operators) - [组合赋值运算符](#compound_assignment_operators)
- [比较运算符](#comparison_operators) - [比较运算符](#comparison_operators)
- [三目运算符Ternary Conditional Operator](#ternary_conditional_operator) - [三目运算符](#ternary_conditional_operator)
- [空合运算符](#nil_coalescing_operator) - [空合运算符](#nil_coalescing_operator)
- [区间运算符](#range_operators) - [区间运算符](#range_operators)
- [逻辑运算符](#logical_operators) - [逻辑运算符](#logical_operators)
运算符是检查、改变、合并值的特殊符号或短语。例如,加号`+`将两个数相加(如`let i = 1 + 2`)。更复杂的运算例子包括逻辑与运算符`&&`(如`if enteredDoorCode && passedRetinaScan`,或让 i 值加1的便捷自增运算符`++i` 运算符是检查、改变、合并值的特殊符号或短语。例如,加号`+`将两个数相加(如 `let i = 1 + 2`)。更复杂的运算例子包括逻辑与运算符 `&&`(如 `if enteredDoorCode && passedRetinaScan`)。
Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减少常规编码错误。如:赋值符(`=`)不返回值,以防止把想要判断相等运算符(`==`)的地方写成赋值符导致的错误。算术运算符(`+``-``*``/``%`等)会检测并不允许值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见[溢出运算符](../chapter2/25_Advanced_Operators.html#overflow_operators)。 Swift 支持大部分标准 C 语言的运算符,且改进许多特性来减少常规编码错误。如:赋值符(`=`)不返回值,以防止把想要判断相等运算符(`==`)的地方写成赋值符导致的错误。算术运算符(`+``-``*``/``%`等)会检测并不允许值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见[溢出运算符](../chapter2/25_Advanced_Operators.html#overflow_operators)。
区别于 C 语言,在 Swift 中你可以对浮点数进行取余运算(`%`Swift 还提供了 C 语言没有的表达两数之间的值的区间运算符(`a..<b``a...b`),这方便我们表达一个区间内的数值。 区别于 C 语言,在 Swift 中你可以对浮点数进行取余运算(`%`Swift 还提供了 C 语言没有的表达两数之间的值的区间运算符(`a..<b``a...b`),这方便我们表达一个区间内的数值。
本章节只描述了 Swift 中的基本运算符,[高级运算符](../chapter2/25_Advanced_Operators.html)包含了高级运算符,及如何自定义运算符,及如何进行自定义类型的运算符重载。 本章节只描述了 Swift 中的基本运算符,[高级运算符](../chapter2/25_Advanced_Operators.html)这章会包含 Swift 中的高级运算符,及如何自定义运算符,及如何进行自定义类型的运算符重载。
<a name="terminology"></a> <a name="terminology"></a>
## 术语 ## 术语
运算符一元、二元和三元运算符。 运算符分为一元、二元和三元运算符。
- 一元运算符对单一操作对象操作(如`-a`)。一元运算符分前置运算符和后置运算符,前置运算符需紧跟在操作对象之前(如`!b`),后置运算符需紧跟在操作对象之后(如`i++`)。 - 一元运算符对单一操作对象操作(如 `-a`)。一元运算符分前置运算符和后置运算符,前置运算符需紧跟在操作对象之前(如 `!b`),后置运算符需紧跟在操作对象之后(如 `c!`)。
- 二元运算符操作两个操作对象(如`2 + 3`),是中置的,因为它们出现在两个操作对象之间。 - 二元运算符操作两个操作对象(如 `2 + 3`),是中置的,因为它们出现在两个操作对象之间。
- 三元运算符操作三个操作对象,和 C 语言一样Swift 只有一个三元运算符,就是三目运算符(`a ? b : c`)。 - 三元运算符操作三个操作对象,和 C 语言一样Swift 只有一个三元运算符,就是三目运算符(`a ? b : c`)。
受运算符影响的值叫操作数,在表达式`1 + 2`中,加号`+`是二元运算符,它的两个操作数是值`1``2` 受运算符影响的值叫操作数,在表达式 `1 + 2` 中,加号 `+` 是二元运算符,它的两个操作数是值 `1``2`
<a name="assignment_operator"></a> <a name="assignment_operator"></a>
## 赋值运算符 ## 赋值运算符
赋值运算(`a = b`),表示用`b`的值来初始化或更新`a`的值: 赋值运算(`a = b`),表示用 `b` 的值来初始化或更新 `a` 的值:
```swift ```swift
let b = 10 let b = 10
@ -58,7 +61,7 @@ a = b
```swift ```swift
let (x, y) = (1, 2) let (x, y) = (1, 2)
// 现在 x 等于 1, y 等于 2 // 现在 x 等于 1y 等于 2
``` ```
与 C 语言和 Objective-C 不同Swift 的赋值操作并不返回任何值。所以以下代码是错误的: 与 C 语言和 Objective-C 不同Swift 的赋值操作并不返回任何值。所以以下代码是错误的:
@ -69,7 +72,7 @@ if x = y {
} }
``` ```
这个特性使你无法把(`==`)错写成(`=`),由于`if x = y`是错误代码Swift帮你避免此类错误的的发生。 这个特性使你无法把(`==`)错写成(`=`),由于 `if x = y` 是错误代码Swift帮你避免此类错误发生。
<a name="arithmetic_operators"></a> <a name="arithmetic_operators"></a>
## 算术运算符 ## 算术运算符
@ -88,9 +91,9 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
10.0 / 2.5 // 等于 4.0 10.0 / 2.5 // 等于 4.0
``` ```
与 C 语言和 Objective-C 不同的是Swift 默认情况下不允许在数值运算中出现溢出情况。但是你可以使用 Swift 的溢出运算符来实现溢出运算(如`a &+ b`)。详情参见[溢出运算符](../chapter2/25_Advanced_Operators.html#overflow_operators)。 与 C 语言和 Objective-C 不同的是Swift 默认情况下不允许在数值运算中出现溢出情况。但是你可以使用 Swift 的溢出运算符来实现溢出运算(如 `a &+ b`)。详情参见[溢出运算符](../chapter2/25_Advanced_Operators.html#overflow_operators)。
加法运算符也可用于`String`的拼接: 加法运算符也可用于 `String` 的拼接:
```swift ```swift
"hello, " + "world" // 等于 "hello, world" "hello, " + "world" // 等于 "hello, world"
@ -98,16 +101,16 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
### 求余运算符 ### 求余运算符
求余运算(`a % b`)是计算`b`的多少倍刚刚好可以容入`a`,返回多出来的那部分(余数)。 求余运算(`a % b`)是计算 `b` 的多少倍刚刚好可以容入`a`,返回多出来的那部分(余数)。
>注意: > 注意:
求余运算(`%`)在其他语言也叫取模运算。然而严格说来,我们看该运算符对负数的操作结果,"求余"比"取模"更合适些。 求余运算(`%`)在其他语言也叫取模运算。然而严格说来,我们看该运算符对负数的操作结果,求余」比「取模更合适些。
我们来谈谈取余是怎么回事,计算`9 % 4`,你先计算出`4`的多少倍会刚好可以容入`9`中: 我们来谈谈取余是怎么回事,计算 `9 % 4`,你先计算出 `4` 的多少倍会刚好可以容入 `9` 中:
![Art/remainderInteger_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/remainderInteger_2x.png "Art/remainderInteger_2x.png") ![Art/remainderInteger_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/remainderInteger_2x.png "Art/remainderInteger_2x.png")
2倍非常好那余数是1用橙色标出 你可以在 `9` 中放入两个 `4`,那余数是 1用橙色标出
在 Swift 中可以表达为: 在 Swift 中可以表达为:
@ -115,13 +118,13 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
9 % 4 // 等于 1 9 % 4 // 等于 1
``` ```
为了得到`a % b`的结果,`%`计算了以下等式,并输出`余数`作为结果: 为了得到 `a % b` 的结果,`%` 计算了以下等式,并输出`余数`作为结果:
a = (b × 倍数) + 余数 a = (b × 倍数) + 余数
`倍数`取最大值的时候,就会刚好可以容入`a`中。 `倍数`取最大值的时候,就会刚好可以容入 `a` 中。
`9``4`代入等式中,我们得`1` `9``4` 代入等式中,我们得 `1`
9 = (4 × 2) + 1 9 = (4 × 2) + 1
@ -131,13 +134,13 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
-9 % 4 // 等于 -1 -9 % 4 // 等于 -1
``` ```
`-9``4`代入等式,`-2`是取到的最大整数: `-9``4` 代入等式,`-2` 是取到的最大整数:
-9 = (4 × -2) + -1 -9 = (4 × -2) + -1
余数是`-1` 余数是 `-1`
在对负数`b`求余时,`b`的符号会被忽略。这意味着 `a % b``a % -b`的结果是相同的。 在对负数 `b` 求余时,`b` 的符号会被忽略。这意味着 `a % b``a % -b` 的结果是相同的。
### 浮点数求余计算 ### 浮点数求余计算
@ -147,46 +150,14 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
8 % 2.5 // 等于 0.5 8 % 2.5 // 等于 0.5
``` ```
这个例子中,`8`除于`2.5`等于`3``0.5`,所以结果是一个`Double``0.5` 这个例子中,`8` 除以 `2.5` 等于 `3``0.5`,所以结果是一个 `Double` 型的值为 `0.5`
![Art/remainderFloat_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/remainderFloat_2x.png "Art/remainderFloat_2x.png") ![Art/remainderFloat_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/remainderFloat_2x.png "Art/remainderFloat_2x.png")
### 自增和自减运算
和 C 语言一样Swift 也提供了对变量本身加1或减1的自增`++`)和自减(`--`)的缩略算符。其操作对象可以是整形和浮点型。
```swift
var i = 0
++i // 现在 i = 1
```
每调用一次`++i``i`的值就会加1。实际上`++i``i = i + 1`的简写,而`--i``i = i - 1`的简写。
`++``--`既可以用作前置运算又可以用作后置运算。`++i``i++``--i``i--`都是有效的写法。
我们需要注意的是这些运算符即可修改了`i`的值也可以返回`i`的值。如果你只想修改`i`的值,那你就可以忽略这个返回值。但如果你想使用返回值,你就需要留意前置和后置操作的返回值是不同的,她们遵循以下原则:
-`++`前置的时候,先自増再返回。
-`++`后置的时候,先返回再自增。
例如:
```swift
var a = 0
let b = ++a // a 和 b 现在都是 1
let c = a++ // a 现在 2, 但 c 是 a 自增前的值 1
```
上述例子,`let b = ++a`先把`a`加1了再返回`a`的值。所以`a``b`都是新值`1`
`let c = a++`,是先返回了`a`的值,然后`a`才加1。所以`c`得到了`a`的旧值1`a`加1后变成2。
除非你需要使用`i++`的特性,不然推荐你使用`++i``--i`,因为先修改后返回这样的行为更符合我们的逻辑。
### 一元负号运算符 ### 一元负号运算符
数值的正负号可以使用前缀`-`(即一元负号)来切换: 数值的正负号可以使用前缀 `-`(即一元负号)来切换:
```swift ```swift
let three = 3 let three = 3
@ -205,28 +176,29 @@ let minusSix = -6
let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6 let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6
``` ```
虽然一元`+`什么都不会改变,但当你在使用一元负号来表达负数时,你可以使用一元正号来表达正数,如此你的代码会具有对称美。 虽然一元 `+` 什么都不会改变,但当你在使用一元负号来表达负数时,你可以使用一元正号来表达正数,如此你的代码会具有对称美。
<a name="compound_assignment_operators"></a> <a name="compound_assignment_operators"></a>
## 组合赋值运算符Compound Assignment Operators ## 组合赋值运算符
如同 C 语言Swift 也提供把其他运算符和赋值运算(`=`)组合的组合赋值运算符,组合加运算(`+=`)是其中一个例子: 如同 C 语言Swift 也提供把其他运算符和赋值运算(`=`)组合的组合赋值运算符,组合加运算(`+=`)是其中一个例子:
```swift ```swift
var a = 1 var a = 1
a += 2 // a 现在是 3 a += 2
// a 现在是 3
``` ```
表达式`a += 2``a = a + 2`的简写,一个组合加运算就是把加法运算和赋值运算组合成进一个运算符里,同时完成两个运算任务。 表达式 `a += 2``a = a + 2` 的简写,一个组合加运算就是把加法运算和赋值运算组合成进一个运算符里,同时完成两个运算任务。
>注意: > 注意:
复合赋值运算没有返回值,`let b = a += 2`这类代码是错误。这不同于上面提到的自增和自减运算符。 复合赋值运算没有返回值,`let b = a += 2`这类代码是错误。这不同于上面提到的自增和自减运算符。
在[表达式](../chapter3/04_Expressions.html)章节里有复合运算符的完整列表。 在[表达式](../chapter3/04_Expressions.html)章节里有复合运算符的完整列表。
<a name="comparison_operators"></a> <a name="comparison_operators"></a>
## 比较运算符 ## 比较运算符Comparison Operators
所有标准 C 语言中的比较运算都可以在 Swift 中使用: 所有标准 C 语言中的比较运算都可以在 Swift 中使用:
@ -238,7 +210,7 @@ a += 2 // a 现在是 3
- 小于等于(`a <= b` - 小于等于(`a <= b`
> 注意: > 注意:
Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在[类与结构](../chapter2/09_Classes_and_Structures.html)。 Swift 也提供恒等`===`和不恒等`!==`这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在[类与结构](../chapter2/09_Classes_and_Structures.html)。
每个比较运算都返回了一个标识表达式是否成立的布尔值: 每个比较运算都返回了一个标识表达式是否成立的布尔值:
@ -263,12 +235,25 @@ if name == "world" {
// 输出 "hello, world", 因为 `name` 就是等于 "world" // 输出 "hello, world", 因为 `name` 就是等于 "world"
``` ```
关于`if`语句,请看[控制流](../chapter2/05_Control_Flow.html)。 关于 `if` 语句,请看[控制流](../chapter2/05_Control_Flow.html)。
当元组中的值可以比较时,你也可以使用这些运算符来比较它们的大小。例如,因为 `Int``String` 类型的值可以比较,所以类型为 `(Int, String)` 的元组也可以被比较。相反,`Bool` 不能被比较,也意味着存有布尔类型的元组不能被比较。
比较元组大小会按照从左到右、逐值比较的方式,直到发现有两个值不等时停止。如果所有的值都相等,那么这一对元组我们就称它们是相等的。例如:
```swift
(1, "zebra") < (2, "apple") // true因为 1 小于 2
(3, "apple") < (3, "bird") // true因为 3 等于 3但是 apple 小于 bird
(4, "dog") == (4, "dog") // true因为 4 等于 4dog 等于 dog
```
> 注意:
Swift 标准库只能比较七个以内元素的元组比较函数。如果你的元组元素超过七个时,你需要自己实现比较运算符。
<a name="ternary_conditional_operator"></a> <a name="ternary_conditional_operator"></a>
## 三目运算符(Ternary Conditional Operator) ## 三目运算符Ternary Conditional Operator
三目运算符的特殊在于它是有三个操作数的运算符,它的原型`问题 ? 答案1 : 答案2`。它简洁地表达根据`问题`成立与否作出二选一的操作。如果`问题`成立,返回`答案1`的结果; 如果不成立,返回`答案2`的结果。 三目运算符的特殊在于它是有三个操作数的运算符,它的形式`问题 ? 答案 1 : 答案 2`。它简洁地表达根据 `问题`成立与否作出二选一的操作。如果 `问题` 成立,返回 `答案 1` 的结果;反之返回 `答案 2` 的结果。
三目运算符是以下代码的缩写形式: 三目运算符是以下代码的缩写形式:
@ -280,7 +265,7 @@ if question {
} }
``` ```
这里有个计算表格行高的例子。如果有表头那行高应比内容高度要高出50点如果没有表头只需高出20点 这里有个计算表格行高的例子。如果有表头,那行高应比内容高度要高出 50 点;如果没有表头,只需高出 20 点:
```swift ```swift
let contentHeight = 40 let contentHeight = 40
@ -303,28 +288,25 @@ if hasHeader {
// rowHeight 现在是 90 // rowHeight 现在是 90
``` ```
第一段代码例子使用了三目运算,所以一行代码就能让我们得到正确答案。这比第二段代码简洁得多,无需将`rowHeight`定义成变量,因为它的值无需在`if`语句中改变。 第一段代码例子使用了三目运算,所以一行代码就能让我们得到正确答案。这比第二段代码简洁得多,无需将 `rowHeight` 定义成变量,因为它的值无需在 `if` 语句中改变。
三目运算提供有效率且便捷的方式来表达二选一的选择。需要注意的事,过度使用三目运算符会使简洁的代码变的难懂。我们应避免在一个组合语句中使用多个三目运算符。 三目运算提供有效率且便捷的方式来表达二选一的选择。需要注意的事,过度使用三目运算符会使简洁的代码变的难懂。我们应避免在一个组合语句中使用多个三目运算符。
<a name="nil_coalescing_operator"></a> <a name="nil_coalescing_operator"></a>
## 空合运算符(Nil Coalescing Operator) ## 空合运算符Nil Coalescing Operator
空合运算符(`a ?? b`)将对可选类型`a`进行空判断,如果`a`包含一个值就进行解封,否则就返回一个默认值`b`.这个运算符有两个条件: 空合运算符`a ?? b`将对可选类型 `a` 进行空判断,如果 `a` 包含一个值就进行解封,否则就返回一个默认值 `b`。表达式 `a` 必须是 Optional 类型。默认值 `b` 的类型必须要和 `a` 存储值的类型保持一致。
- 表达式`a`必须是Optional类型 空合运算符是对以下代码的简短表达方法:
- 默认值`b`的类型必须要和`a`存储值的类型保持一致
空合运算符是对以下代码的简短表达方法
```swift ```swift
a != nil ? a! : b a != nil ? a! : b
``` ```
上述代码使用了三目运算符。当可选类型`a`的值不为空时,进行强制解封(`a!`)访问`a`中值,反之当`a`中值为空时返回默认值b。无疑空合运算符(`??`)提供了一种更为优雅的方式去封装条件判断和解封两种行为,显得简洁以及更具可读性。 上述代码使用了三目运算符。当可选类型 `a` 的值不为空时,进行强制解封`a!`访问 `a` 中的值;反之返回默认值 `b`。无疑空合运算符`??`提供了一种更为优雅的方式去封装条件判断和解封两种行为,显得简洁以及更具可读性。
> 注意: > 注意:
如果`a`为非空值(`non-nil`),那么值`b`将不会被估值。这也就是所谓的短路求值。 如果 `a` 为非空值`non-nil`那么值 `b` 将不会被计算。这也就是所谓的短路求值。
下文例子采用空合运算符,实现了在默认颜色名和可选自定义颜色名之间抉择: 下文例子采用空合运算符,实现了在默认颜色名和可选自定义颜色名之间抉择:
@ -336,10 +318,10 @@ var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 的值为空,所以 colorNameToUse 的值为 "red" // userDefinedColorName 的值为空,所以 colorNameToUse 的值为 "red"
``` ```
`userDefinedColorName`变量被定义为一个可选`String`类型,默认值为`nil`。由于`userDefinedColorName`是一个可选类型,我们可以使用空合运算符去判断其值。在上一个例子中,通过空合运算符为一个名为`colorNameToUse`的变量赋予一个字符串类型初始值。 `userDefinedColorName` 变量被定义为一个可选`String` 类型,默认值为 `nil`。由于 `userDefinedColorName` 是一个可选类型,我们可以使用空合运算符去判断其值。在上一个例子中,通过空合运算符为一个名为 `colorNameToUse` 的变量赋予一个字符串类型初始值。
由于`userDefinedColorName`值为空,因此表达式`userDefinedColorName ?? defaultColorName`返回`defaultColorName`的值,即`red` 由于 `userDefinedColorName` 值为空,因此表达式 `userDefinedColorName ?? defaultColorName` 返回 `defaultColorName` 的值,即 `red`
另一种情况,分配一个非空值(`non-nil`)给`userDefinedColorName`,再次执行空合运算,运算结果为封包在`userDefaultColorName`中的值,而非默认值。 另一种情况,分配一个非空值`non-nil`)给 `userDefinedColorName`,再次执行空合运算,运算结果为封包在 `userDefaultColorName` 中的值,而非默认值。
```swift ```swift
userDefinedColorName = "green" userDefinedColorName = "green"
@ -348,14 +330,14 @@ colorNameToUse = userDefinedColorName ?? defaultColorName
``` ```
<a name="range_operators"></a> <a name="range_operators"></a>
## 区间运算符 ## 区间运算符Range Operators
Swift 提供了两个方便表达一个区间的值的运算符。 Swift 提供了两个方便表达一个区间的值的运算符。
### 闭区间运算符 ### 闭区间运算符
闭区间运算符(`a...b`)定义一个包含从`a``b`(包括`a``b`)的所有值的区间`b`必须大于等于`a` 闭区间运算符(`a...b`)定义一个包含从 `a``b`包括 `a``b`的所有值的区间`a` 的值不能超过 `b`
闭区间运算符在迭代一个区间的所有值时是非常有用的,如在`for-in`循环中: 闭区间运算符在迭代一个区间的所有值时是非常有用的,如在 `for-in` 循环中:
```swift ```swift
for index in 1...5 { for index in 1...5 {
@ -368,14 +350,14 @@ for index in 1...5 {
// 5 * 5 = 25 // 5 * 5 = 25
``` ```
关于`for-in`,请看[控制流](../chapter2/05_Control_Flow.html)。 关于 `for-in`,请看[控制流](../chapter2/05_Control_Flow.html)。
### 半开区间运算符 ### 半开区间运算符
半开区间(`a..<b`)定义一个从`a``b`但不包括`b`的区间。 半开区间(`a..<b`)定义一个从 `a``b` 但不包括 `b` 的区间。
之所以称为半开区间,是因为该区间包含第一个值而不包括最后的值。 之所以称为半开区间,是因为该区间包含第一个值而不包括最后的值。
半开区间的实用性在于当你使用一个从0开始的列表(如数组)非常方便地从0数到列表的长度。 半开区间的实用性在于当你使用一个从 0 开始的列表如数组非常方便地从0数到列表的长度。
```swift ```swift
let names = ["Anna", "Alex", "Brian", "Jack"] let names = ["Anna", "Alex", "Brian", "Jack"]
@ -389,10 +371,10 @@ for i in 0..<count {
// 第 4 个人叫 Jack // 第 4 个人叫 Jack
``` ```
数组有4个元素,但`0..<count`只数到3(最后一个元素的下标),因为它是半开区间。关于数组,请查阅[数组](../chapter2/04_Collection_Types.html#arrays)。 数组有 4 个元素,但 `0..<count` 只数到3最后一个元素的下标,因为它是半开区间。关于数组,请查阅[数组](../chapter2/04_Collection_Types.html#arrays)。
<a name="logical_operators"></a> <a name="logical_operators"></a>
## 逻辑运算 ## 逻辑运算Logical Operators
逻辑运算的操作对象是逻辑布尔值。Swift 支持基于 C 语言的三个标准逻辑运算。 逻辑运算的操作对象是逻辑布尔值。Swift 支持基于 C 语言的三个标准逻辑运算。
@ -402,9 +384,9 @@ for i in 0..<count {
### 逻辑非 ### 逻辑非
逻辑非运算(`!a`)对一个布尔值取反,使得`true``false``false``true` 逻辑非运算(`!a`)对一个布尔值取反,使得 `true``false``false``true`
它是一个前置运算符,需紧跟在操作数之前,且不加空格。读作`非 a`,例子如下: 它是一个前置运算符,需紧跟在操作数之前,且不加空格。读作 `非 a` ,例子如下:
```swift ```swift
let allowedEntry = false let allowedEntry = false
@ -414,17 +396,17 @@ if !allowedEntry {
// 输出 "ACCESS DENIED" // 输出 "ACCESS DENIED"
``` ```
`if !allowedEntry`语句可以读作如果非 allowedEntry。”,接下一行代码只有在非 allowedEntry”为`true`,即`allowEntry``false`时被执行。 `if !allowedEntry` 语句可以读作如果非 allowedEntry,接下一行代码只有在非 allowedEntry」为 `true`,即 `allowEntry``false` 时被执行。
在示例代码中,小心地选择布尔常量或变量有助于代码的可读性,并且避免使用双重逻辑非运算,或混乱的逻辑语句。 在示例代码中,小心地选择布尔常量或变量有助于代码的可读性,并且避免使用双重逻辑非运算,或混乱的逻辑语句。
### 逻辑与 ### 逻辑与
逻辑与(`a && b`)表达了只有`a``b`的值都为`true`时,整个表达式的值才会是`true` 逻辑与(`a && b`)表达了只有 `a``b` 的值都为 `true` 时,整个表达式的值才会是 `true`
只要任意一个值为`false`,整个表达式的值就为`false`。事实上,如果第一个值为`false`,那么是不去计算第二个值的,因为它已经不可能影响整个表达式的结果了。这被称做短路计算short-circuit evaluation 只要任意一个值为 `false`,整个表达式的值就为 `false`。事实上,如果第一个值为 `false`,那么是不去计算第二个值的,因为它已经不可能影响整个表达式的结果了。这被称做短路计算short-circuit evaluation
以下例子,只有两个`Bool`值都为`true`的时候才允许进入: 以下例子,只有两个 `Bool` 值都为 `true` 的时候才允许进入 if
```swift ```swift
let enteredDoorCode = true let enteredDoorCode = true
@ -439,11 +421,11 @@ if enteredDoorCode && passedRetinaScan {
### 逻辑或 ### 逻辑或
逻辑或(`a || b`)是一个由两个连续的`|`组成的中置运算符。它表示了两个逻辑表达式的其中一个为`true`,整个表达式就为`true` 逻辑或(`a || b`)是一个由两个连续的 `|` 组成的中置运算符。它表示了两个逻辑表达式的其中一个为 `true`,整个表达式就为 `true`
同逻辑与运算类似,逻辑或也是短路计算的,当左端的表达式为`true`时,将不计算右边的表达式了,因为它不可能改变整个表达式的值了。 同逻辑与运算类似,逻辑或也是短路计算的,当左端的表达式为 `true` 时,将不计算右边的表达式了,因为它不可能改变整个表达式的值了。
以下示例代码中,第一个布尔值(`hasDoorKey`)为`false`,但第二个值(`knowsOverridePassword`)为`true`,所以整个表达是`true`,于是允许进入: 以下示例代码中,第一个布尔值(`hasDoorKey`)为 `false`,但第二个值(`knowsOverridePassword`)为 `true`,所以整个表达是 `true`,于是允许进入:
```swift ```swift
let hasDoorKey = false let hasDoorKey = false
@ -469,14 +451,14 @@ if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
// 输出 "Welcome!" // 输出 "Welcome!"
``` ```
这个例子使用了含多个`&&``||`的复合逻辑。但无论怎样,`&&``||`始终只能操作两个值。所以这实际是三个简单逻辑连续操作的结果。我们来解读一下: 这个例子使用了含多个 `&&``||` 的复合逻辑。但无论怎样,`&&``||` 始终只能操作两个值。所以这实际是三个简单逻辑连续操作的结果。我们来解读一下:
如果我们输入了正确的密码并通过了视网膜扫描,或者我们有一把有效的钥匙,又或者我们知道紧急情况下重置的密码,我们就能把门打开进入。 如果我们输入了正确的密码并通过了视网膜扫描,或者我们有一把有效的钥匙,又或者我们知道紧急情况下重置的密码,我们就能把门打开进入。
前两种情况,我们都不满足,所以前两个简单逻辑的结果是`false`,但是我们是知道紧急情况下重置的密码的,所以整个复杂表达式的值还是`true` 前两种情况,我们都不满足,所以前两个简单逻辑的结果是 `false`,但是我们是知道紧急情况下重置的密码的,所以整个复杂表达式的值还是 `true`
>注意: > 注意:
Swift 逻辑操作符`&&``||`是左结合的,这意味着拥有多元逻辑操作符的复合表达式优先计算最左边的子表达式。 Swift 逻辑操作符 `&&``||` 是左结合的,这意味着拥有多元逻辑操作符的复合表达式优先计算最左边的子表达式。
### 使用括号来明确优先级 ### 使用括号来明确优先级

View File

@ -10,7 +10,13 @@
> 2.1 > 2.1
> 翻译:[DianQK](https://github.com/DianQK) > 翻译:[DianQK](https://github.com/DianQK)
> 校对:[shanks](http://codebuild.me), [Realank](https://github.com/Realank) > 校对:[shanks](http://codebuild.me), [Realank](https://github.com/Realank),
> 2.2
> 校对:[SketchK](https://github.com/SketchK)
> 3.0
> 校对:[CMB](https://github.com/chenmingbiao)
本页包含内容: 本页包含内容:
@ -27,11 +33,12 @@
- [比较字符串](#comparing_strings) - [比较字符串](#comparing_strings)
- [字符串的 Unicode 表示形式](#unicode_representations_of_strings) - [字符串的 Unicode 表示形式](#unicode_representations_of_strings)
`String`是例如"hello, world""albatross"这样的有序的`Character`(字符)类型的值的集合。通过`String`类型来表示。 `String`是例如"hello, world""albatross"这样的有序的`Character`(字符)类型的值的集合。通过`String`类型来表示。
一个`String`的内容可以用变量的方式读取,它包括一个`Character`值的集合。 一个`String`的内容可以用许多方式读取,它包括一个`Character`值的集合。
创建和操作字符串的语法与 C 语言中字符串操作相似,轻量并且易读。 创建和操作字符串的语法与 C 语言中字符串操作相似,轻量并且易读。
字符串连接操作只需要简单地通过`+`符号将两个字符串相连即可。与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。你也可以在字符串内插过程中使用字符串插入常量、变量、字面量表达成更长的字符串,这样可以很容易的创建自定义的字符串值,进行展示、存储以及打印。 字符串连接操作只需要简单地通过`+`符号将两个字符串相连即可。与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。你也可以在字符串内插过程中使用字符串插入常量、变量、字面量表达成更长的字符串,这样可以很容易的创建自定义的字符串值,进行展示、存储以及打印。
尽管语法简易,但`String`类型是一种快速、现代化的字符串实现。 尽管语法简易,但`String`类型是一种快速、现代化的字符串实现。
每一个字符串都是由编码无关的 Unicode 字符组成,并支持访问字符的多种 Unicode 表示形式representations 每一个字符串都是由编码无关的 Unicode 字符组成,并支持访问字符的多种 Unicode 表示形式representations
@ -40,7 +47,6 @@
> 更多关于在 Foundation 和 Cocoa 中使用`String`的信息请查看 *[Using Swift with Cocoa and Objective-C (Swift 2.1)](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216)*。 > 更多关于在 Foundation 和 Cocoa 中使用`String`的信息请查看 *[Using Swift with Cocoa and Objective-C (Swift 2.1)](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216)*。
<a name="string_literals"></a> <a name="string_literals"></a>
## 字符串字面量String Literals ## 字符串字面量String Literals
@ -77,6 +83,7 @@ if emptyString.isEmpty {
// 打印输出:"Nothing to see here" // 打印输出:"Nothing to see here"
``` ```
<a name="string_mutability"></a> <a name="string_mutability"></a>
## 字符串可变性 (String Mutability) ## 字符串可变性 (String Mutability)
@ -95,6 +102,7 @@ constantString += " and another Highlander"
> 注意: > 注意:
在 Objective-C 和 Cocoa 中,您需要通过选择两个不同的类(`NSString``NSMutableString`)来指定字符串是否可以被修改。 在 Objective-C 和 Cocoa 中,您需要通过选择两个不同的类(`NSString``NSMutableString`)来指定字符串是否可以被修改。
<a name="strings_are_value_types"></a> <a name="strings_are_value_types"></a>
## 字符串是值类型Strings Are Value Types ## 字符串是值类型Strings Are Value Types
@ -109,6 +117,7 @@ Swift 默认字符串拷贝的方式保证了在函数/方法中传递的是字
在实际编译时Swift 编译器会优化字符串的使用,使实际的复制只发生在绝对必要的情况下,这意味着您将字符串作为值类型的同时可以获得极高的性能。 在实际编译时Swift 编译器会优化字符串的使用,使实际的复制只发生在绝对必要的情况下,这意味着您将字符串作为值类型的同时可以获得极高的性能。
<a name="working_with_characters"></a> <a name="working_with_characters"></a>
## 使用字符Working with Characters ## 使用字符Working with Characters
@ -141,6 +150,7 @@ print(catString)
// 打印输出:"Cat!🐱" // 打印输出:"Cat!🐱"
``` ```
<a name="concatenating_strings_and_characters"></a> <a name="concatenating_strings_and_characters"></a>
## 连接字符串和字符 (Concatenating Strings and Characters) ## 连接字符串和字符 (Concatenating Strings and Characters)
@ -203,6 +213,7 @@ Unicode 是一个国际标准,用于文本的编码和表示。
它使您可以用标准格式表示来自任意语言几乎所有的字符,并能够对文本文件或网页这样的外部资源中的字符进行读写操作。 它使您可以用标准格式表示来自任意语言几乎所有的字符,并能够对文本文件或网页这样的外部资源中的字符进行读写操作。
Swift 的`String``Character`类型是完全兼容 Unicode 标准的。 Swift 的`String``Character`类型是完全兼容 Unicode 标准的。
<a name="unicode_scalars"></a> <a name="unicode_scalars"></a>
### Unicode 标量Unicode Scalars ### Unicode 标量Unicode Scalars
@ -248,7 +259,7 @@ let sparklingHeart = "\u{1F496}" // 💖, Unicode 标量 U+1F496
```swift ```swift
let eAcute: Character = "\u{E9}" // é let eAcute: Character = "\u{E9}" // é
let combinedEAcute: Character = "\u{65}\u{301}" // e 后面加上 ́ let combinedEAcute: Character = "\u{65}\u{301}" // e 后面加上 ́
// eAcute 是 é, combinedEAcute 是 // eAcute 是 é, combinedEAcute 是 é
``` ```
可扩展的字符群集是一个灵活的方法,用许多复杂的脚本字符表示单一的`Character`值。 可扩展的字符群集是一个灵活的方法,用许多复杂的脚本字符表示单一的`Character`值。
@ -259,7 +270,7 @@ let combinedEAcute: Character = "\u{65}\u{301}" // e 后面加上 ́
```swift ```swift
let precomposed: Character = "\u{D55C}" // 한 let precomposed: Character = "\u{D55C}" // 한
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ, ᅡ, ᆫ let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ, ᅡ, ᆫ
// precomposed 是 한, decomposed 是 한 // precomposed 是 한, decomposed 是
``` ```
可拓展的字符群集可以使包围记号(例如`COMBINING ENCLOSING CIRCLE`或者`U+20DD`)的标量包围其他 Unicode 标量,作为一个单一的`Character`值: 可拓展的字符群集可以使包围记号(例如`COMBINING ENCLOSING CIRCLE`或者`U+20DD`)的标量包围其他 Unicode 标量,作为一个单一的`Character`值:
@ -290,7 +301,7 @@ print("unusualMenagerie has \(unusualMenagerie.characters.count) characters")
注意在 Swift 中,使用可拓展的字符群集作为`Character`值来连接或改变字符串时,并不一定会更改字符串的字符数量。 注意在 Swift 中,使用可拓展的字符群集作为`Character`值来连接或改变字符串时,并不一定会更改字符串的字符数量。
例如,如果你用四个字符的单词`cafe`初始化一个新的字符串,然后添加一个`COMBINING ACTUE ACCENT`(`U+0301`)作为字符串的结尾。最终这个字符串的字符数量仍然是`4`,因为第四个字符是``,而不是`e` 例如,如果你用四个字符的单词`cafe`初始化一个新的字符串,然后添加一个`COMBINING ACTUE ACCENT`(`U+0301`)作为字符串的结尾。最终这个字符串的字符数量仍然是`4`,因为第四个字符是`é`,而不是`e`
```swift ```swift
var word = "cafe" var word = "cafe"
@ -323,31 +334,31 @@ print("the number of characters in \(word) is \(word.characters.count)")
使用`startIndex`属性可以获取一个`String`的第一个`Character`的索引。使用`endIndex`属性可以获取最后一个`Character`的后一个位置的索引。因此,`endIndex`属性不能作为一个字符串的有效下标。如果`String`是空串,`startIndex``endIndex`是相等的。 使用`startIndex`属性可以获取一个`String`的第一个`Character`的索引。使用`endIndex`属性可以获取最后一个`Character`的后一个位置的索引。因此,`endIndex`属性不能作为一个字符串的有效下标。如果`String`是空串,`startIndex``endIndex`是相等的。
通过调用`String.Index``predecessor()`方法,可以立即得到前面一个索引,调用`successor()`方法可以立即得到后面一个索引。任何一个`String`的索引都可以通过锁链作用的这些方法来获取另一个索引,也可以调用`advancedBy(_:)`方法来获取。但如果尝试获取出界的字符串索引,就会抛出一个运行时错误 通过调用 `String``index(before:)``index(after:)` 方法可以立即得到前面或后面一个索引。您还可以通过调用 `index(_:offsetBy:)` 方法来获取对应偏移量的索引,这种方式可以避免多次调用 `index(before:)``index(after:)` 方法。
你可以使用下标语法来访问`String`特定索引的`Character` 你可以使用下标语法来访问 `String` 特定索引的 `Character`
```swift ```swift
let greeting = "Guten Tag!" let greeting = "Guten Tag!"
greeting[greeting.startIndex] greeting[greeting.startIndex]
// G // G
greeting[greeting.endIndex.predecessor()] greeting[greeting.index(before: greeting.endIndex)]
// ! // !
greeting[greeting.startIndex.successor()] greeting[greeting.index(after: greeting.startIndex)]
// u // u
let index = greeting.startIndex.advancedBy(7) let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index] greeting[index]
// a // a
``` ```
试图获取越界索引对应的`Character`,将引发一个运行时错误。 试图获取越界索引对应的 `Character`,将引发一个运行时错误。
```swift ```swift
greeting[greeting.endIndex] // error greeting[greeting.endIndex] // error
greeting.endIndex.successor() // error greeting.index(after: endIndex) // error
``` ```
使用`characters`属性的`indices`属性会创建一个包含全部索引的范围(`Range`),用来在一个字符串中访问单个字符。 使用 `characters` 属性的 `indices` 属性会创建一个包含全部索引的范围(`Range`),用来在一个字符串中访问单个字符。
```swift ```swift
for index in greeting.characters.indices { for index in greeting.characters.indices {
@ -356,39 +367,37 @@ for index in greeting.characters.indices {
// 打印输出 "G u t e n T a g ! " // 打印输出 "G u t e n T a g ! "
``` ```
> 注意:
> 您可以使用 `startIndex` 和 `endIndex` 属性或者 `index(before:)` 、`index(after:)` 和 `index(_:offsetBy:)` 方法在任意一个确认的并遵循 `Collection` 协议的类型里面,如上文所示是使用在 `String` 中,您也可以使用在 `Array`、`Dictionary` 和 `Set`中。
<a name="inserting_and_removing"></a> <a name="inserting_and_removing"></a>
### 插入和删除 (Inserting and Removing) ### 插入和删除 (Inserting and Removing)
调用`insert(_:atIndex:)`方法可以在一个字符串的指定索引插入一个字符。 调用 `insert(_:atIndex:)` 方法可以在一个字符串的指定索引插入一个字符,调用 `insert(contentsOf:at:)` 方法可以在一个字符串的指定索引插入一个段字符串
```swift ```swift
var welcome = "hello" var welcome = "hello"
welcome.insert("!", atIndex: welcome.endIndex) welcome.insert("!", at: welcome.endIndex)
// welcome now 现在等于 "hello!" // welcome 变量现在等于 "hello!"
welcome.insert(contentsOf:" there".characters, at: welcome.index(before: welcome.endIndex))
// welcome 变量现在等于 "hello there!"
``` ```
调用`insertContentsOf(_:at:)`方法可以在一个字符串的指定索引插入一个字符串。 调用 `remove(at:)` 方法可以在一个字符串的指定索引删除一个字符,调用 `removeSubrange(_:)` 方法可以在一个字符串的指定索引删除一个字符串。
```swift ```swift
welcome.insertContentsOf(" there".characters, at: welcome.endIndex.predecessor()) welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome 现在等于 "hello there!"
```
调用`removeAtIndex(_:)`方法可以在一个字符串的指定索引删除一个字符。
```swift
welcome.removeAtIndex(welcome.endIndex.predecessor())
// welcome 现在等于 "hello there" // welcome 现在等于 "hello there"
```
let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
调用`removeRange(_:)`方法可以在一个字符串的指定索引删除一个子字符串。 welcome.removeSubrange(range)
```swift
let range = welcome.endIndex.advancedBy(-6)..<welcome.endIndex
welcome.removeRange(range)
// welcome 现在等于 "hello" // welcome 现在等于 "hello"
``` ```
> 注意:
> 您可以使用 `insert(_:at:)`、`insert(contentsOf:at:)`、`remove(at:)` 和 `removeSubrange(_:)` 方法在任意一个确认的并遵循 `RangeReplaceableCollection` 协议的类型里面,如上文所示是使用在 `String` 中,您也可以使用在 `Array`、`Dictionary` 和 `Set` 中。
<a name="comparing_strings"></a> <a name="comparing_strings"></a>
## 比较字符串 (Comparing Strings) ## 比较字符串 (Comparing Strings)
@ -417,7 +426,7 @@ if quotation == sameQuotation {
// "Voulez-vous un café?" 使用 LATIN SMALL LETTER E WITH ACUTE // "Voulez-vous un café?" 使用 LATIN SMALL LETTER E WITH ACUTE
let eAcuteQuestion = "Voulez-vous un caf\u{E9}?" let eAcuteQuestion = "Voulez-vous un caf\u{E9}?"
// "Voulez-vous un caf?" 使用 LATIN SMALL LETTER E and COMBINING ACUTE ACCENT // "Voulez-vous un café?" 使用 LATIN SMALL LETTER E and COMBINING ACUTE ACCENT
let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?" let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?"
if eAcuteQuestion == combinedEAcuteQuestion { if eAcuteQuestion == combinedEAcuteQuestion {
@ -472,7 +481,7 @@ let romeoAndJuliet = [
var act1SceneCount = 0 var act1SceneCount = 0
for scene in romeoAndJuliet { for scene in romeoAndJuliet {
if scene.hasPrefix("Act 1 ") { if scene.hasPrefix("Act 1 ") {
++act1SceneCount act1SceneCount += 1
} }
} }
print("There are \(act1SceneCount) scenes in Act 1") print("There are \(act1SceneCount) scenes in Act 1")
@ -486,9 +495,9 @@ var mansionCount = 0
var cellCount = 0 var cellCount = 0
for scene in romeoAndJuliet { for scene in romeoAndJuliet {
if scene.hasSuffix("Capulet's mansion") { if scene.hasSuffix("Capulet's mansion") {
++mansionCount mansionCount += 1
} else if scene.hasSuffix("Friar Lawrence's cell") { } else if scene.hasSuffix("Friar Lawrence's cell") {
++cellCount cellCount += 1
} }
} }
print("\(mansionCount) mansion scenes; \(cellCount) cell scenes") print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
@ -520,7 +529,6 @@ Swift 提供了几种不同的方式来访问字符串的 Unicode 表示形式
let dogString = "Dog‼🐶" let dogString = "Dog‼🐶"
``` ```
<a name="UTF-8_representation"></a> <a name="UTF-8_representation"></a>
### UTF-8 表示 ### UTF-8 表示

View File

@ -9,7 +9,11 @@
> 翻译+校对:[JackAlan](https://github.com/AlanMelody) > 翻译+校对:[JackAlan](https://github.com/AlanMelody)
> 2.1 > 2.1
> 校对:[shanks](http://codebuild.me) > 校对:[shanks](http://codebuild.me)
> 2.2
> 校对:[SketchK](https://github.com/SketchK) 2016-05-11
本页包含内容: 本页包含内容:
@ -87,7 +91,7 @@ var threeDoubles = [Double](count: 3, repeatedValue:0.0)
我们可以使用加法操作符(`+`)来组合两种已存在的相同类型数组。新数组的数据类型会被从两个数组的数据类型中推断出来: 我们可以使用加法操作符(`+`)来组合两种已存在的相同类型数组。新数组的数据类型会被从两个数组的数据类型中推断出来:
```swift ```swift
var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5) var anotherThreeDoubles = [Double](count: 3, repeatedValue: 2.5)
// anotherThreeDoubles 被推断为 [Double],等价于 [2.5, 2.5, 2.5] // anotherThreeDoubles 被推断为 [Double],等价于 [2.5, 2.5, 2.5]
var sixDoubles = threeDoubles + anotherThreeDoubles var sixDoubles = threeDoubles + anotherThreeDoubles
@ -406,7 +410,7 @@ for genre in favoriteGenres {
更多关于`for-in`循环的信息,参见[For 循环](./05_Control_Flow.html#for_loops)。 更多关于`for-in`循环的信息,参见[For 循环](./05_Control_Flow.html#for_loops)。
Swift 的`Set`类型没有确定的顺序,为了按照特定顺序来遍历一个`Set`中的值可以使用`sort()`方法,它将根据提供的序列返回一个有序集合. Swift 的`Set`类型没有确定的顺序,为了按照特定顺序来遍历一个`Set`中的值可以使用`sort()`方法,它将返回一个有序数组,这个数组的元素排列顺序由操作符'<'对元素进行比较的结果来确定.
```swift ```swift
for genre in favoriteGenres.sort() { for genre in favoriteGenres.sort() {

View File

@ -12,31 +12,27 @@
> 翻译:[Prayer](https://github.com/futantan) > 翻译:[Prayer](https://github.com/futantan)
> 校对:[shanks](http://codebuild.me) > 校对:[shanks](http://codebuild.me)
> 2.2
> 翻译:[LinusLing](https://github.com/linusling)
> 校对:[SketchK](https://github.com/SketchK) 2016-05-12
本页包含内容: 本页包含内容:
- [For 循环](#for_loops) - [For-In 循环](#for_in_loops)
- [While 循环](#while_loops) - [While 循环](#while_loops)
- [条件语句](#conditional_statement) - [条件语句](#conditional_statement)
- [控制转移语句Control Transfer Statements](#control_transfer_statements) - [控制转移语句Control Transfer Statements](#control_transfer_statements)
- [提前退出](#early_exit) - [提前退出](#early_exit)
- [检测API可用性](#checking_api_availability) - [检测 API 可用性](#checking_api_availability)
Swift 提供了类似 C 语言的流程控制结构,包括可以多次执行任务的`for``while`循环,基于特定条件选择执行不同代码分支的`if``guard``switch`语句,还有控制流程跳转到其他代码的`break``continue`语句。 Swift提供了多种流程控制结构,包括可以多次执行任务的`while`循环,基于特定条件选择执行不同代码分支的`if``guard``switch`语句,还有控制流程跳转到其他代码的`break``continue`语句。
除了 C 语言里面传统的 for 循环,Swift 还增加了`for-in`循环用来更简单地遍历数组array字典dictionary区间range字符串string和其他序列类型。 Swift 还增加了`for-in`循环用来更简单地遍历数组array字典dictionary区间range字符串string和其他序列类型。
Swift 的`switch`语句比 C 语言中更加强大。在 C 语言中,如果某个 case 不小心漏写了`break`,这个 case 就会贯穿至下一个 caseSwift 无需写`break`所以不会发生这种贯穿的情况。case 还可以匹配更多的类型模式包括区间匹配range matching元组tuple和特定类型的描述。`switch`的 case 语句中匹配的值可以是由 case 体内部临时的常量或者变量决定,也可以由`where`分句描述更复杂的匹配条件。 Swift 的`switch`语句比 C 语言中更加强大。在 C 语言中,如果某个 case 不小心漏写了`break`,这个 case 就会贯穿至下一个 caseSwift 无需写`break`所以不会发生这种贯穿的情况。case 还可以匹配更多的类型模式包括区间匹配range matching元组tuple和特定类型的描述。`switch`的 case 语句中匹配的值可以是由 case 体内部临时的常量或者变量决定,也可以由`where`分句描述更复杂的匹配条件。
<a name="for_loops"></a> <a name="for_in_loops"></a>
## For 循环 ## For-In 循环
Swift 提供两种`for`循环形式来按照指定的次数执行一系列语句:
* `for-in`循环对一个集合里面的每个元素执行一系列语句。
* for 循环,用来重复执行一系列语句直到达成特定条件达成,一般通过在每次循环完成后增加计数器的值来实现。
<a name="for_in"></a>
### For-In
你可以使用`for-in`循环来遍历一个集合里面的所有元素,例如由数字表示的区间、数组中的元素、字符串中的字符。 你可以使用`for-in`循环来遍历一个集合里面的所有元素,例如由数字表示的区间、数组中的元素、字符串中的字符。
@ -99,53 +95,6 @@ for (animalName, legCount) in numberOfLegs {
字典元素的遍历顺序和插入顺序可能不同,字典的内容在内部是无序的,所以遍历元素时不能保证顺序。关于数组和字典,详情参见[集合类型](./04_Collection_Types.html)。 字典元素的遍历顺序和插入顺序可能不同,字典的内容在内部是无序的,所以遍历元素时不能保证顺序。关于数组和字典,详情参见[集合类型](./04_Collection_Types.html)。
<a name="for"></a>
### For
除了`for-in`循环Swift 提供使用条件判断和递增方法的标准 C 样式`for`循环:
```swift
for var index = 0; index < 3; ++index {
print("index is \(index)")
}
// index is 0
// index is 1
// index is 2
```
下面是一般情况下这种循环方式的格式:
```swift
for initialization; condition; increment {
statements
}
```
和 C 语言中一样,分号将循环的定义分为 3 个部分不同的是Swift 不需要使用圆括号将“initialization; condition; increment”包括起来。
这个循环执行流程如下:
1. 循环首次启动时,*初始化表达式( initialization expression *被调用一次,用来初始化循环所需的所有常量和变量。
2. *条件表达式condition expression*被调用,如果表达式调用结果为`false`,循环结束,继续执行`for`循环关闭大括号(`}`)之后的代码。如果表达式调用结果为`true`,则会执行大括号内部的代码。
3. 执行所有语句之后,执行*递增表达式increment expression*。通常会增加或减少计数器的值,或者根据语句输出来修改某一个初始化的变量。当递增表达式运行完成后,重复执行第 2 步,条件表达式会再次执行。
在初始化表达式中声明的常量和变量(比如`var index = 0`)只在`for`循环的生命周期里有效。如果想在循环结束后访问`index`的值,你必须要在循环生命周期开始前声明`index`
```swift
var index: Int
for index = 0; index < 3; ++index {
print("index is \(index)")
}
// index is 0
// index is 1
// index is 2
print("The loop statements were executed \(index) times")
// 输出 "The loop statements were executed 3 times
```
注意`index`在循环结束后最终的值是`3`而不是`2`。最后一次调用递增表达式`++index`会将`index`设置为`3`,从而导致`index < 3`条件为`false`,并终止循环。
<a name="while_loops"></a> <a name="while_loops"></a>
## While 循环 ## While 循环
@ -169,7 +118,7 @@ while condition {
下面的例子来玩一个叫做蛇和梯子的小游戏,也叫做滑道和梯子: 下面的例子来玩一个叫做蛇和梯子的小游戏,也叫做滑道和梯子:
![image](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/snakesAndLadders_2x.png) ![image](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/snakesAndLadders_2x.png)
游戏的规则如下: 游戏的规则如下:
@ -201,7 +150,8 @@ var square = 0
var diceRoll = 0 var diceRoll = 0
while square < finalSquare { while square < finalSquare {
// 掷骰子 // 掷骰子
if ++diceRoll == 7 { diceRoll = 1 } diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
// 根据点数移动 // 根据点数移动
square += diceRoll square += diceRoll
if square < board.count { if square < board.count {
@ -212,11 +162,12 @@ while square < finalSquare {
print("Game over!") print("Game over!")
``` ```
本例中使用了最简单的方法来模拟掷骰子。 `diceRoll`的值并不是一个随机数,而是以`0`为初始值,之后每一次`while`循环,`diceRoll`的值使用前置自增操作符(`++i`)来自增 1 ,然后检测是否超出了最大值。`++diceRoll`调用完成_后_返回值等于`diceRoll`自增后的值。任何时候如果`diceRoll`的值等于7时,就超过了骰子的最大值,会被重置为`1`。所以`diceRoll`的取值顺序会一直是`1``2``3``4``5``6``1``2` 本例中使用了最简单的方法来模拟掷骰子。 `diceRoll`的值并不是一个随机数,而是以`0`为初始值,之后每一次`while`循环,`diceRoll`的值增加 1 ,然后检测是否超出了最大值。任何时候如果`diceRoll`的值等于 7 时,就超过了骰子的最大值,会被重置为`1`。所以`diceRoll`的取值顺序会一直是`1`。因此,`diceRoll` 所有的值只可能是 `1` `2``3``4``5``6``1``2`
掷完骰子后,玩家向前移动`diceRoll`个方格,如果玩家移动超过了第 25 个方格,这个时候游戏结束,相应地,代码会在`square`增加`board[square]`的值向前或向后移动(遇到了梯子或者蛇)之前,检测`square`的值是否小于`board``count`属性。 掷完骰子后,玩家向前移动`diceRoll`个方格,如果玩家移动超过了第 25 个方格,这个时候游戏结束,相应地,代码会在`square`增加`board[square]`的值向前或向后移动(遇到了梯子或者蛇)之前,检测`square`的值是否小于`board``count`属性。
如果没有这个检测(`square < board.count``board[square]`可能会越界访问`board`数组,导致错误。例如如果`square`等于`26` 代码会去尝试访问`board[26]`,超过数组的长度。 > 注意:
> 如果没有这个检测(`square < board.count``board[square]`可能会越界访问`board`数组,导致错误。例如如果`square`等于`26` 代码会去尝试访问`board[26]`,超过数组的长度。
当本轮`while`循环运行完毕,会再检测循环条件是否需要再运行一次循环。如果玩家移动到或者超过第 25 个方格,循环条件结果为`false`,此时游戏结束。 当本轮`while`循环运行完毕,会再检测循环条件是否需要再运行一次循环。如果玩家移动到或者超过第 25 个方格,循环条件结果为`false`,此时游戏结束。
@ -259,7 +210,8 @@ repeat {
// 顺着梯子爬上去或者顺着蛇滑下去 // 顺着梯子爬上去或者顺着蛇滑下去
square += board[square] square += board[square]
// 掷骰子 // 掷骰子
if ++diceRoll == 7 { diceRoll = 1 } diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
// 根据点数移动 // 根据点数移动
square += diceRoll square += diceRoll
} while square < finalSquare } while square < finalSquare
@ -346,7 +298,8 @@ if temperatureInFahrenheit <= 32 {
switch some value to consider { switch some value to consider {
case value 1: case value 1:
respond to value 1 respond to value 1
case value 2, value 3: case value 2,
value 3:
respond to value 2 or 3 respond to value 2 or 3
default: default:
otherwise, do something else otherwise, do something else
@ -407,7 +360,8 @@ default:
```swift ```swift
switch some value to consider { switch some value to consider {
case value 1, value 2: case value 1,
value 2:
statements statements
} }
``` ```
@ -442,7 +396,7 @@ print("There are \(naturalCount) \(countedThings).")
// 输出 "There are dozens of moons orbiting Saturn." // 输出 "There are dozens of moons orbiting Saturn."
``` ```
在上例中,`approximateCount`在一个`switch`声明中被估值。每一个`case`都与之进行比较。因为`approximateCount`落在了12100的区间所以`naturalCount`等于`"dozens of"`值,并且此后这段执行跳出了`switch`声明。 在上例中,`approximateCount`在一个`switch`声明中被估值。每一个`case`都与之进行比较。因为`approximateCount`落在了 12100 的区间,所以`naturalCount`等于`"dozens of"`值,并且此后这段执行跳出了`switch`声明。
> 注意: > 注意:
> 闭区间操作符(`...`)以及半开区间操作符(`..<`)功能被重载去返回`IntervalType`或`Range`。一个区间可以决定他是否包含特定的元素,就像当匹配一个`switch`声明的`case`一样。区间是一个连续值的集合,可以用`for-in`语句遍历它。 > 闭区间操作符(`...`)以及半开区间操作符(`..<`)功能被重载去返回`IntervalType`或`Range`。一个区间可以决定他是否包含特定的元素,就像当匹配一个`switch`声明的`case`一样。区间是一个连续值的集合,可以用`for-in`语句遍历它。
@ -471,7 +425,7 @@ default:
// 输出 "(1, 1) is inside the box" // 输出 "(1, 1) is inside the box"
``` ```
![image](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/coordinateGraphSimple_2x.png) ![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的矩形里或者在这个矩形外面。
@ -498,7 +452,7 @@ case let (x, y):
// 输出 "on the x-axis with an x value of 2" // 输出 "on the x-axis with an x value of 2"
``` ```
![image](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/coordinateGraphMedium_2x.png) ![image](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/coordinateGraphMedium_2x.png)
在上面的例子中,`switch`语句会判断某个点是否在红色的x轴上是否在黄色y轴上或者不在坐标轴上。 在上面的例子中,`switch`语句会判断某个点是否在红色的x轴上是否在黄色y轴上或者不在坐标轴上。
@ -508,8 +462,6 @@ case let (x, y):
请注意,这个`switch`语句不包含默认分支。这是因为最后一个 case ——`case let(x, y)`声明了一个可以匹配余下所有值的元组。这使得`switch`语句已经完备了,因此不需要再书写默认分支。 请注意,这个`switch`语句不包含默认分支。这是因为最后一个 case ——`case let(x, y)`声明了一个可以匹配余下所有值的元组。这使得`switch`语句已经完备了,因此不需要再书写默认分支。
在上面的例子中,`x`和`y`是常量,这是因为没有必要在其对应的 case 分支中修改它们的值。然而,它们也可以是变量——程序将会创建临时变量,并用相应的值初始化它。修改这些变量只会影响其对应的 case 分支。
<a name="where"></a> <a name="where"></a>
#### Where #### Where
@ -530,7 +482,7 @@ case let (x, y):
// 输出 "(1, -1) is on the line x == -y" // 输出 "(1, -1) is on the line x == -y"
``` ```
![image](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/coordinateGraphComplex_2x.png) ![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`上,或者不在对角线上。
@ -557,7 +509,7 @@ case let (x, y):
`continue`语句告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。就好像在说“本次循环迭代我已经执行完了”,但是并不会离开整个循环体。 `continue`语句告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。就好像在说“本次循环迭代我已经执行完了”,但是并不会离开整个循环体。
> 注意: > 注意:
> 在一个带有条件和递增的for循环体中调用`continue`语句后,迭代增量仍然会被计算求值。循环体继续像往常一样工作,仅仅只是循环体中的执行代码会被跳过。 > 在一个带有条件和递增的 for 循环体中,调用`continue`语句后,迭代增量仍然会被计算求值。循环体继续像往常一样工作,仅仅只是循环体中的执行代码会被跳过。
下面的例子把一个小写字符串中的元音字母和空格字符移除,生成了一个含义模糊的短句: 下面的例子把一个小写字符串中的元音字母和空格字符移除,生成了一个含义模糊的短句:
@ -573,7 +525,7 @@ for character in puzzleInput.characters {
} }
} }
print(puzzleOutput) print(puzzleOutput)
// 输出 "grtmndsthnklk" // 输出 "grtmndsthnklk"
``` ```
在上面的代码中,只要匹配到元音字母或者空格字符,就调用`continue`语句,使本次循环迭代结束,从新开始下次循环迭代。这种行为使`switch`匹配到元音字母和空格字符时不做处理,而不是让每一个匹配到的字符都被打印。 在上面的代码中,只要匹配到元音字母或者空格字符,就调用`continue`语句,使本次循环迭代结束,从新开始下次循环迭代。这种行为使`switch`匹配到元音字母和空格字符时不做处理,而不是让每一个匹配到的字符都被打印。
@ -668,11 +620,9 @@ print(description)
产生一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签,并且该标签后面还需带着一个冒号。下面是一个`while`循环体的语法,同样的规则适用于所有的循环体和`switch`代码块。 产生一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签,并且该标签后面还需带着一个冒号。下面是一个`while`循环体的语法,同样的规则适用于所有的循环体和`switch`代码块。
```swift > `label name`: while `condition` {
label name: while condition { > `statements`
statements > }
}
```
下面的例子是在一个带有标签的`while`循环体中调用`break`和`continue`语句,该循环体是前面章节中*蛇和梯子*的改编版本。这次,游戏增加了一条额外的规则: 下面的例子是在一个带有标签的`while`循环体中调用`break`和`continue`语句,该循环体是前面章节中*蛇和梯子*的改编版本。这次,游戏增加了一条额外的规则:
@ -682,7 +632,7 @@ label name: while condition {
游戏的棋盘和之前一样: 游戏的棋盘和之前一样:
![image](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/snakesAndLadders_2x.png) ![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`值被和之前一样的方式初始化:
@ -724,8 +674,8 @@ print("Game over!")
- 如果骰子数将会使玩家的移动超出最后的方格,那么这种移动是不合法的,玩家需要重新掷骰子。`continue gameLoop`语句结束本次`while`循环的迭代,开始下一次循环迭代。 - 如果骰子数将会使玩家的移动超出最后的方格,那么这种移动是不合法的,玩家需要重新掷骰子。`continue gameLoop`语句结束本次`while`循环的迭代,开始下一次循环迭代。
- 在剩余的所有情况中,骰子数产生的都是合法的移动。玩家向前移动骰子数个方格,然后游戏逻辑再处理玩家当前是否处于蛇头或者梯子的底部。本次循环迭代结束,控制跳转到`while`循环体的条件判断语句处,再决定是否能够继续执行下次循环迭代。 - 在剩余的所有情况中,骰子数产生的都是合法的移动。玩家向前移动骰子数个方格,然后游戏逻辑再处理玩家当前是否处于蛇头或者梯子的底部。本次循环迭代结束,控制跳转到`while`循环体的条件判断语句处,再决定是否能够继续执行下次循环迭代。
> 注意: >注意:
> 如果上述的`break`语句没有使用`gameLoop`标签,那么它将会中断`switch`代码块而不是`while`循环体。使用`gameLoop`标签清晰的表明了`break`想要中断的是哪个代码块。 如果上述的`break`语句没有使用`gameLoop`标签,那么它将会中断`switch`代码块而不是`while`循环体。使用`gameLoop`标签清晰的表明了`break`想要中断的是哪个代码块。
同时请注意,当调用`continue gameLoop`去跳转到下一次循环迭代时,这里使用`gameLoop`标签并不是严格必须的。因为在这个游戏中,只有一个循环体,所以`continue`语句会影响到哪个循环体是没有歧义的。然而,`continue`语句使用`gameLoop`标签也是没有危害的。这样做符合标签的使用规则,同时参照旁边的`break gameLoop`,能够使游戏的逻辑更加清晰和易于理解。 同时请注意,当调用`continue gameLoop`去跳转到下一次循环迭代时,这里使用`gameLoop`标签并不是严格必须的。因为在这个游戏中,只有一个循环体,所以`continue`语句会影响到哪个循环体是没有歧义的。然而,`continue`语句使用`gameLoop`标签也是没有危害的。这样做符合标签的使用规则,同时参照旁边的`break gameLoop`,能够使游戏的逻辑更加清晰和易于理解。
<a name="early_exit"></a> <a name="early_exit"></a>
@ -738,38 +688,34 @@ func greet(person: [String: String]) {
guard let name = person["name"] else { guard let name = person["name"] else {
return return
} }
print("Hello \(name)") print("Hello \(name)")
guard let location = person["location"] else { guard let location = person["location"] else {
print("I hope the weather is nice near you.") print("I hope the weather is nice near you.")
return return
} }
print("I hope the weather is nice in \(location).") print("I hope the weather is nice in \(location).")
} }
greet(["name": "John"]) greet(["name": "John"])
// prints "Hello John!" // 输出 "Hello John!"
// prints "I hope the weather is nice near you." // 输出 "I hope the weather is nice near you."
greet(["name": "Jane", "location": "Cupertino"]) greet(["name": "Jane", "location": "Cupertino"])
// prints "Hello Jane!" // 输出 "Hello Jane!"
// prints "I hope the weather is nice in Cupertino." // 输出 "I hope the weather is nice in Cupertino."
``` ```
如果`guard`语句的条件被满足,则在保护语句的封闭大括号结束后继续执行代码。任何使用了可选绑定作为条件的一部分并被分配了值的变量或常量对于剩下的保护语句出现的代码段是可用的。 如果`guard`语句的条件被满足,则在保护语句的封闭大括号结束后继续执行代码。任何使用了可选绑定作为条件的一部分并被分配了值的变量或常量对于剩下的保护语句出现的代码段是可用的。
如果条件不被满足,在`else`分支上的代码就会被执行。这个分支必须转移控制以退出`guard`语句出现的代码段。它可以用控制转移语句如`return`,`break`,`continue`或者`throw`做这件事,或者调用一个不返回的方法或函数,例如`fatalError()`。 如果条件不被满足,在`else`分支上的代码就会被执行。这个分支必须转移控制以退出`guard`语句出现的代码段。它可以用控制转移语句如`return`,`break`,`continue`或者`throw`做这件事,或者调用一个不返回的方法或函数,例如`fatalError()`。
相比于可以实现同样功能的`if`语句,按需使用`guard`语句会提升我们代码的可靠性。 相比于可以实现同样功能的`if`语句,按需使用`guard`语句会提升我们代码的可靠性。它可以使你的代码连贯的被执行而不需要将它包在`else`块中,它可以使你用于处理违反某个条件的代码紧邻于对该条件进行判断的地方。
它可以使你的代码连贯的被执行而不需要将它包在`else`块中,它可以使你处理违反要求的代码使其接近要求。
<a name="checking_api_availability"></a> <a name="checking_api_availability"></a>
## 检测 API 可用性 ## 检测 API 可用性
Swift 有检查 API 可用性的内置支持,这可以确保我们不会不小心地使用对于当前部署目标不可用的 API。 Swift 有检查 API 可用性的内置支持,这可以确保我们不会不小心地使用对于当前部署目标不可用的 API。
编译器使用 SDK 中的可用信息来验证我们的代码中使用的所有 API 在项目指定的部署目标上是否可用。如果我们尝试使用一个不可用的 APISwift 会在编译报错。 编译器使用 SDK 中的可用信息来验证我们的代码中使用的所有 API 在项目指定的部署目标上是否可用。如果我们尝试使用一个不可用的 APISwift 会在编译报错。
我们使用一个可用性条件在一个`if`或`guard`语句中去有条件的执行一段代码,这取决于我们想要使用的 API 是否在运行时是可用的。编译器使用从可用性条件语句中获取的信息去验证在代码块中调用的 API 是否都可用。 我们使用一个可用性条件在一个`if`或`guard`语句中去有条件的执行一段代码,这取决于我们想要使用的 API 是否在运行时是可用的。编译器使用从可用性条件语句中获取的信息去验证在代码块中调用的 API 是否都可用。
@ -781,14 +727,14 @@ if #available(iOS 9, OSX 10.10, *) {
} }
``` ```
以上可用性条件指定在 iOS 系统上`if`段的代码仅在 iOS 9 及更高版本的系统上执行;在 OS X在 OS X v10.10 及更高版本的系统上执行。最后一个参数,`*`,是必须写的,用于处理未来潜在的平台 以上可用性条件指定在 iOS`if`段的代码仅在 iOS 9 及更高可运行;在 OS X仅在 OS X v10.10 及更高可运行。最后一个参数,`*`,是必须的并且指定在任何其他平台上,`if`段的代码在最小可用部署目标指定项目中执行
在它的一般形式中,可用性条件获取了一系列平台名字和版本。平台名字可以是`iOS``OSX`或`watchOS`。除了特定的主板本号像 iOS 8我们可以指定较小的版本号像 iOS 8.3 以及 OS X v10.10.3。 在它普遍的形式中,可用性条件获取了平台名字和版本的清单。平台名字可以是`iOS``OSX`或`watchOS`。除了特定的主板本号像 iOS 8我们可以指定较小的版本号像iOS 8.3 以及 OS X v10.10.3。
```swift ```swift
if #available(platform name version, ..., *) { if #available(`platform name` `version`, `...`, *) {
statements to execute if the APIs are available `statements to execute if the APIs are available`
} else { } else {
fallback statements to execute if the APIs are unavailable `fallback statements to execute if the APIs are unavailable`
} }
``` ```

View File

@ -12,48 +12,60 @@
> 翻译:[DianQK](https://github.com/DianQK) > 翻译:[DianQK](https://github.com/DianQK)
> 定稿:[shanks](http://codebuild.me) > 定稿:[shanks](http://codebuild.me)
本页包含内容: > 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-12
> 3.0
> 翻译: [crayygy](https://github.com/crayygy) 2016-09-12
本页包含内容:
- [函数定义与调用Defining and Calling Functions](#Defining_and_Calling_Functions) - [函数定义与调用Defining and Calling Functions](#Defining_and_Calling_Functions)
- [函数参数与返回值Function Parameters and Return Values](#Function_Parameters_and_Return_Values) - [函数参数与返回值Function Parameters and Return Values](#Function_Parameters_and_Return_Values)
- [函数参数名称Function Parameter Names](#Function_Parameter_Names) - [函数参数标签和参数名称 (Function Argument Labels and Parameter Names) ](#Function_Argument_Labels_and_Parameter_Names)
- [函数类型Function Types](#Function_Types) - [函数类型Function Types](#Function_Types)
- [嵌套函数Nested Functions](#Nested_Functions) - [嵌套函数Nested Functions](#Nested_Functions)
*函数*是用来完成特定任务的独立的代码块。你给一个函数起一个合适的名字,用来标识函数做什么,并且当函数需要执行的时候,这个名字会被用于“调用”函数。
Swift 统一的函数语法足够灵活,可以用来表示任何函数,包括从最简单的没有参数名字的 C 风格函数,到复杂的带局部和外部参数名的 Objective-C 风格函数。参数可以提供默认值,以简化函数调用。参数也可以既当做传入参数,也当做传出参数,也就是说,一旦函数执行结束,传入的参数值可以被修改 `函数` 是一段完成特定任务的独立代码片段。你可以通过给函数命名来标识某个函数的功能,这个名字可以被用来在需要的时候"调用"这个函数来完成它的任务
Swift 中,每个函数都有一种类型,包括函数的参数值类型和返回值类型。你可以把函数类型当做任何其他普通变量类型一样处理,这样就可以更简单地把函数当做别的函数的参数也可以从其他函数中返回函数。函数的定义可以写在其他函数定义中,这样可以在嵌套函数范围内实现功能封装 Swift 统一的函数语法非常的灵活,可以用来表示任何函数,包括从最简单的没有参数名字的 C 风格函数,到复杂的带局部和外部参数名的 Objective-C 风格函数。参数可以提供默认值,以简化函数调用。参数也可以既当做传入参数,也当做传出参数,也就是说,一旦函数执行结束,传入的参数值将被修改
在 Swift 中,每个函数都有一个由函数的参数值类型和返回值类型组成的类型。你可以把函数类型当做任何其他普通变量类型一样处理,这样就可以更简单地把函数当做别的函数的参数,也可以从其他函数中返回函数。函数的定义可以写在其他函数定义中,这样可以在嵌套函数范围内实现功能封装。
<a name="Defining_and_Calling_Functions"></a> <a name="Defining_and_Calling_Functions"></a>
## 函数的定义与调用Defining and Calling Functions ## 函数的定义与调用 (Defining and Calling Functions)
当你定义一个函数时,你可以定义一个或多个有名字和类型的值,作为函数的输入(称为*参数parameters*),也可以定义某种类型的值作为函数执行结束的输出(称为*返回类型return type*)。 当你定义一个函数时,你可以定义一个或多个有名字和类型的值,作为函数的输入(称为*参数**parameters*),也可以定义某种类型的值作为函数执行结束的输出(称为 *返回* 类型,*return* type
每个函数有个*函数名*,用来描述函数执行的任务。要使用一个函数时,用函数名“调用”,并传给它匹配的输入值(称作*实参arguments*)。一个函数的实参必须与函数参数表里参数的顺序一致。 每个函数有个函数名,用来描述函数执行的任务。要使用一个函数时,用函数名“调用”这个函数,并传给它匹配的输入值(称作 *实参* *arguments*)。函数的实参必须与函数参数表里参数的顺序一致。
下面例子中的函数的名字是`sayHello(_:)`,之所以叫这个名字,是因为这个函数用一个人的名字当做输入,并返回向这个人问候的语句。为了完成这个任务,你需要定义一个输入参数——一个叫做 `personName``String` 值,和一个包含给这个人问候语的 `String` 类型的返回值:
在下面例子中的函数叫做`"sayHello(_:)"`,之所以叫这个名字,是因为这个函数用一个人的名字当做输入,并返回给这个人的问候语。为了完成这个任务,你定义一个输入参数-一个叫做 `personName``String` 值,和一个包含给这个人问候语的 `String` 类型的返回值:
```swift ```swift
func sayHello(personName: String) -> String { func greet(person: String) -> String {
let greeting = "Hello, " + personName + "!" let greeting = "Hello, " + person + "!"
return greeting return greeting
} }
``` ```
所有的这些信息汇总起来成为函数的*定义*,并以 `func` 作为前缀。指定函数返回类型时,用返回箭头 `->`(一个连字符后跟一个右尖括号)后跟返回类型的名称的方式来表示。 所有的这些信息汇总起来成为函数的*定义*,并以 `func` 作为前缀。指定函数返回类型时,用返回箭头 `->`(一个连字符后跟一个右尖括号)后跟返回类型的名称的方式来表示。
该定义描述了函数做什么,它期望接收什么和执行结束时它返回的结果是什么类型。这样的定义使得函数可以在别的地方以一种清晰的方式被调用: 该定义描述了函数的功能,它期望接收什么作为参数和执行结束时它返回的结果是什么类型。这样的定义使得函数可以在别的地方以一种清晰的方式被调用:
```swift ```swift
print(sayHello("Anna")) print(greet(person: "Anna"))
// prints "Hello, Anna!" // 打印 "Hello, Anna!"
print(sayHello("Brian")) print(greet(person: "Brian"))
// prints "Hello, Brian!" // 打印 "Hello, Brian!"
``` ```
调用 `sayHello(_:)` 函数时,在圆括号中传给它一个 `String` 类型的实参,例如 `sayHello("Anna")`。因为这个函数返回一个 `String` 类型的值,`sayHello` 可以被包含在 `print(_:separator:terminator:)` 的调用中,用来输出这个函数的返回值,正如上面所示 调用 `sayHello(_:)` 函数时,在圆括号中传给它一个 `String` 类型的实参,例如 `sayHello("Anna")`正如上面所示,因为这个函数返回一个 `String` 类型的值,所以`sayHello` 可以被包含在 `print(_:separator:terminator:)` 的调用中,用来输出这个函数的返回值。
>注意
`print(_:separator:terminator:)` 函数的第一个参数并没有设置一个标签,而其他的参数因为已经有了默认值,因此是可选的。关于这些函数语法上的变化详见下方关于 函数参数标签和参数名 以及 默认参数值。
`sayHello(_:)` 的函数体中,先定义了一个新的名为 `greeting``String` 常量,同时,把对 `personName` 的问候消息赋值给了 `greeting` 。然后用 `return` 关键字把这个问候返回出去。一旦 `return greeting` 被调用,该函数结束它的执行并返回 `greeting` 的当前值。 `sayHello(_:)` 的函数体中,先定义了一个新的名为 `greeting``String` 常量,同时,把对 `personName` 的问候消息赋值给了 `greeting` 。然后用 `return` 关键字把这个问候返回出去。一旦 `return greeting` 被调用,该函数结束它的执行并返回 `greeting` 的当前值。
@ -62,100 +74,101 @@ print(sayHello("Brian"))
为了简化这个函数的定义,可以将问候消息的创建和返回写成一句: 为了简化这个函数的定义,可以将问候消息的创建和返回写成一句:
```swift ```swift
func sayHelloAgain(personName: String) -> String { func greetAgain(person: String) -> String {
return "Hello again, " + personName + "!" return "Hello again, " + person + "!"
} }
print(sayHelloAgain("Anna")) print(greetAgain(person: "Anna"))
// prints "Hello again, Anna!" // 打印 "Hello again, Anna!"
``` ```
<a name="Function_Parameters_and_Return_Values"></a> <a name="Function_Parameters_and_Return_Values"></a>
## 函数参数与返回值Function Parameters and Return Values ## 函数参数与返回值 (Function Parameters and Return Values)
函数参数与返回值在 Swift 中极为灵活。你可以定义任何类型的函数,包括从只带一个未名参数的简单函数到复杂的带有表达性参数名和不同参数选项的复杂函数。 函数参数与返回值在 Swift 中非常的灵活。你可以定义任何类型的函数,包括从只带一个未名参数的简单函数到复杂的带有表达性参数名和不同参数选项的复杂函数。
### 无参函数Functions Without Parameters <a name="functions_without_parameters"></a>
### 无参数函数 (Functions Without Parameters)
函数可以没有参数。下面这个函数就是一个无参函数,当被调用时,它返回固定的 `String` 消息: 函数可以没有参数。下面这个函数就是一个无参函数,当被调用时,它返回固定的 `String` 消息:
```swift ```swift
func sayHelloWorld() -> String { func sayHelloWorld() -> String {
return "hello, world" return "hello, world"
} }
print(sayHelloWorld()) print(sayHelloWorld())
// prints "hello, world" // 打印 "hello, world"
``` ```
尽管这个函数没有参数,但是定义中在函数名后还是需要一对圆括号。当被调用时,也需要在函数名后写一对圆括号。 尽管这个函数没有参数,但是定义中在函数名后还是需要一对圆括号。当被调用时,也需要在函数名后写一对圆括号。
<a name="functions_with_multiple_parameters"></a>
### 多参数函数 (Functions With Multiple Parameters) ### 多参数函数 (Functions With Multiple Parameters)
函数可以有多种输入参数,这些参数被包含在函数的括号之中,以逗号分隔。 函数可以有多种输入参数,这些参数被包含在函数的括号之中,以逗号分隔。
这个函数用一个人名和是否已经打过招呼作为输入,并返回对这个人的适当问候语: 下面这个函数用一个人名和是否已经打过招呼作为输入,并返回对这个人的适当问候语:
```swift ```swift
func sayHello(personName: String, alreadyGreeted: Bool) -> String { func greet(person: String, alreadyGreeted: Bool) -> String {
if alreadyGreeted { if alreadyGreeted {
return sayHelloAgain(personName) return greetAgain(person: person)
} else { } else {
return sayHello(personName) return greet(person: person)
} }
} }
print(sayHello("Tim", alreadyGreeted: true)) print(greet(person: "Tim", alreadyGreeted: true))
// prints "Hello again, Tim!" // 打印 "Hello again, Tim!"
``` ```
你通过在括号内传递一个`String`参数值和一个标识为`alreadyGreeted``Bool`值,使用逗号分隔来调用`sayHello(_:alreadyGreeted:)`函数。 可以通过在括号内使用逗号分隔来传递一个`String`参数值和一个标识为`alreadyGreeted``Bool`值,来调用`sayHello(_:alreadyGreeted:)`函数。注意这个函数和上面`greet(person:)`是不同的。虽然它们都有着同样的名字`greet`,但是`greet(person:alreadyGreeted:)`函数需要两个参数,而`greet(person:)`只需要一个参数。
当调用超过一个参数的函数时,第一个参数后的参数根据其对应的参数名称标记,函数参数命名在[函数参数名称Function Parameter Names](#Function_Parameter_Names)有更详细的描述。
<a name="functions_without_return_values"></a> <a name="functions_without_return_values"></a>
### 无返回值函数Functions Without Return Values ### 无返回值函数 (Functions Without Return Values)
函数可以没有返回值。下面是 `sayHello(_:)` 函数的另一个版本,叫 `sayGoodbye(_:)`,这个函数直接输出 `String` 值,而不是返回它: 函数可以没有返回值。下面是 `sayHello(_:)` 函数的另一个版本,叫 `sayGoodbye(_:)`,这个函数直接打印一个`String`值,而不是返回它:
```swift ```swift
func sayGoodbye(personName: String) { func greet(person: String) {
print("Goodbye, \(personName)!") print("Hello, \(person)!")
} }
sayGoodbye("Dave") greet(person: "Dave")
// prints "Goodbye, Dave!" // 打印 "Hello, Dave!"
``` ```
因为这个函数不需要返回值,所以这个函数的定义中没有返回箭头(->)和返回类型。 因为这个函数不需要返回值,所以这个函数的定义中没有返回箭头(->)和返回类型。
> 注意 >注意
> 严格上来说,虽然没有返回值被定义,`sayGoodbye(_:)` 函数依然返回了值。没有定义返回类型的函数会返回特殊的值,叫 `Void`。它其实是一个空的元组tuple没有任何元素可以写成`()` 严格上来说,虽然没有返回值被定义,`sayGoodbye(_:)` 函数依然返回了值。没有定义返回类型的函数会返回一个特殊的`Void`。它其实是一个空的元组tuple没有任何元素可以写成()。
被调用时,一个函数的返回值可以被忽略: 被调用时,一个函数的返回值可以被忽略:
```swift ```swift
func printAndCount(stringToPrint: String) -> Int { func printAndCount(string: String) -> Int {
print(stringToPrint) print(string)
return stringToPrint.characters.count return string.characters.count
} }
func printWithoutCounting(stringToPrint: String) { func printWithoutCounting(string: String) {
printAndCount(stringToPrint) let _ = printAndCount(string: string)
} }
printAndCount("hello, world") printAndCount(string: "hello, world")
// prints "hello, world" and returns a value of 12 // 打印 "hello, world" 并且返回值 12
printWithoutCounting("hello, world") printWithoutCounting(string: "hello, world")
// prints "hello, world" but does not return a value // 打印 "hello, world" 但是没有返回任何值
``` ```
第一个函数 `printAndCount(_:)`,输出一个字符串并返回 `Int` 类型的字符数。第二个函数 `printWithoutCounting`调用了第一个函数,但是忽略了它的返回值。当第二个函数被调用时,消息依然会由第一个函数输出,但是返回值不会被用到。 第一个函数 `printAndCount(_:)`,输出一个字符串并返回 `Int` 类型的字符数。第二个函数 `printWithoutCounting`调用了第一个函数,但是忽略了它的返回值。当第二个函数被调用时,消息依然会由第一个函数输出,但是返回值不会被用到。
> 注意 >注意
> 返回值可以被忽略但定义了有返回值的函数必须返回一个值如果在函数定义底部没有返回任何值将导致编译错误compile-time error 返回值可以被忽略,但定义了有返回值的函数必须返回一个值,如果在函数定义底部没有返回任何值,将导致编译错误compile-time error
<a name="functions_with_multiple_return_values"></a> <a name="functions_with_multiple_return_values"></a>
### 多重返回值函数Functions with Multiple Return Values ### 多重返回值函数 (Functions with Multiple Return Values)
你可以用元组tuple类型让多个值作为一个复合值从函数中返回。 你可以用元组tuple类型让多个值作为一个复合值从函数中返回。
面的这个例子中,定义了一个名为`minMax(_:)`的函数,作用是在一个`Int`数组中找出最小值与最大值。 例中定义了一个名为 `minMax(_:)` 的函数,作用是在一个 `Int` 类型的数组中找出最小值与最大值。
```swift ```swift
func minMax(array: [Int]) -> (min: Int, max: Int) { func minMax(array: [Int]) -> (min: Int, max: Int) {
@ -172,31 +185,33 @@ func minMax(array: [Int]) -> (min: Int, max: Int) {
} }
``` ```
`minMax(_:)`函数返回一个包含两个`Int`值的元组,这些值被标记为`min``max`,以便查询函数的返回值时可以通过名字访问它们。 `minMax(_:)` 函数返回一个包含两个 `Int` 值的元组,这些值被标记为 `min``max` ,以便查询函数的返回值时可以通过名字访问它们。
`minMax(_:)`的函数体中,在开始的时候设置两个工作变量`currentMin``currentMax`的值为数组中的第一个数。然后函数会遍历数组中剩余的值并检查该值是否比`currentMin``currentMax`更小或更大。最后数组中的最小值与最大值作为一个包含两个`Int`值的元组返回。 `minMax(_:)` 的函数体中,在开始的时候设置两个工作变量 `currentMin``currentMax` 的值为数组中的第一个数。然后函数会遍历数组中剩余的值并检查该值是否比 `currentMin``currentMax` 更小或更大。最后数组中的最小值与最大值作为一个包含两个 `Int` 值的元组返回。
因为元组的成员值已被命名,因此可以通过语法来检索找到的最小值与最大值: 因为元组的成员值已被命名,因此可以通过 `.` 语法来检索找到的最小值与最大值:
```swift ```swift
let bounds = minMax([8, -6, 2, 109, 3, 71]) let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("min is \(bounds.min) and max is \(bounds.max)") print("min is \(bounds.min) and max is \(bounds.max)")
// prints "min is -6 and max is 109" // 打印 "min is -6 and max is 109"
``` ```
需要注意的是,元组的成员不需要在元组从函数中返回时命名,因为它们的名字已经在函数返回类型中指定了。 需要注意的是,元组的成员不需要在元组从函数中返回时命名,因为它们的名字已经在函数返回类型中指定了。
<a name="optional_tuple_return_types"></a> <a name="optional_tuple_return_types"></a>
###可选元组返回类型(Optional Tuple Return Types) ### 可选元组返回类型 (Optional Tuple Return Types)
如果函数返回的元组类型有可能整个元组都“没有值”,你可以使用*可选的(Optional* 元组返回类型反映整个元组可以是`nil`的事实。你可以通过在元组类型的右括号后放置一个问号来定义一个可选元组,例如`(Int, Int)?``(String, Int, Bool)?` 如果函数返回的元组类型有可能整个元组都“没有值”,你可以使用可选的( `optional` 元组返回类型反映整个元组可以是`nil`的事实。你可以通过在元组类型的右括号后放置一个问号来定义一个可选元组,例如 `(Int, Int)?``(String, Int, Bool)?`
> 注意 >注意
> 可选元组类型如`(Int, Int)?`与元组包含可选类型如`(Int?, Int?)`是不同的.可选的元组类型,整个元组是可选的,而不只是元组中的每个元素值。 可选元组类型如 `(Int, Int)?` 与元组包含可选类型如 `(Int?, Int?)` 是不同的.可选的元组类型,整个元组是可选的,而不只是元组中的每个元素值。
前面的`minMax(_:)`函数返回了一个包含两个`Int`值的元组。但是函数不会对传入的数组执行任何安全检查,如果`array`参数是一个空数组,如上定义的`minMax(_:)`在试图访问`array[0]`时会触发一个运行时错误。
为了安全地处理这个“空数组”问题,将`minMax(_:)`函数改写为使用可选元组返回类型,并且当数组为空时返回`nil` 前面的 `minMax(_:)` 函数返回了一个包含两个 `Int` 值的元组。但是函数不会对传入的数组执行任何安全检查,如果 `array` 参数是一个空数组,如上定义的 `minMax(_:)` 在试图访问 `array[0]` 时会触发一个运行时错误(runtime error)。
为了安全地处理这个“空数组”问题,将 `minMax(_:)` 函数改写为使用可选元组返回类型,并且当数组为空时返回 `nil`
```swift ```swift
func minMax(array: [Int]) -> (min: Int, max: Int)? { func minMax(array: [Int]) -> (min: Int, max: Int)? {
@ -214,105 +229,97 @@ func minMax(array: [Int]) -> (min: Int, max: Int)? {
} }
``` ```
你可以使用可选绑定来检查`minMax(_:)`函数返回的是一个实际的元组值还是`nil` 你可以使用可选绑定来检查 `minMax(_:)` 函数返回的是一个存在的元组值还是 `nil`
```swift ```swift
if let bounds = minMax([8, -6, 2, 109, 3, 71]) { if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
print("min is \(bounds.min) and max is \(bounds.max)") print("min is \(bounds.min) and max is \(bounds.max)")
} }
// prints "min is -6 and max is 109" // 打印 "min is -6 and max is 109"
``` ```
<a name="Function_Parameter_Names"></a>
## 函数参数名称Function Parameter Names
函数参数都有一个*外部参数名external parameter name*和一个*局部参数名local parameter name*。外部参数名用于在函数调用时标注传递给函数的参数,局部参数名在函数的实现内部使用。 <a name="Function_Argument_Labels_and_Parameter_Names"></a>
## 函数参数标签和参数名称 (Function Argument Labels and Parameter Names)
每个函数参数都有一个参数标签( argument label )以及一个参数名称( parameter name )。参数标签在调用函数的时候使用;调用的时候需要将函数的参数标签写在对应的参数前面。参数名称在函数的实现中使用。默认情况下,函数参数使用参数名称来作为它们的参数标签。
```swift ```swift
func someFunction(firstParameterName: Int, secondParameterName: Int) { func someFunction(firstParameterName: Int, secondParameterName: Int) {
// function body goes here // In the function body, firstParameterName and secondParameterName
// firstParameterName and secondParameterName refer to // refer to the argument values for the first and second parameters.
// the argument values for the first and second parameters }
someFunction(firstParameterName: 1, secondParameterName: 2)
```
所有的参数都必须有一个独一无二的名字。虽然多个参数拥有同样的参数标签是可能的,但是一个唯一的函数标签能够使你的代码更具可读性。
<a name="specifying_argument_labels"></a>
### 参数标签 (Specifying Argument Labels)
你可以在函数名称前指定它的参数标签,中间以空格分隔:
```swift
func someFunction(argumentLabel parameterName: Int) {
// In the function body, parameterName refers to the argument value
// for that parameter.
}
```
这个版本的 `greet(person:)` 函数,接收一个人的名字和他的家乡,并且返回一句问候:
```swift
func greet(person: String, from hometown: String) -> String {
return "Hello \(person)! Glad you could visit from \(hometown)."
}
print(greet(person: "Bill", from: "Cupertino"))
// Prints "Hello Bill! Glad you could visit from Cupertino."
```
参数标签的使用能够让一个函数在调用时更有表达力,更类似自然语言,并且仍保持了函数内部的可读性以及清晰的意图。
<a name="omitting_argument_labels"></a>
### 忽略参数标签(Omitting Argument Labels)
如果你不希望为某个参数添加一个标签,可以使用一个下划线(`_`)来代替一个明确的参数标签。
```swift
func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
// In the function body, firstParameterName and secondParameterName
// refer to the argument values for the first and second parameters.
} }
someFunction(1, secondParameterName: 2) someFunction(1, secondParameterName: 2)
``` ```
一般情况下,第一个参数省略其外部参数名,第二个以及随后的参数使用其局部参数名作为外部参数名。所有参数必须有独一无二的局部参数名。尽管多个参数可以有相同的外部参数名,但不同的外部参数名能让你的代码更有可读性 如果一个参数有一个标签,那么在调用的时候必须使用标签来标记这个参数
<a name="specifying_external_parameter_names"></a>
### 指定外部参数名Specifying External Parameter Names
你可以在局部参数名前指定外部参数名,中间以空格分隔:
```swift
func someFunction(externalParameterName localParameterName: Int) {
// function body goes here, and can use localParameterName
// to refer to the argument value for that parameter
}
```
> 注意
> 如果你提供了外部参数名,那么函数在被调用时,必须使用外部参数名。
这个版本的`sayHello(_:)`函数,接收两个人的名字,会同时返回对他俩的问候:
```swift
func sayHello(to person: String, and anotherPerson: String) -> String {
return "Hello \(person) and \(anotherPerson)!"
}
print(sayHello(to: "Bill", and: "Ted"))
// prints "Hello Bill and Ted!"
```
为每个参数指定外部参数名后,在你调用`sayHello(to:and:)`函数时两个外部参数名都必须写出来。
使用外部函数名可以使函数以一种更富有表达性的类似句子的方式调用,并使函数体意图清晰,更具可读性。
### 忽略外部参数名Omitting External Parameter Names
如果你不想为第二个及后续的参数设置外部参数名,用一个下划线(`_`)代替一个明确的参数名。
```swift
func someFunction(firstParameterName: Int, _ secondParameterName: Int) {
// function body goes here
// firstParameterName and secondParameterName refer to
// the argument values for the first and second parameters
}
someFunction(1, 2)
```
> 注意
> 因为第一个参数默认忽略其外部参数名称,显式地写下划线是多余的。
<a name="default_parameter_values"></a> <a name="default_parameter_values"></a>
### 默认参数值Default Parameter Values ### 默认参数值 (Default Parameter Values)
你可以在函数体中为每个参数定义`默认值Deafult Values`。当默认值被定义后,调用这个函数时可以忽略这个参数。 你可以在函数体中通过给参数赋值来为任意一个参数定义默认值Deafult Values。当默认值被定义后调用这个函数时可以忽略这个参数。
```swift ```swift
func someFunction(parameterWithDefault: Int = 12) { func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
// function body goes here // If you omit the second argument when calling this function, then
// if no arguments are passed to the function call, // the value of parameterWithDefault is 12 inside the function body.
// value of parameterWithDefault is 12
} }
someFunction(6) // parameterWithDefault is 6 someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault is 6
someFunction() // parameterWithDefault is 12 someFunction(parameterWithoutDefault: 4) // parameterWithDefault is 12
``` ```
> 注意 将不带有默认值的参数放在函数参数列表的最前。一般来说,没有默认值的参数更加的重要,将不带默认值的参数放在最前保证在函数调用时,非默认参数的顺序是一致的,同时也使得相同的函数在不同情况下调用时显得更为清晰。
> 将带有默认值的参数放在函数参数列表的最后。这样可以保证在函数调用时,非默认参数的顺序是一致的,同时使得相同的函数在不同情况下调用时显得更为清晰。
<a name="variadic_parameters"></a> <a name="variadic_parameters"></a>
### 可变参数Variadic Parameters ### 可变参数 (Variadic Parameters)
一个`可变参数variadic parameter`可以接受零个或多个值。函数调用时,你可以用可变参数来指定函数参数可以被传入不确定数量的输入值。通过在变量类型名后面加入`...`的方式来定义可变参数。 一个可变参数variadic parameter可以接受零个或多个值。函数调用时你可以用可变参数来指定函数参数可以被传入不确定数量的输入值。通过在变量类型名后面加入`...`的方式来定义可变参数。
可变参数的传入值在函数体中变为此类型的一个数组。例如,一个叫做 `numbers``Double...` 型可变参数,在函数体内可以当做一个叫 `numbers``[Double]` 型的数组常量。 可变参数的传入值在函数体中变为此类型的一个数组。例如,一个叫做 `numbers``Double...` 型可变参数,在函数体内可以当做一个叫 `numbers``[Double]` 型的数组常量。
下面的这个函数用来计算一组任意长度数字的`算术平均数arithmetic mean` 下面的这个函数用来计算一组任意长度数字的 *算术平均数arithmetic mean)*
```swift ```swift
func arithmeticMean(numbers: Double...) -> Double { func arithmeticMean(_ numbers: Double...) -> Double {
var total: Double = 0 var total: Double = 0
for number in numbers { for number in numbers {
total += number total += number
@ -325,74 +332,33 @@ arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers // returns 10.0, which is the arithmetic mean of these three numbers
``` ```
> 注意 >注意
> 一个函数最多只能有一个可变参数。 一个函数最多只能有一个可变参数。
如果函数有一个或多个带默认值的参数,而且还有一个可变参数,那么把可变参数放在参数表的最后。
<a name="constant_and_variable_parameters"></a>
### 常量参数和变量参数Constant and Variable Parameters
函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误。这意味着你不能错误地更改参数值。
但是,有时候,如果函数中有传入参数的变量值副本将是很有用的。你可以通过指定一个或多个参数为变量参数,从而避免自己在函数中定义新的变量。变量参数不是常量,你可以在函数中把它当做新的可修改副本来使用。
通过在参数名前加关键字 `var` 来定义变量参数:
```swift
func alignRight(var string: String, totalLength: Int, pad: Character) -> String {
let amountToPad = totalLength - string.characters.count
if amountToPad < 1 {
return string
}
let padString = String(pad)
for _ in 1...amountToPad {
string = padString + string
}
return string
}
let originalString = "hello"
let paddedString = alignRight(originalString, totalLength: 10, pad: "-")
// paddedString is equal to "-----hello"
// originalString is still equal to "hello"
```
这个例子中定义了一个叫做 `alignRight(_:totalLength:pad:)` 的新函数,用来将输入的字符串对齐到更长的输出字符串的右边缘。左侧空余的地方用指定的填充字符填充。这个例子中,字符串`"hello"`被转换成了`"-----hello"`
`alignRight(_:totalLength:pad:)` 函数将输入参数 `string` 定义为变量参数。这意味着 `string` 现在可以作为一个局部变量,被传入的字符串值初始化,并且可以在函数体中进行操作。
函数首先计算出有多少字符需要被添加到`string`的左边,从而将其在整个字符串中右对齐。这个值存储在一个称为`amountToPad`的本地常量。如果不需要填充(也就是说,如果`amountToPad`小于1该函数简单地返回没有任何填充的输入值`string`
否则,该函数用`pad`字符创建一个叫做`padString`的临时`String`常量,并将`amountToPad``padString`添加到现有字符串的左边。(一个`String`值不能被添加到一个`Character`值上,所以`padString`常量用于确保`+`操作符两侧都是`String`值)。
> 注意
> 对变量参数所进行的修改在函数调用结束后便消失了,并且对于函数体外是不可见的。变量参数仅仅存在于函数调用的生命周期中。
<a name="in_out_parameters"></a> <a name="in_out_parameters"></a>
### 输入输出参数In-Out Parameters ### 输入输出参数In-Out Parameters
变量参数,正如上面所述,仅仅能在函数体内被更改。如果你想要一个函数可以修改参数的值并且想要在这些修改在函数调用结束后仍然存在那么就应该把这个参数定义为输入输出参数In-Out Parameters 函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误(compile-time error)。这意味着你不能错误地更改参数值。如果你想要一个函数可以修改参数的值并且想要在这些修改在函数调用结束后仍然存在那么就应该把这个参数定义为输入输出参数In-Out Parameters
定义一个输入输出参数时,在参数定义前加 `inout` 关键字。一个输入输出参数有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。想获取更多的关于输入输出参数的细节和相关的编译器优化,请查看[输入输出参数](../chapter3/05_Declarations.html#function_declaration)一节。 定义一个输入输出参数时,在参数定义前加 `inout` 关键字。一个`输入输出参数`有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。想获取更多的关于输入输出参数的细节和相关的编译器优化,请查看`输入输出参数`一节。
<!--上面的链接对应的内容没有更新翻译--> 你只能传递变量给输入输出参数。你不能传入常量或者字面量literal value因为这些量是不能被修改的。当传入的参数作为输入输出参数时需要在参数名前加 `&` 符,表示这个值可以被函数修改。
你只能传递变量给输入输出参数。你不能传入常量或者字面量literal value因为这些量是不能被修改的。当传入的参数作为输入输出参数时需要在参数名前加`&`符,表示这个值可以被函数修改。 >注意
输入输出参数不能有默认值,而且可变参数不能用 `inout` 标记。
> 注意
> 输入输出参数不能有默认值,而且可变参数不能用 `inout` 标记。如果你用 `inout` 标记一个参数,这个参数不能被 `var` 或者 `let` 标记。
面是例子`swapTwoInts(_:_:)` 函数有两个分别叫做 `a``b` 的输入输出参数: 例中`swapTwoInts(_:_:)` 函数有两个分别叫做 `a``b` 的输入输出参数:
```swift ```swift
func swapTwoInts(inout a: Int, inout _ b: Int) { func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a let temporaryA = a
a = b a = b
b = temporaryA b = temporaryA
} }
``` ```
这个 `swapTwoInts(_:_:)` 函数简单地交换 `a``b` 的值。该函数先将 `a` 的值存到一个临时常量 `temporaryA` 中,然后将 `b` 的值赋给 `a`,最后将 `temporaryA` 赋值给 `b` `swapTwoInts(_:_:)` 函数简单地交换 `a``b` 的值。该函数先将 `a` 的值存到一个临时常量 `temporaryA` 中,然后将 `b` 的值赋给 `a`,最后将 `temporaryA` 赋值给 `b`
你可以用两个 `Int` 型的变量来调用 `swapTwoInts(_:_:)`。需要注意的是,`someInt``anotherInt` 在传入 `swapTwoInts(_:_:)` 函数前,都加了 `&` 的前缀: 你可以用两个 `Int` 型的变量来调用 `swapTwoInts(_:_:)`。需要注意的是,`someInt``anotherInt` 在传入 `swapTwoInts(_:_:)` 函数前,都加了 `&` 的前缀:
@ -401,31 +367,32 @@ var someInt = 3
var anotherInt = 107 var anotherInt = 107
swapTwoInts(&someInt, &anotherInt) swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)") print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// prints "someInt is now 107, and anotherInt is now 3" // Prints "someInt is now 107, and anotherInt is now 3"
``` ```
从上面这个例子中,我们可以看到 `someInt``anotherInt` 的原始值在 `swapTwoInts(_:_:)` 函数中被修改,尽管它们的定义在函数体外。 从上面这个例子中,我们可以看到 `someInt``anotherInt` 的原始值在 `swapTwoInts(_:_:)` 函数中被修改,尽管它们的定义在函数体外。
> 注意 >注意
> 输入输出参数和返回值是不一样的。上面的 `swapTwoInts` 函数并没有定义任何返回值,但仍然修改了 `someInt` 和 `anotherInt` 的值。输入输出参数是函数对函数体外产生影响的另一种方式。 输入输出参数和返回值是不一样的。上面的 `swapTwoInts` 函数并没有定义任何返回值,但仍然修改了 `someInt``anotherInt` 的值。输入输出参数是函数对函数体外产生影响的另一种方式。
<a name="Function_Types"></a> <a name="Function_Types"></a>
## 函数类型Function Types ## 函数类型 (Function Types)
每个函数都有种特定的函数类型由函数的参数类型和返回类型组成。 每个函数都有种特定的`函数类型`,函数的类型由函数的参数类型和返回类型组成。
例如: 例如:
```swift ```swift
func addTwoInts(a: Int, _ b: Int) -> Int { func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b return a + b
} }
func multiplyTwoInts(a: Int, _ b: Int) -> Int { func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
return a * b return a * b
} }
``` ```
这个例子中定义了两个简单的数学函数:`addTwoInts``multiplyTwoInts`。这两个函数都接受两个 `Int` 值, 返回一个`Int`值。 这个例子中定义了两个简单的数学函数:`addTwoInts``multiplyTwoInts`。这两个函数都接受两个 `Int` 值, 返回一个 `Int` 值。
这两个函数的类型是 `(Int, Int) -> Int`,可以解读为“这个函数类型有两个 `Int` 型的参数并返回一个 `Int` 型的值。”。 这两个函数的类型是 `(Int, Int) -> Int`,可以解读为“这个函数类型有两个 `Int` 型的参数并返回一个 `Int` 型的值。”。
@ -440,7 +407,7 @@ func printHelloWorld() {
这个函数的类型是:`() -> Void`,或者叫“没有参数,并返回 `Void` 类型的函数”。 这个函数的类型是:`() -> Void`,或者叫“没有参数,并返回 `Void` 类型的函数”。
<a name="using_function_types"></a> <a name="using_function_types"></a>
### 使用函数类型Using Function Types ### 使用函数类型 (Using Function Types)
在 Swift 中,使用函数类型就像使用其他类型一样。例如,你可以定义一个类型为函数的常量或变量,并将适当的函数赋值给它: 在 Swift 中,使用函数类型就像使用其他类型一样。例如,你可以定义一个类型为函数的常量或变量,并将适当的函数赋值给它:
@ -448,76 +415,77 @@ func printHelloWorld() {
var mathFunction: (Int, Int) -> Int = addTwoInts var mathFunction: (Int, Int) -> Int = addTwoInts
``` ```
可以解读为: 段代码可以解读为:
定义一个叫做 `mathFunction` 的变量,类型是‘一个有两个 `Int` 型的参数并返回一个 `Int` 型的值的函数’,并让这个新变量指向 `addTwoInts` 函数”。 定义一个叫做 `mathFunction` 的变量,类型是‘一个有两个 `Int` 型的参数并返回一个 `Int` 型的值的函数’,并让这个新变量指向 `addTwoInts` 函数”。
`addTwoInts``mathFunction` 有同样的类型,所以这个赋值过程在 Swift 类型检查中是允许的。 `addTwoInts``mathFunction` 有同样的类型,所以这个赋值过程在 Swift 类型检查(type-check)中是允许的。
现在,你可以用 `mathFunction` 来调用被赋值的函数了: 现在,你可以用 `mathFunction` 来调用被赋值的函数了:
```swift ```swift
print("Result: \(mathFunction(2, 3))") print("Result: \(mathFunction(2, 3))")
// prints "Result: 5" // Prints "Result: 5"
``` ```
有相同匹配类型的不同函数可以被赋值给同一个变量,就像非函数类型的变量一样: 有相同匹配类型的不同函数可以被赋值给同一个变量,就像非函数类型的变量一样:
```swift ```swift
mathFunction = multiplyTwoInts mathFunction = multiplyTwoInts
print("Result: \(mathFunction(2, 3))") print("Result: \(mathFunction(2, 3))")
// prints "Result: 6" // Prints "Result: 6"
``` ```
就像其他类型一样,当赋值一个函数给常量或变量时,你可以让 Swift 来推断其函数类型: 就像其他类型一样,当赋值一个函数给常量或变量时,你可以让 Swift 来推断其函数类型:
```swift ```swift
let anotherMathFunction = addTwoInts let anotherMathFunction = addTwoInts
// anotherMathFunction is inferred to be of type (Int, Int) -> Int // anotherMathFunction 被推断为 (Int, Int) -> Int 类型
``` ```
<a name="function_types_as_parameter_types"></a> <a name="function_types_as_parameter_types"></a>
### 函数类型作为参数类型Function Types as Parameter Types ### 函数类型作为参数类型 (Function Types as Parameter Types)
你可以用`(Int, Int) -> Int`这样的函数类型作为另一个函数的参数类型。这样你可以将函数的一部分实现留给函数的调用者来提供。 你可以用 `(Int, Int) -> Int` 这样的函数类型作为另一个函数的参数类型。这样你可以将函数的一部分实现留给函数的调用者来提供。
下面是另一个例子,正如上面的函数一样,同样是输出某种数学运算结果: 下面是另一个例子,正如上面的函数一样,同样是输出某种数学运算结果:
```swift ```swift
func printMathResult(mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) { func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
print("Result: \(mathFunction(a, b))") print("Result: \(mathFunction(a, b))")
} }
printMathResult(addTwoInts, 3, 5) printMathResult(addTwoInts, 3, 5)
// prints "Result: 8" // 打印 "Result: 8"
``` ```
这个例子定义了 `printMathResult(_:_:_:)` 函数,它有三个参数:第一个参数叫 `mathFunction`,类型是`(Int, Int) -> Int`,你可以传入任何这种类型的函数;第二个和第三个参数叫 `a``b`,它们的类型都是 `Int`,这两个值作为已给出的函数的输入值。 这个例子定义了 `printMathResult(_:_:_:)` 函数,它有三个参数:第一个参数叫 `mathFunction`,类型是 `(Int, Int) -> Int`,你可以传入任何这种类型的函数;第二个和第三个参数叫 `a``b`,它们的类型都是 `Int`,这两个值作为已给出的函数的输入值。
`printMathResult(_:_:_:)` 被调用时,它被传入 `addTwoInts` 函数和整数`3``5`。它用传入`3``5`调用 `addTwoInts`,并输出结果:`8` `printMathResult(_:_:_:)` 被调用时,它被传入 `addTwoInts` 函数和整数 `3``5`。它用传入 `3``5` 调用 `addTwoInts`,并输出结果:`8`
`printMathResult(_:_:_:)` 函数的作用就是输出另一个适当类型的数学函数的调用结果。它不关心传入函数是如何实现的,只关心这个传入的函数类型是正确的。这使得 `printMathResult(_:_:_:)` 能以一种类型安全type-safe的方式将一部分功能转给调用者实现。 `printMathResult(_:_:_:)` 函数的作用就是输出另一个适当类型的数学函数的调用结果。它不关心传入函数是如何实现的,只关心传入的函数是不是一个正确的类型。这使得 `printMathResult(_:_:_:)` 能以一种类型安全type-safe的方式将一部分功能转给调用者实现。
<a name="function_types_as_return_types"></a> <a name="function_types_as_return_types"></a>
### 函数类型作为返回类型Function Types as Return Types ### 函数类型作为返回类型 (Function Types as Return Types)
你可以用函数类型作为另一个函数的返回类型。你需要做的是在返回箭头(`->`)后写一个完整的函数类型。 你可以用函数类型作为另一个函数的返回类型。你需要做的是在返回箭头(->)后写一个完整的函数类型。
下面的这个例子中定义了两个简单函数,分别是 `stepForward``stepBackward``stepForward` 函数返回一个比输入值大的值。`stepBackward` 函数返回一个比输入值小的值。这两个函数的类型都是 `(Int) -> Int` 下面的这个例子中定义了两个简单函数,分别是 `stepForward` `stepBackward``stepForward`函数返回一个比输入值大 `1` 的值。`stepBackward` 函数返回一个比输入值小 `1` 的值。这两个函数的类型都是 `(Int) -> Int`
```swift ```swift
func stepForward(input: Int) -> Int { func stepForward(_ input: Int) -> Int {
return input + 1 return input + 1
} }
func stepBackward(input: Int) -> Int { func stepBackward(_ input: Int) -> Int {
return input - 1 return input - 1
} }
``` ```
下面这个叫做 `chooseStepFunction(_:)` 的函数,它的返回类型是 `(Int) -> Int` 类型的函数。`chooseStepFunction(_:)` 根据布尔值 `backwards` 来返回 `stepForward(_:)` 函数或 `stepBackward(_:)` 函数: 如下名为 `chooseStepFunction(_:)` 的函数,它的返回类型是 `(Int) -> Int` 类型的函数。`chooseStepFunction(_:)` 根据布尔值 `backwards` 来返回 `stepForward(_:)` 函数或 `stepBackward(_:)` 函数:
```swift ```swift
func chooseStepFunction(backwards: Bool) -> (Int) -> Int { func chooseStepFunction(backward: Bool) -> (Int) -> Int {
return backwards ? stepBackward : stepForward return backward ? stepBackward : stepForward
} }
``` ```
@ -525,13 +493,14 @@ func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
```swift ```swift
var currentValue = 3 var currentValue = 3
let moveNearerToZero = chooseStepFunction(currentValue > 0) let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the stepBackward() function // moveNearerToZero 现在指向 stepBackward() 函数。
``` ```
上面这个例子中计算出从 `currentValue` 逐渐接近到`0`是需要向正数走还是向负数走。`currentValue` 的初始值是`3`,这意味着 `currentValue > 0` 是真的(`true`),这将使得 `chooseStepFunction(_:)` 返回 `stepBackward(_:)` 函数。一个指向返回的函数的引用保存在了 `moveNearerToZero` 常量中。 上面这个例子中计算出从 `currentValue` 逐渐接近到0是需要向正数走还是向负数走。`currentValue` 的初始值是 `3`,这意味着 `currentValue > 0` 为真(true这将使得 `chooseStepFunction(_:)` 返回 `stepBackward(_:)` 函数。一个指向返回的函数的引用保存在了 `moveNearerToZero` 常量中。
现在moveNearerToZero 指向了正确的函数,它可以被用来数到零:
现在,`moveNearerToZero` 指向了正确的函数,它可以被用来数到`0`
```swift ```swift
print("Counting to zero:") print("Counting to zero:")
@ -548,22 +517,22 @@ print("zero!")
``` ```
<a name="Nested_Functions"></a> <a name="Nested_Functions"></a>
## 嵌套函数Nested Functions ## 嵌套函数 (Nested Functions)
章中你所见到的所有函数都叫全局函数global functions它们定义在全局域中。你也可以把函数定义在别的函数体中称作嵌套函数nested functions 到目前为止本章中你所见到的所有函数都叫`全局`函数global functions它们定义在`全局域`中。你也可以把函数定义在别的函数体中,称作 `嵌套函数`nested functions
默认情况下嵌套函数是对外界不可见的但是可以被它们的外围函数enclosing function调用。一个外围函数也可以返回它的某一个嵌套函数使得这个函数可以在其他域中被使用。 默认情况下嵌套函数是对外界不可见的但是可以被它们的外围函数enclosing function调用。一个外围函数也可以返回它的某一个嵌套函数使得这个函数可以在其他域中被使用。
你可以用返回嵌套函数的方式重写 `chooseStepFunction(_:)` 函数: 你可以用返回嵌套函数的方式重写 `chooseStepFunction(_:)` 函数:
```swift ```swift
func chooseStepFunction(backwards: Bool) -> (Int) -> Int { func chooseStepFunction(backward: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int { return input + 1 } func stepForward(input: Int) -> Int { return input + 1 }
func stepBackward(input: Int) -> Int { return input - 1 } func stepBackward(input: Int) -> Int { return input - 1 }
return backwards ? stepBackward : stepForward return backward ? stepBackward : stepForward
} }
var currentValue = -4 var currentValue = -4
let moveNearerToZero = chooseStepFunction(currentValue > 0) let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the nested stepForward() function // moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 { while currentValue != 0 {
print("\(currentValue)... ") print("\(currentValue)... ")

View File

@ -11,6 +11,9 @@
> 2.1 > 2.1
> 翻译:[100mango](https://github.com/100mango), [magicdict](https://github.com/magicdict) > 翻译:[100mango](https://github.com/100mango), [magicdict](https://github.com/magicdict)
> 校对:[shanks](http://codebuild.me) > 校对:[shanks](http://codebuild.me)
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-12
本页包含内容: 本页包含内容:
@ -134,7 +137,7 @@ reversed = names.sort( { s1, s2 in return s1 > s2 } )
reversed = names.sort( { s1, s2 in s1 > s2 } ) reversed = names.sort( { s1, s2 in s1 > s2 } )
``` ```
在这个例子中,`sort(_:)`方法的第二个参数函数类型明确了闭包必须返回一个`Bool`类型值。因为闭包函数体只包含了一个单一表达式(`s1 > s2`),该表达式返回`Bool`类型值,因此这里没有歧义,`return`关键字可以省略。 在这个例子中,`sort(_:)`方法的数类型明确了闭包必须返回一个`Bool`类型值。因为闭包函数体只包含了一个单一表达式(`s1 > s2`),该表达式返回`Bool`类型值,因此这里没有歧义,`return`关键字可以省略。
<a name="shorthand_argument_names"></a> <a name="shorthand_argument_names"></a>
### 参数名称缩写Shorthand Argument Names ### 参数名称缩写Shorthand Argument Names
@ -152,7 +155,7 @@ reversed = names.sort( { $0 > $1 } )
<a name="operator_functions"></a> <a name="operator_functions"></a>
### 运算符函数Operator Functions ### 运算符函数Operator Functions
实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。Swift 的`String`类型定义了关于大于号(`>`)的字符串实现,其作为一个函数接受两个`String`类型的参数并返回`Bool`类型的值。而这正好与`sort(_:)`方法的第二个参数需要的函数类型相符合。因此您可以简单地传递一个大于号Swift 可以自动推断出您想使用大于号的字符串函数实现: 实际上还有一种更简短的方式来撰写上面例子中的闭包表达式。Swift 的`String`类型定义了关于大于号(`>`)的字符串实现,其作为一个函数接受两个`String`类型的参数并返回`Bool`类型的值。而这正好与`sort(_:)`方法的参数需要的函数类型相符合。因此您可以简单地传递一个大于号Swift 可以自动推断出您想使用大于号的字符串函数实现:
```swift ```swift
reversed = names.sort(>) reversed = names.sort(>)
@ -213,7 +216,8 @@ let numbers = [16, 58, 510]
```swift ```swift
let strings = numbers.map { let strings = numbers.map {
(var number) -> String in (number) -> String in
var number = number
var output = "" var output = ""
while number > 0 { while number > 0 {
output = digitNames[number % 10]! + output output = digitNames[number % 10]! + output
@ -227,7 +231,7 @@ let strings = numbers.map {
`map(_:)`为数组中每一个元素调用了闭包表达式。您不需要指定闭包的输入参数`number`的类型,因为可以通过要映射的数组类型进行推断。 `map(_:)`为数组中每一个元素调用了闭包表达式。您不需要指定闭包的输入参数`number`的类型,因为可以通过要映射的数组类型进行推断。
在该例中,闭包`number`参数被声明为一个变量参数(变量的具体描述请参看[常量参数和变量参数](./06_Functions.html#constant_and_variable_parameters)),因此可以在闭包函数体内对其进行修改,而不用再定义一个新的局部变量并将`number`的值赋值给它。闭包表达式指定了返回类型为`String`,以表明存储映射值的新数组类型为`String` 在该例中,局部变量`number`的值由闭包中的`number`参数获得,因此可以在闭包函数体内对其进行修改,(闭包或者函数的参数总是常量),闭包表达式指定了返回类型为`String`,以表明存储映射值的新数组类型为`String`
闭包表达式在每次被调用的时候创建了一个叫做`output`的字符串并返回。其使用求余运算符(`number % 10`)计算最后一位数字并利用`digitNames`字典获取所映射的字符串。 闭包表达式在每次被调用的时候创建了一个叫做`output`的字符串并返回。其使用求余运算符(`number % 10`)计算最后一位数字并利用`digitNames`字典获取所映射的字符串。

View File

@ -10,7 +10,10 @@
> 2.1 > 2.1
> 翻译:[Channe](https://github.com/Channe) > 翻译:[Channe](https://github.com/Channe)
> 校对:[shanks](http://codebuild.me) > 校对:[shanks](http://codebuild.me)
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-13
本页内容包含: 本页内容包含:
@ -293,11 +296,8 @@ if let somePlanet = Planet(rawValue: positionToFind) {
<a name="recursive_enumerations"></a> <a name="recursive_enumerations"></a>
## 递归枚举Recursive Enumerations ## 递归枚举Recursive Enumerations
当各种可能的情况可以被穷举时,非常适合使用枚举进行数据建模,例如可以用枚举来表示用于简单整数运算的操作符。这些操作符让你可以将简单的算术表达式,例如整数`5`,结合为更为复杂的表达式,例如`5 + 4`
算术表达式的一个重要特性是,表达式可以嵌套使用。例如,表达式`(5 + 4) * 2`,乘号右边是一个数字,左边则是另一个表达式。因为数据是嵌套的,因而用来存储数据的枚举类型也需要支持这种嵌套——这意味着枚举类型需要支持递归。 *递归枚举recursive enumeration*是一种枚举类型,它有一个或多个枚举成员使用该枚举类型的实例作为关联值。使用递归枚举时,编译器会插入一个间接层。你可以在枚举成员前加上`indirect`来表示该成员可递归。
*递归枚举recursive enumeration*是一种枚举类型,它有一个或多个枚举成员使用该枚举类型的实例作为关联值。使用递归枚举时,编译器会插入一个间接层。你可以在枚举成员前加上`indirect`来表示该成员可递归。
例如,下面的例子中,枚举类型存储了简单的算术表达式: 例如,下面的例子中,枚举类型存储了简单的算术表达式:
@ -319,7 +319,14 @@ indirect enum ArithmeticExpression {
} }
``` ```
上面定义的枚举类型可以存储三种算术表达式:纯数字、两个表达式相加、两个表达式相乘。枚举成员`Addition``Multiplication`的关联值也是算术表达式——这些关联值使得嵌套表达式成为可能。 上面定义的枚举类型可以存储三种算术表达式:纯数字、两个表达式相加、两个表达式相乘。枚举成员`Addition``Multiplication`的关联值也是算术表达式——这些关联值使得嵌套表达式成为可能。例如,表达式`(5 + 4) * 2`,乘号右边是一个数字,左边则是另一个表达式。因为数据是嵌套的,因而用来存储数据的枚举类型也需要支持这种嵌套——这意味着枚举类型需要支持递归。下面的代码展示了使用`ArithmeticExpression `这个递归枚举创建表达式`(5 + 4) * 2`
```swift
let five = ArithmeticExpression.Number(5)
let four = ArithmeticExpression.Number(4)
let sum = ArithmeticExpression.Addition(five, four)
let product = ArithmeticExpression.Multiplication(sum, ArithmeticExpression.Number(2))
```
要操作具有递归性质的数据结构,使用递归函数是一种直截了当的方式。例如,下面是一个对算术表达式求值的函数: 要操作具有递归性质的数据结构,使用递归函数是一种直截了当的方式。例如,下面是一个对算术表达式求值的函数:
@ -334,12 +341,7 @@ func evaluate(expression: ArithmeticExpression) -> Int {
return evaluate(left) * evaluate(right) return evaluate(left) * evaluate(right)
} }
} }
// 计算 (5 + 4) * 2
let five = ArithmeticExpression.Number(5)
let four = ArithmeticExpression.Number(4)
let sum = ArithmeticExpression.Addition(five, four)
let product = ArithmeticExpression.Multiplication(sum, ArithmeticExpression.Number(2))
print(evaluate(product)) print(evaluate(product))
// 输出 "18" // 输出 "18"
``` ```

View File

@ -10,6 +10,9 @@
> 2.1 > 2.1
> 校对:[shanks](http://codebuild.me)2015-10-29 > 校对:[shanks](http://codebuild.me)2015-10-29
> 2.2
> 校对:[SketchK](https://github.com/SketchK) 2016-05-13
本页包含内容: 本页包含内容:
- [类和结构体对比](#comparing_classes_and_structures) - [类和结构体对比](#comparing_classes_and_structures)
@ -33,7 +36,7 @@ Swift 中类和结构体有很多共同点。共同处在于:
* 定义属性用于存储值 * 定义属性用于存储值
* 定义方法用于提供功能 * 定义方法用于提供功能
* 定义附属脚本用于访问 * 定义下标操作使得可以通过下标语法来访问实例所包含的
* 定义构造器用于生成初始化值 * 定义构造器用于生成初始化值
* 通过扩展以增加默认实现的功能 * 通过扩展以增加默认实现的功能
* 实现协议以提供某种标准功能 * 实现协议以提供某种标准功能

View File

@ -1,431 +1,438 @@
# 属性 (Properties) # 属性 (Properties)
--- ---
> 1.0 > 1.0
> 翻译:[shinyzhu](https://github.com/shinyzhu) > 翻译:[shinyzhu](https://github.com/shinyzhu)
> 校对:[pp-prog](https://github.com/pp-prog) [yangsiy](https://github.com/yangsiy) > 校对:[pp-prog](https://github.com/pp-prog) [yangsiy](https://github.com/yangsiy)
> 2.0
> 翻译+校对:[yangsiy](https://github.com/yangsiy) > 2.0
> 翻译+校对:[yangsiy](https://github.com/yangsiy)
> 2.1
> 翻译:[buginux](https://github.com/buginux)
> 校对:[shanks](http://codebuild.me)2015-10-29 > 2.1
> 翻译:[buginux](https://github.com/buginux)
本页包含内容: > 校对:[shanks](http://codebuild.me)2015-10-29
- [存储属性Stored Properties](#stored_properties)
- [计算属性Computed Properties](#computed_properties) > 2.2
- [属性观察器Property Observers](#property_observers) > 翻译:[saitjr](https://github.com/saitjr)2016-04-11[SketchK](https://github.com/SketchK) 2016-05-13
- [全局变量和局部变量Global and Local Variables](#global_and_local_variables)
- [类型属性Type Properties](#type_properties)
*属性*将值跟特定的类、结构或枚举关联。存储属性存储常量或变量作为实例的一部分,而计算属性计算(不是存储)一个值。计算属性可以用于类、结构体和枚举,存储属性只能用于类和结构体。 本页包含内容:
存储属性和计算属性通常与特定类型的实例关联。但是,属性也可以直接作用于类型本身,这种属性称为类型属性。 - [存储属性Stored Properties](#stored_properties)
- [计算属性Computed Properties](#computed_properties)
另外,还可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作。属性观察器可以添加到自己定义的存储属性上,也可以添加到从父类继承的属性上。 - [属性观察器Property Observers](#property_observers)
- [全局变量和局部变量Global and Local Variables](#global_and_local_variables)
<a name="stored_properties"></a> - [类型属性Type Properties](#type_properties)
## 存储属性
*属性*将值跟特定的类、结构或枚举关联。存储属性存储常量或变量作为实例的一部分,而计算属性计算(不是存储)一个值。计算属性可以用于类、结构体和枚举,存储属性只能用于类和结构体。
简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量。存储属性可以是*变量存储属性*(用关键字`var`定义),也可以是*常量存储属性*(用关键字`let`定义)。
存储属性和计算属性通常与特定类型的实例关联。但是,属性也可以直接作用于类型本身,这种属性称为类型属性。
可以在定义存储属性的时候指定默认值,请参考[默认构造器](./14_Initialization.html#default_initializers)一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考[构造过程中常量属性的修改](./14_Initialization.html#assigning_constant_properties_during_initialization)一节。
另外,还可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作。属性观察器可以添加到自己定义的存储属性上,也可以添加到从父类继承的属性上。
下面的例子定义了一个名为`FixedLengthRange`的结构体,它描述了一个在创建后无法修改值域宽度的区间:
<a name="stored_properties"></a>
```swift ## 存储属性
struct FixedLengthRange {
var firstValue: Int 简单来说,一个存储属性就是存储在特定类或结构体实例里的一个常量或变量。存储属性可以是*变量存储属性*(用关键字 `var` 定义),也可以是*常量存储属性*(用关键字 `let` 定义)。
let length: Int
} 可以在定义存储属性的时候指定默认值,请参考[默认构造器](./14_Initialization.html#default_initializers)一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考[构造过程中常量属性的修改](./14_Initialization.html#assigning_constant_properties_during_initialization)一节。
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
// 该区间表示整数012 下面的例子定义了一个名为 `FixedLengthRange` 的结构体,该结构体用于描述整数的范围,且这个范围值在被创建后不能被修改.
rangeOfThreeItems.firstValue = 6
// 该区间现在表示整数678 ```swift
``` struct FixedLengthRange {
var firstValue: Int
`FixedLengthRange`的实例包含一个名为`firstValue`的变量存储属性和一个名为`length`的常量存储属性。在上面的例子中,`length`在创建实例的时候被初始化,因为它是一个常量存储属性,所以之后无法修改它的值。 let length: Int
}
<a name="stored_properties_of_constant_structure_instances"></a> var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
### 常量结构体的存储属性 // 该区间表示整数012
rangeOfThreeItems.firstValue = 6
如果创建了一个结构体的实例并将其赋值给一个常量,则无法修改该实例的任何属性,即使定义了变量存储属性: // 该区间现在表示整数678
```
```swift
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4) `FixedLengthRange` 的实例包含一个名为 `firstValue` 的变量存储属性和一个名为 `length` 的常量存储属性。在上面的例子中,`length` 在创建实例的时候被初始化,因为它是一个常量存储属性,所以之后无法修改它的值。
// 该区间表示整数0123
rangeOfFourItems.firstValue = 6 <a name="stored_properties_of_constant_structure_instances"></a>
// 尽管 firstValue 是个变量属性,这里还是会报错 ### 常量结构体的存储属性
```
如果创建了一个结构体的实例并将其赋值给一个常量,则无法修改该实例的任何属性,即使有属性被声明为变量也不行:
因为`rangeOfFourItems`被声明成了常量(用`let`关键字),即使`firstValue`是一个变量属性,也无法再修改它了。
```swift
这种行为是由于结构体struct属于*值类型*。当值类型的实例被声明为常量的时候,它的所有属性也就成了常量。 let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// 该区间表示整数0123
属于*引用类型*的类class则不一样。把一个引用类型的实例赋给一个常量后仍然可以修改该实例的变量属性。 rangeOfFourItems.firstValue = 6
// 尽管 firstValue 是个变量属性,这里还是会报错
<a name="lazy_stored_properties"></a> ```
### 延迟存储属性
因为 `rangeOfFourItems` 被声明成了常量(用 `let` 关键字),即使 `firstValue` 是一个变量属性,也无法再修改它了。
延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用`lazy`来标示一个延迟存储属性。
这种行为是由于结构体struct属于*值类型*。当值类型的实例被声明为常量的时候,它的所有属性也就成了常量。
> 注意
> 必须将延迟存储属性声明成变量(使用`var`关键字),因为属性的初始值可能在实例构造完成之后才会得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。 属于*引用类型*的类class则不一样。把一个引用类型的实例赋给一个常量后仍然可以修改该实例的变量属性。
延迟属性很有用,当属性的值依赖于在实例的构造过程结束后才会知道具体值的外部因素时,或者当获得属性的初始值需要复杂或大量计算时,可以只在需要的时候计算它。 <a name="lazy_stored_properties"></a>
### 延迟存储属性
下面的例子使用了延迟存储属性来避免复杂类中不必要的初始化。例子中定义了`DataImporter``DataManager`两个类,下面是部分代码:
延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用 `lazy` 来标示一个延迟存储属性。
```swift
class DataImporter { > 注意
/* > 必须将延迟存储属性声明成变量(使用 `var` 关键字),因为属性的初始值可能在实例构造完成之后才会得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。
DataImporter 是一个负责将外部文件中的数据导入的类。
这个类的初始化会消耗不少时间 延迟属性很有用,当属性的值依赖于在实例的构造过程结束后才会知道影响值的外部因素时,或者当获得属性的初始值需要复杂或大量计算时,可以只在需要的时候计算它
*/
var fileName = "data.txt" 下面的例子使用了延迟存储属性来避免复杂类中不必要的初始化。例子中定义了 `DataImporter``DataManager` 两个类,下面是部分代码:
// 这里会提供数据导入功能
} ```swift
class DataImporter {
class DataManager { /*
lazy var importer = DataImporter() DataImporter 是一个负责将外部文件中的数据导入的类。
var data = [String]() 这个类的初始化会消耗不少时间。
// 这里会提供数据管理功能 */
} var fileName = "data.txt"
// 这里会提供数据导入功能
let manager = DataManager() }
manager.data.append("Some data")
manager.data.append("Some more data") class DataManager {
// DataImporter 实例的 importer 属性还没有被创建 lazy var importer = DataImporter()
``` var data = [String]()
// 这里会提供数据管理功能
`DataManager`类包含一个名为`data`的存储属性,初始值是一个空的字符串(`String`)数组。这里没有给出全部代码,只需知道`DataManager`类的目的是管理和提供对这个字符串数组的访问即可。 }
`DataManager`的一个功能是从文件导入数据。该功能由`DataImporter`类提供,`DataImporter`完成初始化需要消耗不少时间:因为它的实例在初始化时可能要打开文件,还要读取文件内容到内存。 let manager = DataManager()
manager.data.append("Some data")
`DataManager`管理数据时也可能不从文件中导入数据。所以当`DataManager`的实例被创建时,没必要创建一个`DataImporter`的实例,更明智的做法是第一次用到`DataImporter`的时候才去创建它。 manager.data.append("Some more data")
// DataImporter 实例的 importer 属性还没有被创建
由于使用了`lazy``importer`属性只有在第一次被访问的时候才被创建。比如访问它的属性`fileName`时: ```
```swift `DataManager` 类包含一个名为 `data` 的存储属性,初始值是一个空的字符串(`String`)数组。这里没有给出全部代码,只需知道 `DataManager` 类的目的是管理和提供对这个字符串数组的访问即可。
print(manager.importer.fileName)
// DataImporter 实例的 importer 属性现在被创建了 `DataManager` 的一个功能是从文件导入数据。该功能由 `DataImporter` 类提供,`DataImporter` 完成初始化需要消耗不少时间:因为它的实例在初始化时可能要打开文件,还要读取文件内容到内存。
// 输出 "data.txt”
``` `DataManager` 管理数据时也可能不从文件中导入数据。所以当 `DataManager` 的实例被创建时,没必要创建一个 `DataImporter` 的实例,更明智的做法是第一次用到 `DataImporter` 的时候才去创建它。
> 注意 由于使用了 `lazy` `importer` 属性只有在第一次被访问的时候才被创建。比如访问它的属性 `fileName` 时:
> 如果一个被标记为`lazy`的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。
```swift
<a name="stored_properties_and_instance_variables"></a> print(manager.importer.fileName)
### 存储属性和实例变量 // DataImporter 实例的 importer 属性现在被创建了
// 输出 "data.txt”
如果您有过 Objective-C 经验,应该知道 Objective-C 为类实例存储值和引用提供两种方法。除了属性之外,还可以使用实例变量作为属性值的后端存储。 ```
Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的后端存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。属性的全部信息——包括命名、类型和内存管理特征——都在唯一一个地方(类型定义中)定义。 > 注意
> 如果一个被标记为 `lazy` 的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。
<a name="computed_properties"></a>
## 计算属性 <a name="stored_properties_and_instance_variables"></a>
### 存储属性和实例变量
除存储属性外,类、结构体和枚举可以定义*计算属性*。计算属性不直接存储值,而是提供一个 getter 和一个可选的 setter来间接获取和设置其他属性或变量的值。
如果您有过 Objective-C 经验,应该知道 Objective-C 为类实例存储值和引用提供两种方法。除了属性之外,还可以使用实例变量作为属性值的后端存储。
```swift
struct Point { Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的后端存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。属性的全部信息——包括命名、类型和内存管理特征——都在唯一一个地方(类型定义中)定义。
var x = 0.0, y = 0.0
} <a name="computed_properties"></a>
struct Size { ## 计算属性
var width = 0.0, height = 0.0
} 除存储属性外,类、结构体和枚举可以定义*计算属性*。计算属性不直接存储值,而是提供一个 getter 和一个可选的 setter来间接获取和设置其他属性或变量的值。
struct Rect {
var origin = Point() ```swift
var size = Size() struct Point {
var center: Point { var x = 0.0, y = 0.0
get { }
let centerX = origin.x + (size.width / 2) struct Size {
let centerY = origin.y + (size.height / 2) var width = 0.0, height = 0.0
return Point(x: centerX, y: centerY) }
} struct Rect {
set(newCenter) { var origin = Point()
origin.x = newCenter.x - (size.width / 2) var size = Size()
origin.y = newCenter.y - (size.height / 2) var center: Point {
} get {
} let centerX = origin.x + (size.width / 2)
} let centerY = origin.y + (size.height / 2)
var square = Rect(origin: Point(x: 0.0, y: 0.0), return Point(x: centerX, y: centerY)
size: Size(width: 10.0, height: 10.0)) }
let initialSquareCenter = square.center set(newCenter) {
square.center = Point(x: 15.0, y: 15.0) origin.x = newCenter.x - (size.width / 2)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))") origin.y = newCenter.y - (size.height / 2)
// 输出 "square.origin is now at (10.0, 10.0)” }
``` }
}
这个例子定义了 3 个结构体来描述几何形状: var square = Rect(origin: Point(x: 0.0, y: 0.0),
size: Size(width: 10.0, height: 10.0))
- `Point`封装了一个`(x, y)`的坐标 let initialSquareCenter = square.center
- `Size`封装了一个`width`和一个`height` square.center = Point(x: 15.0, y: 15.0)
- `Rect`表示一个有原点和尺寸的矩形 print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// 输出 "square.origin is now at (10.0, 10.0)”
`Rect`也提供了一个名为`center`的计算属性。一个矩形的中心点可以从原点(`origin`)和尺寸(`size`)算出,所以不需要将它以显式声明的`Point`来保存。`Rect`的计算属性`center`提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。 ```
上述例子中创建了一个名为`square``Rect`实例,初始值原点是`(0, 0)`,宽度高度都是`10`。如下图中蓝色正方形所示。 这个例子定义了 3 个结构体来描述几何形状:
`square``center`属性可以通过点运算符(`square.center`)来访问,这会调用该属性的 getter 来获取它的值。跟直接返回已经存在的值不同getter 实际上通过计算然后返回一个新的`Point`来表示`square`的中心点。如代码所示,它正确返回了中心点`(5, 5)` - `Point` 封装了一个 `(x, y)` 的坐标
- `Size` 封装了一个 `width` 和一个 `height`
`center`属性之后被设置了一个新的值`(15, 15)`,表示向右上方移动正方形到如下图橙色正方形所示的位置。设置属性`center`的值会调用它的 setter 来修改属性`origin``x``y`的值,从而实现移动正方形到新的位置。 - `Rect` 表示一个有原点和尺寸的矩形
<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" /> `Rect`也提供了一个名为`center` 的计算属性。一个矩形的中心点可以从原点(`origin`)和大小(`size`)算出,所以不需要将它以显式声明的 `Point` 来保存。`Rect` 的计算属性 `center` 提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。
<a name="shorthand_setter_declaration"></a> 上述例子中创建了一个名为 `square``Rect` 实例,初始值原点是 `(0, 0)`,宽度高度都是 `10`。如下图中蓝色正方形所示。
### 便捷 setter 声明
`square``center` 属性可以通过点运算符(`square.center`)来访问,这会调用该属性的 getter 来获取它的值。跟直接返回已经存在的值不同getter 实际上通过计算然后返回一个新的 `Point` 来表示 `square` 的中心点。如代码所示,它正确返回了中心点 `(5, 5)`
如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称`newValue`。下面是使用了便捷 setter 声明的`Rect`结构体代码:
`center` 属性之后被设置了一个新的值 `(15, 15)`,表示向右上方移动正方形到如下图橙色正方形所示的位置。设置属性`center`的值会调用它的 setter 来修改属性 `origin``x``y` 的值,从而实现移动正方形到新的位置。
```swift
struct AlternativeRect { <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" />
var origin = Point()
var size = Size() <a name="shorthand_setter_declaration"></a>
var center: Point { ### 便捷 setter 声明
get {
let centerX = origin.x + (size.width / 2) 如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称 `newValue`。下面是使用了便捷 setter 声明的 `Rect` 结构体代码:
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY) ```swift
} struct AlternativeRect {
set { var origin = Point()
origin.x = newValue.x - (size.width / 2) var size = Size()
origin.y = newValue.y - (size.height / 2) var center: Point {
} get {
} let centerX = origin.x + (size.width / 2)
} let centerY = origin.y + (size.height / 2)
``` return Point(x: centerX, y: centerY)
}
<a name="readonly_computed_properties"></a> set {
### 只读计算属性 origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
只有 getter 没有 setter 的计算属性就是*只读计算属性*。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。 }
}
> 注意 }
> 必须使用`var`关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。`let`关键字只用来声明常量属性,表示初始化后再也无法修改的值。 ```
只读计算属性的声明可以去掉`get`关键字和花括号: <a name="readonly_computed_properties"></a>
### 只读计算属性
```swift
struct Cuboid { 只有 getter 没有 setter 的计算属性就是*只读计算属性*。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double { > 注意
return width * height * depth > 必须使用 `var` 关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。`let` 关键字只用来声明常量属性,表示初始化后再也无法修改的值。
}
} 只读计算属性的声明可以去掉 `get` 关键字和花括号:
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") ```swift
// 输出 "the volume of fourByFiveByTwo is 40.0" struct Cuboid {
``` var width = 0.0, height = 0.0, depth = 0.0
var volume: Double {
这个例子定义了一个名为`Cuboid`的结构体,表示三维空间的立方体,包含`width``height``depth`属性。结构体还有一个名为`volume`的只读计算属性用来返回立方体的体积。为`volume`提供 setter 毫无意义,因为无法确定如何修改`width``height``depth`三者的值来匹配新的`volume`。然而,`Cuboid`提供一个只读计算属性来让外部用户直接获取体积是很有用的。 return width * height * depth
}
<a name="property_observers"></a> }
## 属性观察器 let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
*属性观察器*监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新值和当前值相同的时候也不例外。 // 输出 "the volume of fourByFiveByTwo is 40.0"
```
可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重写属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。属性重写请参考[重写](./13_Inheritance.html#overriding)。
这个例子定义了一个名为 `Cuboid` 的结构体,表示三维空间的立方体,包含 `width``height``depth` 属性。结构体还有一个名为 `volume` 的只读计算属性用来返回立方体的体积。为 `volume` 提供 setter 毫无意义,因为无法确定如何修改 `width``height``depth` 三者的值来匹配新的 `volume`。然而,`Cuboid` 提供一个只读计算属性来让外部用户直接获取体积是很有用的。
> 注意
> 不需要为非重写的计算属性添加属性观察器,因为可以通过它的 setter 直接监控和响应值的变化。 <a name="property_observers"></a>
## 属性观察器
可以为属性添加如下的一个或全部观察器:
*属性观察器*监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,即使新值和当前值相同的时候也不例外。
- `willSet`在新的值被设置之前调用
- `didSet`在新的值被设置之后立即调用 可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重写属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。你不必为非重写的计算属性添加属性观察器,因为可以通过它的 setter 直接监控和响应值的变化。 属性重写请参考[重写](./13_Inheritance.html#overriding)。
`willSet`观察器会将新的属性值作为常量参数传入,在`willSet`的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称`newValue`表示。
可以为属性添加如下的一个或全部观察器:
类似地,`didSet`观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名`oldValue`
- `willSet` 在新的值被设置之前调用
> 注意 - `didSet` 在新的值被设置之后立即调用
> 父类的属性在子类的构造器中被赋值时,它在父类中的`willSet`和`didSet`观察器会被调用。
> 有关构造器代理的更多信息,请参考[值类型的构造器代理](./14_Initialization.html#initializer_delegation_for_value_types)和[类的构造器代理规则](./14_Initialization.html#initializer_delegation_for_class_types) `willSet` 观察器会将新的属性值作为常量参数传入,在 `willSet` 的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称 `newValue` 表示
这里是一个`willSet``didSet`的实际例子,其中定义了一个名为`StepCounter`的类,用来统计一个人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用 同样,`didSet` 观察器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名 `oldValue`。如果在 `didSet` 方法中再次对该属性赋值,那么新值会覆盖旧的值
```swift > 注意
class StepCounter { > 父类的属性在子类的构造器中被赋值时,它在父类中的 `willSet` 和 `didSet` 观察器会被调用,随后才会调用子类的观察器。在父类初始化方法调用之前,子类给属性赋值时,观察器不会被调用。
var totalSteps: Int = 0 { > 有关构造器代理的更多信息,请参考[值类型的构造器代理](./14_Initialization.html#initializer_delegation_for_value_types)和[类的构造器代理规则](./14_Initialization.html#initializer_delegation_for_class_types)。
willSet(newTotalSteps) {
print("About to set totalSteps to \(newTotalSteps)") 下面是一个 `willSet``didSet` 实际运用的例子,其中定义了一个名为 `StepCounter` 的类,用来统计一个人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。
}
didSet { ```swift
if totalSteps > oldValue { class StepCounter {
print("Added \(totalSteps - oldValue) steps") var totalSteps: Int = 0 {
} willSet(newTotalSteps) {
} print("About to set totalSteps to \(newTotalSteps)")
} }
} didSet {
let stepCounter = StepCounter() if totalSteps > oldValue {
stepCounter.totalSteps = 200 print("Added \(totalSteps - oldValue) steps")
// About to set totalSteps to 200 }
// Added 200 steps }
stepCounter.totalSteps = 360 }
// About to set totalSteps to 360 }
// Added 160 steps let stepCounter = StepCounter()
stepCounter.totalSteps = 896 stepCounter.totalSteps = 200
// About to set totalSteps to 896 // About to set totalSteps to 200
// Added 536 steps // Added 200 steps
``` stepCounter.totalSteps = 360
// About to set totalSteps to 360
`StepCounter`类定义了一个`Int`类型的属性`totalSteps`,它是一个存储属性,包含`willSet``didSet`观察器。 // Added 160 steps
stepCounter.totalSteps = 896
`totalSteps`被设置新值的时候,它的`willSet``didSet`观察器都会被调用,甚至新值和当前值完全相同时也会被调用。 // About to set totalSteps to 896
// Added 536 steps
例子中的`willSet`观察器将表示新值的参数自定义为`newTotalSteps`,这个观察器只是简单的将新的值输出。 ```
`didSet`观察器在`totalSteps`的值改变后被调用,它把新值和旧值进行对比,如果总步数增加了,就输出一个消息表示增加了多少步。`didSet`没有为旧值提供自定义名称,所以默认值`oldValue`表示旧值的参数名 `StepCounter` 类定义了一个 `Int` 类型的属性 `totalSteps`,它是一个存储属性,包含 `willSet``didSet` 观察器
> 注意 `totalSteps` 被设置新值的时候,它的 `willSet``didSet` 观察器都会被调用,即使新值和当前值完全相同时也会被调用。
> 如果在一个属性的`didSet`观察器里为它赋值,这个值会替换之前设置的值。
例子中的 `willSet` 观察器将表示新值的参数自定义为 `newTotalSteps`,这个观察器只是简单的将新的值输出。
<a name="global_and_local_variables"></a>
##全局变量和局部变量 `didSet` 观察器在 `totalSteps` 的值改变后被调用,它把新值和旧值进行对比,如果总步数增加了,就输出一个消息表示增加了多少步。`didSet` 没有为旧值提供自定义名称,所以默认值 `oldValue` 表示旧值的参数名。
计算属性和属性观察器所描述的功能也可以用于*全局变量*和*局部变量*。全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。 >注意
>
前面章节提到的全局或局部变量都属于存储型变量,跟存储属性类似,它为特定类型的值提供存储空间,并允许读取和写入。 >如果将属性通过 in-out 方式传入函数,`willSet` 和 `didSet` 也会调用。这是因为 in-out 参数采用了拷入拷出模式:即在函数内部使用的是参数的 copy函数结束后又对参数重新赋值。关于 in-out 参数详细的介绍,请参考[输入输出参数](../chapter3/05_Declarations.html#in-out_parameters)
另外,在全局或局部范围都可以定义计算型变量和为存储型变量定义观察器。计算型变量跟计算属性一样,返回一个计算结果而不是存储值,声明格式也完全一样。 <a name="global_and_local_variables"></a>
##全局变量和局部变量
> 注意
> 全局的常量或变量都是延迟计算的,跟[延迟存储属性](#lazy_stored_properties)相似,不同的地方在于,全局的常量或变量不需要标记`lazy`修饰符。 计算属性和属性观察器所描述的功能也可以用于*全局变量*和*局部变量*。全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。
> 局部范围的常量或变量从不延迟计算。
前面章节提到的全局或局部变量都属于存储型变量,跟存储属性类似,它为特定类型的值提供存储空间,并允许读取和写入。
<a name="type_properties"></a>
##类型属性 另外,在全局或局部范围都可以定义计算型变量和为存储型变量定义观察器。计算型变量跟计算属性一样,返回一个计算结果而不是存储值,声明格式也完全一样。
实例属性属于一个特定类型的实例,每创建一个实例,实例都拥有属于自己的一套属性值,实例之间的属性相互独立。 > 注意
> 全局的常量或变量都是延迟计算的,跟[延迟存储属性](#lazy_stored_properties)相似,不同的地方在于,全局的常量或变量不需要标记`lazy`修饰符。
也可以为类型本身定义属性,无论创建了多少个该类型的实例,这些属性都只有唯一一份。这种属性就是*类型属性*。 > 局部范围的常量或变量从不延迟计算。
类型属性用于定义某个类型所有实例共享的数据,比如所有实例都能用的一个常量(就像 C 语言中的静态常量),或者所有实例都能访问的一个变量(就像 C 语言中的静态变量)。 <a name="type_properties"></a>
##类型属性
存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算型属性一样只能定义成变量属性。
实例属性属于一个特定类型的实例,每创建一个实例,实例都拥有属于自己的一套属性值,实例之间的属性相互独立。
> 注意
> 跟实例的存储型属性不同,必须给存储型类型属性指定默认值,因为类型本身没有构造器,也就无法在初始化过程中使用构造器给类型属性赋值。 也可以为类型本身定义属性,无论创建了多少个该类型的实例,这些属性都只有唯一一份。这种属性就是*类型属性*。
> 存储型类型属性是延迟初始化的,它们只有在第一次被访问的时候才会被初始化。即使它们被多个线程同时访问,系统也保证只会对其进行一次初始化,并且不需要对其使用`lazy`修饰符。
类型属性用于定义某个类型所有实例共享的数据,比如所有实例都能用的一个常量(就像 C 语言中的静态常量),或者所有实例都能访问的一个变量(就像 C 语言中的静态变量)。
<a name="type_property_syntax"></a>
###类型属性语法 存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算型属性一样只能定义成变量属性。
在 C 或 Objective-C 中,与某个类型关联的静态常量和静态变量,是作为全局(*global*)静态变量定义的。但是在 Swift 中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。 > 注意
> 跟实例的存储型属性不同,必须给存储型类型属性指定默认值,因为类型本身没有构造器,也就无法在初始化过程中使用构造器给类型属性赋值。
使用关键字`static`来定义类型属性。在为类定义计算型类型属性时,可以改用关键字`class`来支持子类对父类的实现进行重写。下面的例子演示了存储型和计算型类型属性的语法: > 存储型类型属性是延迟初始化的,它们只有在第一次被访问的时候才会被初始化。即使它们被多个线程同时访问,系统也保证只会对其进行一次初始化,并且不需要对其使用 `lazy` 修饰符。
```swift <a name="type_property_syntax"></a>
struct SomeStructure { ###类型属性语法
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int { 在 C 或 Objective-C 中,与某个类型关联的静态常量和静态变量,是作为全局(*global*)静态变量定义的。但是在 Swift 中,类型属性是作为类型定义的一部分写在类型最外层的花括号内,因此它的作用范围也就在类型支持的范围内。
return 1
} 使用关键字 `static` 来定义类型属性。在为类定义计算型类型属性时,可以改用关键字 `class` 来支持子类对父类的实现进行重写。下面的例子演示了存储型和计算型类型属性的语法:
}
enum SomeEnumeration { ```swift
static var storedTypeProperty = "Some value." struct SomeStructure {
static var computedTypeProperty: Int { static var storedTypeProperty = "Some value."
return 6 static var computedTypeProperty: Int {
} return 1
} }
class SomeClass { }
static var storedTypeProperty = "Some value." enum SomeEnumeration {
static var computedTypeProperty: Int { static var storedTypeProperty = "Some value."
return 27 static var computedTypeProperty: Int {
} return 6
class var overrideableComputedTypeProperty: Int { }
return 107 }
} class SomeClass {
} static var storedTypeProperty = "Some value."
``` static var computedTypeProperty: Int {
return 27
> 注意 }
> 例子中的计算型类型属性是只读的,但也可以定义可读可写的计算型类型属性,跟计算型实例属性的语法相同。 class var overrideableComputedTypeProperty: Int {
return 107
<a name="querying_and_setting_type_properties"></a> }
###获取和设置类型属性的值 }
```
跟实例属性一样,类型属性也是通过点运算符来访问。但是,类型属性是通过类型本身来访问,而不是通过实例。比如:
> 注意
```swift > 例子中的计算型类型属性是只读的,但也可以定义可读可写的计算型类型属性,跟计算型实例属性的语法相同。
print(SomeStructure.storedTypeProperty)
// 输出 "Some value." <a name="querying_and_setting_type_properties"></a>
SomeStructure.storedTypeProperty = "Another value." ###获取和设置类型属性的值
print(SomeStructure.storedTypeProperty)
// 输出 "Another value.” 跟实例属性一样,类型属性也是通过点运算符来访问。但是,类型属性是通过类型本身来访问,而不是通过实例。比如:
print(SomeEnumeration.computedTypeProperty)
// 输出 "6" ```swift
print(SomeClass.computedTypeProperty) print(SomeStructure.storedTypeProperty)
// 输出 "27" // 输出 "Some value."
``` SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)
下面的例子定义了一个结构体,使用两个存储型类型属性来表示两个声道的音量,每个声道具有`0``10`之间的整数音量。 // 输出 "Another value.”
print(SomeEnumeration.computedTypeProperty)
下图展示了如何把两个声道结合来模拟立体声的音量。当声道的音量是`0`,没有一个灯会亮;当声道的音量是`10`,所有灯点亮。本图中,左声道的音量是`9`,右声道的音量是`7` // 输出 "6"
print(SomeClass.computedTypeProperty)
<img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/staticPropertiesVUMeter_2x.png" alt="Static Properties VUMeter" width="243" height="357" /> // 输出 "27"
```
上面所描述的声道模型使用`AudioChannel`结构体的实例来表示:
下面的例子定义了一个结构体,使用两个存储型类型属性来表示两个声道的音量,每个声道具有 `0``10` 之间的整数音量。
```swift
struct AudioChannel { 下图展示了如何把两个声道结合来模拟立体声的音量。当声道的音量是 `0`,没有一个灯会亮;当声道的音量是 `10`,所有灯点亮。本图中,左声道的音量是 `9`,右声道的音量是 `7`
static let thresholdLevel = 10
static var maxInputLevelForAllChannels = 0 <img src="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/staticPropertiesVUMeter_2x.png" alt="Static Properties VUMeter" width="243" height="357" />
var currentLevel: Int = 0 {
didSet { 上面所描述的声道模型使用 `AudioChannel` 结构体的实例来表示:
if currentLevel > AudioChannel.thresholdLevel {
// 将当前音量限制在阀值之内 ```swift
currentLevel = AudioChannel.thresholdLevel struct AudioChannel {
} static let thresholdLevel = 10
if currentLevel > AudioChannel.maxInputLevelForAllChannels { static var maxInputLevelForAllChannels = 0
// 存储当前音量作为新的最大输入音量 var currentLevel: Int = 0 {
AudioChannel.maxInputLevelForAllChannels = currentLevel didSet {
} if currentLevel > AudioChannel.thresholdLevel {
} // 将当前音量限制在阀值之内
} currentLevel = AudioChannel.thresholdLevel
} }
``` if currentLevel > AudioChannel.maxInputLevelForAllChannels {
// 存储当前音量作为新的最大输入音量
结构`AudioChannel`定义了 2 个存储型类型属性来实现上述功能。第一个是`thresholdLevel`,表示音量的最大上限阈值,它是一个值为`10`的常量,对所有实例都可见,如果音量高于`10`,则取最大上限值`10`(见后面描述)。 AudioChannel.maxInputLevelForAllChannels = currentLevel
}
第二个类型属性是变量存储型属性`maxInputLevelForAllChannels`,它用来表示所有`AudioChannel`实例的最大音量,初始值是`0` }
}
`AudioChannel`也定义了一个名为`currentLevel`的存储型实例属性,表示当前声道现在的音量,取值为`0``10` }
```
属性`currentLevel`包含`didSet`属性观察器来检查每次设置后的属性值,它做如下两个检查:
结构 `AudioChannel` 定义了 2 个存储型类型属性来实现上述功能。第一个是 `thresholdLevel`,表示音量的最大上限阈值,它是一个值为 `10` 的常量,对所有实例都可见,如果音量高于 `10`,则取最大上限值 `10`(见后面描述)。
- 如果`currentLevel`的新值大于允许的阈值`thresholdLevel`,属性观察器将`currentLevel`的值限定为阈值`thresholdLevel`
- 如果修正后的`currentLevel`值大于静态类型属性`maxInputLevelForAllChannels`的值,属性观察器就将新值保存在`maxInputLevelForAllChannels` 第二个类型属性是变量存储型属性 `maxInputLevelForAllChannels`,它用来表示所有 `AudioChannel` 实例的最大音量,初始值是`0`
> 注意 `AudioChannel` 也定义了一个名为 `currentLevel` 的存储型实例属性,表示当前声道现在的音量,取值为 `0``10`
> 在第一个检查过程中,`didSet`属性观察器将`currentLevel`设置成了不同的值,但这不会造成属性观察器被再次调用。
属性 `currentLevel` 包含 `didSet` 属性观察器来检查每次设置后的属性值,它做如下两个检查:
可以使用结构体`AudioChannel`创建两个声道`leftChannel``rightChannel`,用以表示立体声系统的音量:
- 如果 `currentLevel` 的新值大于允许的阈值 `thresholdLevel`,属性观察器将 `currentLevel` 的值限定为阈值 `thresholdLevel`
```swift - 如果修正后的 `currentLevel` 值大于静态类型属性 `maxInputLevelForAllChannels` 的值,属性观察器就将新值保存在 `maxInputLevelForAllChannels` 中。
var leftChannel = AudioChannel()
var rightChannel = AudioChannel() > 注意
``` > 在第一个检查过程中,`didSet` 属性观察器将 `currentLevel` 设置成了不同的值,但这不会造成属性观察器被再次调用。
如果将左声道的`currentLevel`设置成`7`,类型属性`maxInputLevelForAllChannels`也会更新成`7` 可以使用结构体 `AudioChannel` 创建两个声道 `leftChannel``rightChannel`,用以表示立体声系统的音量
```swift ```swift
leftChannel.currentLevel = 7 var leftChannel = AudioChannel()
print(leftChannel.currentLevel) var rightChannel = AudioChannel()
// 输出 "7" ```
print(AudioChannel.maxInputLevelForAllChannels)
// 输出 "7" 如果将左声道的 `currentLevel` 设置成 `7`,类型属性 `maxInputLevelForAllChannels` 也会更新成 `7`
```
```swift
如果试图将右声道的`currentLevel`设置成`11`,它会被修正到最大值`10`,同时`maxInputLevelForAllChannels`的值也会更新到`10` leftChannel.currentLevel = 7
print(leftChannel.currentLevel)
```swift // 输出 "7"
rightChannel.currentLevel = 11 print(AudioChannel.maxInputLevelForAllChannels)
print(rightChannel.currentLevel) // 输出 "7"
// 输出 "10" ```
print(AudioChannel.maxInputLevelForAllChannels)
// 输出 "10" 如果试图将右声道的 `currentLevel` 设置成 `11`,它会被修正到最大值 `10`,同时 `maxInputLevelForAllChannels` 的值也会更新到 `10`
```
```swift
rightChannel.currentLevel = 11
print(rightChannel.currentLevel)
// 输出 "10"
print(AudioChannel.maxInputLevelForAllChannels)
// 输出 "10"
```

View File

@ -9,7 +9,10 @@
> 翻译+校对:[DianQK](https://github.com/DianQK) > 翻译+校对:[DianQK](https://github.com/DianQK)
> 2.1 > 2.1
> 翻译:[DianQK](https://github.com/DianQK)[Realank](https://github.com/Realank) 校对:[shanks](http://codebuild.me)2016-01-18 > 翻译:[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
本页包含内容: 本页包含内容:
@ -33,7 +36,7 @@
class Counter { class Counter {
var count = 0 var count = 0
func increment() { func increment() {
++count count += 1
} }
func incrementBy(amount: Int) { func incrementBy(amount: Int) {
count += amount count += amount
@ -112,7 +115,7 @@ counter.incrementBy(5, numberOfTimes: 3)
```swift ```swift
func increment() { func increment() {
self.count++ self.count += 1
} }
``` ```
@ -224,7 +227,7 @@ ovenLight.next()
```swift ```swift
class SomeClass { class SomeClass {
static func someTypeMethod() { class func someTypeMethod() {
// type method implementation goes here // type method implementation goes here
} }
} }

View File

@ -9,7 +9,10 @@
> 翻译+校对:[shanks](http://codebuild.me) > 翻译+校对:[shanks](http://codebuild.me)
> 2.1 > 2.1
> 翻译+校对:[shanks](http://codebuild.me)[Realank](https://github.com/Realank) > 翻译+校对:[shanks](http://codebuild.me)[Realank](https://github.com/Realank)
> 2.2
> 校对:[SketchK](https://github.com/SketchK) 2016-05-13
本页包含内容: 本页包含内容:

View File

@ -6,11 +6,14 @@
> 校对:[menlongsheng](https://github.com/menlongsheng) > 校对:[menlongsheng](https://github.com/menlongsheng)
> 2.02.1 > 2.02.1
> 翻译+校对:[shanks](http://codebuild.me) > 翻译+校对:[shanks](http://codebuild.me)
>
> 2.2
> 校对:[SketchK](https://github.com/SketchK) 2016-05-13
本页包含内容: 本页包含内容:
- [定义一个基类Base class](#defining_a_base_class) - [定义一个基类(Defining a Base Class](#defining_a_base_class)
- [子类生成Subclassing](#subclassing) - [子类生成Subclassing](#subclassing)
- [重写Overriding](#overriding) - [重写Overriding](#overriding)
- [防止重写Preventing Overrides](#preventing_overrides) - [防止重写Preventing Overrides](#preventing_overrides)
@ -22,7 +25,7 @@
可以为类中继承来的属性添加属性观察器property observers这样一来当属性值改变时类就会被通知到。可以为任何属性添加属性观察器无论它原本被定义为存储型属性stored property还是计算型属性computed property 可以为类中继承来的属性添加属性观察器property observers这样一来当属性值改变时类就会被通知到。可以为任何属性添加属性观察器无论它原本被定义为存储型属性stored property还是计算型属性computed property
<a name="defining_a_base_class"></a> <a name="defining_a_base_class"></a>
## 定义一个基类Base class ## 定义一个基类(Defining a Base Class
不继承于其它类的类,称之为*基类base class*。 不继承于其它类的类,称之为*基类base class*。
@ -233,6 +236,6 @@ print("AutomaticCar: \(automatic.description)")
你可以通过把方法,属性或下标标记为*`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

@ -12,6 +12,10 @@
> 翻译:[Channe](https://github.com/Channe)[Realank](https://github.com/Realank) > 翻译:[Channe](https://github.com/Channe)[Realank](https://github.com/Realank)
> 校对:[shanks](http://codebuild.me)2016-1-23 > 校对:[shanks](http://codebuild.me)2016-1-23
> 2.2
> 翻译:[pmst](https://github.com/colourful987)
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-14
本页包含内容: 本页包含内容:
- [存储属性的初始赋值](#setting_initial_values_for_stored_properties) - [存储属性的初始赋值](#setting_initial_values_for_stored_properties)
@ -277,9 +281,9 @@ let twoByTwo = Size(width: 2.0, height: 2.0)
构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给自己的其它构造器。类则不同,它可以继承自其它类(请参考[继承](./13_Inheritance.html)),这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。这些责任将在后续章节[类的继承和构造过程](#class_inheritance_and_initialization)中介绍。 构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给自己的其它构造器。类则不同,它可以继承自其它类(请参考[继承](./13_Inheritance.html)),这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。这些责任将在后续章节[类的继承和构造过程](#class_inheritance_and_initialization)中介绍。
对于值类型,你可以使用`self.init`在自定义的构造器中引用类型中的其它构造器。并且你只能在构造器内部调用`self.init` 对于值类型,你可以使用`self.init`在自定义的构造器中引用相同类型中的其它构造器。并且你只能在构造器内部调用`self.init`
如果你为某个值类型定义了一个自定义的构造器,你将无法访问到默认构造器(如果是结构体,还将无法访问逐一成员构造器)。这限制可以防止你为值类型定义了一个进行额外必要设置的复杂构造器之后,别人还是错误使用了一个自动生成的构造器 如果你为某个值类型定义了一个自定义的构造器,你将无法访问到默认构造器(如果是结构体,还将无法访问逐一成员构造器)。这限制可以防止你为值类型增加了一个额外的且十分复杂构造器之后,仍然有人错误使用自动生成的构造器
> 注意 > 注意
假如你希望默认构造器、逐一成员构造器以及你自己的自定义构造器都能用来创建实例,可以将自定义的构造器写到扩展(`extension`)中,而不是写在值类型的原始定义中。想查看更多内容,请查看[扩展](./21_Extensions.html)章节。 假如你希望默认构造器、逐一成员构造器以及你自己的自定义构造器都能用来创建实例,可以将自定义的构造器写到扩展(`extension`)中,而不是写在值类型的原始定义中。想查看更多内容,请查看[扩展](./21_Extensions.html)章节。
@ -486,7 +490,7 @@ Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造
<a name="initializer_inheritance_and_overriding"></a> <a name="initializer_inheritance_and_overriding"></a>
### 构造器的继承和重写 ### 构造器的继承和重写
跟 Objective-C 中的子类不同Swift 中的子类默认情况下不会继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更专业的子类继承,并被错误地用来创建子类的实例。 跟 Objective-C 中的子类不同Swift 中的子类默认情况下不会继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更精细的子类继承,并被错误地用来创建子类的实例。
> 注意 > 注意
父类的构造器仅会在安全和适当的情况下被继承。具体内容请参考后续章节[构造器的自动继承](#automatic_initializer_inheritance)。 父类的构造器仅会在安全和适当的情况下被继承。具体内容请参考后续章节[构造器的自动继承](#automatic_initializer_inheritance)。
@ -606,7 +610,7 @@ let mysteryMeat = Food()
// mysteryMeat 的名字是 [Unnamed] // mysteryMeat 的名字是 [Unnamed]
``` ```
类层级中的第二个类是`Food`的子类`RecipeIngredient``RecipeIngredient`构建了食谱中的一味调味剂。它引入了`Int`类型的属性`quantity`(以及从`Food`继承过来的`name`属性),并且定义了两个构造器来创建`RecipeIngredient`实例: 类层级中的第二个类是`Food`的子类`RecipeIngredient``RecipeIngredient`用来表示食谱中的一项原料。它引入了`Int`类型的属性`quantity`(以及从`Food`继承过来的`name`属性),并且定义了两个构造器来创建`RecipeIngredient`实例:
```swift ```swift
class RecipeIngredient: Food { class RecipeIngredient: Food {
@ -643,7 +647,7 @@ let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6) let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
``` ```
类层级中第三个也是最后一个类是`RecipeIngredient`的子类,叫做`ShoppingListItem`。这个类构建了购物单中出现的某一种调味料。 类层级中第三个也是最后一个类是`RecipeIngredient`的子类,叫做`ShoppingListItem`。这个类构建了购物单中出现的某一种食谱原料。
购物单中的每一项总是从未购买状态开始的。为了呈现这一事实,`ShoppingListItem`引入了一个布尔类型的属性`purchased`,它的默认值是`false``ShoppingListItem`还添加了一个计算型属性`description`,它提供了关于`ShoppingListItem`实例的一些文字描述: 购物单中的每一项总是从未购买状态开始的。为了呈现这一事实,`ShoppingListItem`引入了一个布尔类型的属性`purchased`,它的默认值是`false``ShoppingListItem`还添加了一个计算型属性`description`,它提供了关于`ShoppingListItem`实例的一些文字描述:
@ -807,46 +811,6 @@ if unknownUnit == nil {
// 打印 "This is not a defined temperature unit, so initialization failed." // 打印 "This is not a defined temperature unit, so initialization failed."
``` ```
<a name="failable_initializers_for_classes"></a>
### 类的可失败构造器
值类型(也就是结构体或枚举)的可失败构造器,可以在构造过程中的任意时间点触发构造失败。比如在前面的例子中,结构体`Animal`的可失败构造器在构造过程一开始就触发了构造失败,甚至在`species`属性被初始化前。
而对类而言,可失败构造器只能在类引入的所有存储型属性被初始化后,以及构造器代理调用完成后,才能触发构造失败。
下面例子展示了如何在类的可失败构造器中使用隐式解包可选类型来满足上述要求:
```swift
class Product {
let name: String!
init?(name: String) {
self.name = name
if name.isEmpty { return nil }
}
}
```
上面定义的`Product`类和之前的`Animal`结构体很相似。`Product`类有一个不能为空字符串的常量属性`name`。为了强制这个要求,`Product`类使用了可失败构造器确保这个属性的值不是空字符串后,才允许构造成功。
毕竟,`Product`是一个类而不是结构体,这意味着不同于`Animal``Product`类的所有可失败构造器必须给`name`属性一个初始值,然后才能触发构造失败。
上面的例子中,`Product`类的`name`属性被定义为隐式解包可选字符串类型(`String!`)。因为它是一个可选类型,所以它在构造过程中被赋值前,具有默认值`nil`。这个默认值`nil`意味着`Product`类引入的所有存储型属性都有一个有效的初始值。因此,一旦传入一个空字符串,该可失败构造器可以在`name`属性被赋值前触发构造失败。
> 译者注
> 上面的示例代码和描述并不相符,根据描述,`if name.isEmpty { return nil }`这句代码应该在`self.name = name`之前,而这却会导致编译错误`error: all stored properties of a class instance must be initialized before returning nil from an initializer`,除非将`let name: String!`改为`var name: String!`。
>
> 根据前面的介绍和实测,常量存储属性,只能被初始化一次。上面的代码,在构造器中有初始化常量属性`name`的过程,所以在此之前不会被赋予默认值`nil`,而在构造器的初始化`name`过程之前,因为并不是所有存储型属性都被初始化,是不可以返回`nil`的;而如果将`name`声明为变量存储属性,那么就可以被多次赋值,所以在执行构造器方法之前,就会获得默认值`nil`,和构造器中的再次赋值并不冲突,所以此时,`if name.isEmpty { return nil }`才可以放在构造器前面。英文原文在这里表述有误
因为`name`属性是一个常量,所以一旦构造成功,`name`属性肯定有一个非`nil`的值。即使它被定义为隐式解包可选类型,也完全可以放心大胆地直接访问,而不用检查`name`属性的值是否为`nil`
```swift
if let bowTie = Product(name: "bow tie") {
// 不需要检查 bowTie.name 是否为 nil
print("The product's name is \(bowTie.name)")
}
// 打印 "The product's name is bow tie"
```
<a name="propagation_of_initialization_failure"></a> <a name="propagation_of_initialization_failure"></a>
### 构造失败的传递 ### 构造失败的传递
@ -859,36 +823,38 @@ if let bowTie = Product(name: "bow tie") {
下面这个例子,定义了一个名为`CartItem``Product`类的子类。这个类建立了一个在线购物车中的物品的模型,它有一个名为`quantity`的常量存储型属性,并确保该属性的值至少为`1` 下面这个例子,定义了一个名为`CartItem``Product`类的子类。这个类建立了一个在线购物车中的物品的模型,它有一个名为`quantity`的常量存储型属性,并确保该属性的值至少为`1`
```swift ```swift
class Product {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
class CartItem: Product { class CartItem: Product {
let quantity: Int! let quantity: Int
init?(name: String, quantity: Int) { init?(name: String, quantity: Int) {
if quantity < 1 { return nil }
self.quantity = quantity self.quantity = quantity
super.init(name: name) super.init(name: name)
if quantity < 1 { return nil }
} }
} }
``` ```
`Product`类中的`name`属性类似,`CartItem`类中的`quantity`属性也是隐式解包可选类型。这意味着在构造过程中,该属性在被赋予特定的值之前能有一个默认的初始值`nil` `CartItem` 可失败构造器首先验证接收的 `quantity` 值是否大于等于 1 。倘若 `quantity` 值无效,则立即终止整个构造过程,返回失败结果,且不再执行余下代码。同样地,`Product` 的可失败构造器首先检查 `name` 值,假如 `name` 值为空字符串,则构造器立即执行失败
该可失败构造器以向上代理到父类的可失败构造器`init(name:)`开始。这满足了可失败构造器在触发构造失败前必须总是完成构造器代理调用这个条件。 如果你通过传入一个非空字符串 `name` 以及一个值大于等于 1 的 `quantity` 来创建一个 `CartItem` 实例,那么构造方法能够成功被执行:
如果由于`name`的值为空字符串而导致父类的可失败构造器构造失败,则`CartIem`类的整个构造过程都将立即失败,之后的构造代码将不会再被执行。如果父类构造成功,`CartIem`的可失败构造器会进一步验证`quantity`的值是否不小于`1`
> 译者注
> 上面的示例代码和描述也不相符,`name`属性在被赋予特定的值之前不能获得一个默认的初始值`nil`,并且根据描述,`self.quantity = quantity`这句代码应该放在最后一行,而这却会导致编译错误`error: property 'self.quantity' not initialized at super.init call`,除非将`let quantity: Int!`改为`var quantity: Int!`。
如果你构造一个`name`的值为非空字符串,`quantity`的值不小于`1``CartItem`实例,则可成功构造:
```swift ```swift
if let twoSocks = CartItem(name: "sock", quantity: 2) { if let twoSocks = CartItem(name: "sock", quantity: 2) {
print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)") print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
} }
// 打印 "Item: sock, quantity: 2" // 打印 "Item: sock, quantity: 2
``` ```
如果你试图构造一个`quantity`的值为`0``CartItem`实例, 则`CartItem`的可失败构造器会触发构造失败: 倘若你以一个值为 0 的 `quantity` 来创建一个 `CartItem` 实例,那么将导致 `CartItem` 构造器失败:
```swift ```swift
if let zeroShirts = CartItem(name: "shirt", quantity: 0) { if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
@ -896,10 +862,10 @@ if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
} else { } else {
print("Unable to initialize zero shirts") print("Unable to initialize zero shirts")
} }
// 打印 "Unable to initialize zero shirts" // 打印 "Unable to initialize zero shirts
``` ```
类似的,如果你试图构造一个`name`的值为空字符串的`CartItem`实例,则父类`Product`的可失败构造器会触发构造失败: 同样地,如果你尝试传入一个值为空字符串的 `name`来创建一个 `CartItem` 实例,那么将导致父类 `Product` 的构造过程失败:
```swift ```swift
if let oneUnnamed = CartItem(name: "", quantity: 1) { if let oneUnnamed = CartItem(name: "", quantity: 1) {
@ -907,7 +873,7 @@ if let oneUnnamed = CartItem(name: "", quantity: 1) {
} else { } else {
print("Unable to initialize one unnamed product") print("Unable to initialize one unnamed product")
} }
// 打印 "Unable to initialize one unnamed product" // 打印 "Unable to initialize one unnamed product
``` ```
<a name="overriding_a_failable_initializer"></a> <a name="overriding_a_failable_initializer"></a>
@ -971,7 +937,7 @@ class UntitledDocument: Document {
<a name="the_init!_failable_initializer"></a> <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!`,不过,一旦`init!`构造失败,则会触发一个断言。
@ -1027,7 +993,7 @@ class SomeClass {
下面例子中定义了一个结构体`Checkerboard`,它构建了西洋跳棋游戏的棋盘: 下面例子中定义了一个结构体`Checkerboard`,它构建了西洋跳棋游戏的棋盘:
![西洋跳棋棋盘](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/checkersBoard_2x.png) ![西洋跳棋棋盘](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/chessBoard_2x.png)
西洋跳棋游戏在一副黑白格交替的`10x10`的棋盘中进行。为了呈现这副游戏棋盘,`Checkerboard`结构体定义了一个属性`boardColors`,它是一个包含`100``Bool`值的数组。在数组中,值为`true`的元素表示一个黑格,值为`false`的元素表示一个白格。数组中第一个元素代表棋盘上左上角的格子,最后一个元素代表棋盘上右下角的格子。 西洋跳棋游戏在一副黑白格交替的`10x10`的棋盘中进行。为了呈现这副游戏棋盘,`Checkerboard`结构体定义了一个属性`boardColors`,它是一个包含`100``Bool`值的数组。在数组中,值为`true`的元素表示一个黑格,值为`false`的元素表示一个白格。数组中第一个元素代表棋盘上左上角的格子,最后一个元素代表棋盘上右下角的格子。
@ -1038,8 +1004,8 @@ struct Checkerboard {
let boardColors: [Bool] = { let boardColors: [Bool] = {
var temporaryBoard = [Bool]() var temporaryBoard = [Bool]()
var isBlack = false var isBlack = false
for i in 1...10 { for i in 1...8 {
for j in 1...10 { for j in 1...8 {
temporaryBoard.append(isBlack) temporaryBoard.append(isBlack)
isBlack = !isBlack isBlack = !isBlack
} }
@ -1048,7 +1014,7 @@ struct Checkerboard {
return temporaryBoard return temporaryBoard
}() }()
func squareIsBlackAtRow(row: Int, column: Int) -> Bool { func squareIsBlackAtRow(row: Int, column: Int) -> Bool {
return boardColors[(row * 10) + column] return boardColors[(row * 8) + column]
} }
} }
``` ```
@ -1059,6 +1025,6 @@ struct Checkerboard {
let board = Checkerboard() let board = Checkerboard()
print(board.squareIsBlackAtRow(0, column: 1)) print(board.squareIsBlackAtRow(0, column: 1))
// 打印 "true" // 打印 "true"
print(board.squareIsBlackAtRow(9, column: 9)) print(board.squareIsBlackAtRow(7, column: 7))
// 打印 "false" // 打印 "false"
``` ```

View File

@ -10,6 +10,9 @@
> 2.1 > 2.1
> 校对:[shanks](http://codebuild.me)2015-10-31 > 校对:[shanks](http://codebuild.me)2015-10-31
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-14
本页包含内容: 本页包含内容:
@ -38,13 +41,13 @@ deinit {
<a name="deinitializers_in_action"></a> <a name="deinitializers_in_action"></a>
##析构器实践 ##析构器实践
这是一个析构器实践的例子。这个例子描述了一个简单的游戏,这里定义了两种新类型,分别是`Bank``Player``Bank`类管理一种虚拟硬币,确保流通的硬币数量永远不可能超过 10,000。在游戏中有且只能有一个`Bank`存在,因此`Bank`用类来实现,并使用静态属性和静态方法来存储和管理其当前状态。 这是一个析构器实践的例子。这个例子描述了一个简单的游戏,这里定义了两种新类型,分别是`Bank``Player``Bank`类管理一种虚拟硬币,确保流通的硬币数量永远不可能超过 10,000。在游戏中有且只能有一个`Bank`存在,因此`Bank`用类来实现,并使用类型属性和类型方法来存储和管理其当前状态。
```swift ```swift
class Bank { class Bank {
static var coinsInBank = 10_000 static var coinsInBank = 10_000
static func vendCoins(var numberOfCoinsToVend: Int) -> Int { static func vendCoins(numberOfCoinsRequested: Int) -> Int {
numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank) let numberOfCoinsToVend = min(numberOfCoinsRequested, coinsInBank)
coinsInBank -= numberOfCoinsToVend coinsInBank -= numberOfCoinsToVend
return numberOfCoinsToVend return numberOfCoinsToVend
} }
@ -56,7 +59,7 @@ class Bank {
`Bank`使用`coinsInBank`属性来跟踪它当前拥有的硬币数量。`Bank`还提供了两个方法,`vendCoins(_:)``receiveCoins(_:)`,分别用来处理硬币的分发和收集。 `Bank`使用`coinsInBank`属性来跟踪它当前拥有的硬币数量。`Bank`还提供了两个方法,`vendCoins(_:)``receiveCoins(_:)`,分别用来处理硬币的分发和收集。
`vendCoins(_:)`方法在`Bank`对象分发硬币之前检查是否有足够的硬币。如果硬币不足,`Bank`对象会返回一个比请求时小的数字(如果`Bank`对象中没有硬币了就返回`0`)。`vendCoins`方法声明`numberOfCoinsToVend`为一个变量参数,这样就可以在方法体内部修改分发的硬币数量,而不需要定义一个新的变量。`vendCoins`方法返回一个整型值,表示提供的硬币的实际数量。 `vendCoins(_:)`方法在`Bank`对象分发硬币之前检查是否有足够的硬币。如果硬币不足,`Bank`对象会返回一个比请求时小的数字(如果`Bank`对象中没有硬币了就返回`0`)。`vendCoins`方法返回一个整型值,表示提供的硬币的实际数量。
`receiveCoins(_:)`方法只是将`Bank`对象接收到的硬币数目加回硬币存储中。 `receiveCoins(_:)`方法只是将`Bank`对象接收到的硬币数目加回硬币存储中。

View File

@ -10,7 +10,10 @@
> 2.1 > 2.1
> 翻译:[Channe](https://github.com/Channe) > 翻译:[Channe](https://github.com/Channe)
> 校对:[shanks](http://codebuild.me)[Realank](https://github.com/Realank) 2016-01-23 > 校对:[shanks](http://codebuild.me)[Realank](https://github.com/Realank) 2016-01-23
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-14
本页包含内容: 本页包含内容:

View File

@ -10,7 +10,10 @@
> 翻译+校对:[lyojo](https://github.com/lyojo) > 翻译+校对:[lyojo](https://github.com/lyojo)
> 2.1 > 2.1
> 校对:[shanks](http://codebuild.me)2015-10-31 > 校对:[shanks](http://codebuild.me)2015-10-31
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-15
本页包含内容: 本页包含内容:
@ -50,9 +53,9 @@ class Residence {
} }
``` ```
`Residence`有一个`Int`类型的属性`numberOfRooms`,其默认值为`1``Person`具有一个可选的`residence`属性,其类型为`Residence?` `Residence`有一个`Int`类型的属性`numberOfRooms`,其默认值为`1``Person`具有一个可选的`residence`属性,其类型为`Residence?`
如果创建一个新的`Person`实例,因为它的`residence`属性是可选的,`john`属性将初始化`nil` 假如你创建一个新的`Person`实例,它的`residence`属性由于是是可选型而将初始化为`nil`,在下面的代码中,`john`有一个值`nil``residence`属性
```swift ```swift
let john = Person() let john = Person()
@ -314,7 +317,7 @@ if let firstRoomName = john.residence?[0].name {
```swift ```swift
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]] var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91 testScores["Dave"]?[0] = 91
testScores["Bev"]?[0]++ testScores["Bev"]?[0] += 1
testScores["Brian"]?[0] = 72 testScores["Brian"]?[0] = 72
// "Dave" 数组现在是 [91, 82, 84]"Bev" 数组现在是 [80, 94, 81] // "Dave" 数组现在是 [91, 82, 84]"Bev" 数组现在是 [80, 94, 81]
``` ```

View File

@ -3,7 +3,10 @@
> 2.1 > 2.1
> 翻译+校对:[lyojo](https://github.com/lyojo) [ray16897188](https://github.com/ray16897188) 2015-10-23 > 翻译+校对:[lyojo](https://github.com/lyojo) [ray16897188](https://github.com/ray16897188) 2015-10-23
> 校对:[shanks](http://codebuild.me) 2015-10-24 > 校对:[shanks](http://codebuild.me) 2015-10-24
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-15
本页包含内容: 本页包含内容:
@ -18,7 +21,7 @@
举个例子,假如有个从磁盘上的某个文件读取数据并进行处理的任务,该任务会有多种可能失败的情况,包括指定路径下文件并不存在,文件不具有可读权限,或者文件编码格式不兼容。区分这些不同的失败情况可以让程序解决并处理某些错误,然后把它解决不了的错误报告给用户。 举个例子,假如有个从磁盘上的某个文件读取数据并进行处理的任务,该任务会有多种可能失败的情况,包括指定路径下文件并不存在,文件不具有可读权限,或者文件编码格式不兼容。区分这些不同的失败情况可以让程序解决并处理某些错误,然后把它解决不了的错误报告给用户。
> 注意 > 注意
Swift 中的错误处理涉及到错误处理模式,这会用到 Cocoa 和 Objective-C 中的`NSError`。关于这个类的更多信息请参见 [Using Swift with Cocoa and Objective-C (Swift 2.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 2.2)](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> <a name="representing_and_throwing_errors"></a>
##表示并抛出错误 ##表示并抛出错误
@ -35,7 +38,7 @@ enum VendingMachineError: ErrorType {
} }
``` ```
抛出一个错误可以让你表明有意外情况发生,导致正常的执行流程无法继续执行。抛出错误使用`throws`关键字。例如,下面的代码抛出一个错误,提示贩卖机还需要`5`个硬币: 抛出一个错误可以让你表明有意外情况发生,导致正常的执行流程无法继续执行。抛出错误使用`throw`关键字。例如,下面的代码抛出一个错误,提示贩卖机还需要`5`个硬币:
```swift ```swift
throw VendingMachineError.InsufficientFunds(coinsNeeded: 5) throw VendingMachineError.InsufficientFunds(coinsNeeded: 5)
@ -68,7 +71,7 @@ func cannotThrowErrors() -> String
> 注意 > 注意
只有 throwing 函数可以传递错误。任何在某个非 throwing 函数内部抛出的错误只能在函数内部处理。 只有 throwing 函数可以传递错误。任何在某个非 throwing 函数内部抛出的错误只能在函数内部处理。
下面的例子中,`VendingMechine`类有一个`vend(itemNamed:)`方法,如果请求的物品不存在、缺货或者花费超过了投入金额,该方法就会抛出一个相应的`VendingMachineError` 下面的例子中,`VendingMechine`类有一个`vend(itemNamed:)`方法,如果请求的物品不存在、缺货或者投入金额小于物品价格,该方法就会抛出一个相应的`VendingMachineError`
```swift ```swift
struct Item { struct Item {
@ -88,7 +91,7 @@ class VendingMachine {
} }
func vend(itemNamed name: String) throws { func vend(itemNamed name: String) throws {
guard var item = inventory[name] else { guard let item = inventory[name] else {
throw VendingMachineError.InvalidSelection throw VendingMachineError.InvalidSelection
} }
@ -101,8 +104,11 @@ class VendingMachine {
} }
coinsDeposited -= item.price coinsDeposited -= item.price
--item.count
inventory[name] = item var newItem = item
newItem.count -= 1
inventory[name] = newItem
dispenseSnack(name) dispenseSnack(name)
} }
} }
@ -124,7 +130,20 @@ func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
} }
``` ```
上例中,`buyFavoriteSnack(_:vendingMachine:)`函数会查找某人最喜欢的零食,并通过调用`vend(itemNamed:)`方法来尝试为他们购买。因为`vend(itemNamed:)`方法能抛出错误,所以在调用的它时候在它前面加了`try`关键字。 上例中,`buyFavoriteSnack(_:vendingMachine:)`函数会查找某人最喜欢的零食,并通过调用`vend(itemNamed:)`方法来尝试为他们购买。因为`vend(itemNamed:)`方法能抛出错误,所以在调用的它时候在它前面加了`try`关键字。
throwing构造器能像throwing函数一样传递错误.例如下面代码中的`PurchasedSnack`构造器在构造过程中调用了throwing函数,并且通过传递到它的调用者来处理这些错误。
```swift
struct PurchasedSnack {
let name: String
init(name: String, vendingMachine: VendingMachine) throws {
try vendingMachine.vend(itemNamed: name)
self.name = name
}
}
```
###用 Do-Catch 处理错误 ###用 Do-Catch 处理错误
@ -166,7 +185,7 @@ do {
###将错误转换成可选值 ###将错误转换成可选值
可以使用`try?`通过将错误转换成一个可选值来处理错误。如果在评估`try?`表达式时一个错误被抛出,那么表达式的值就是`nil`。例如下面代码中`x``y`有相同的 可以使用`try?`通过将错误转换成一个可选值来处理错误。如果在评估`try?`表达式时一个错误被抛出,那么表达式的值就是`nil`。例如,在下面代码中,`x``y`相同的数值和等价的含义
```swift ```swift
func someThrowingFunction() throws -> Int { func someThrowingFunction() throws -> Int {
@ -197,7 +216,7 @@ func fetchData() -> Data? {
### 禁用错误传递 ### 禁用错误传递
有时你知道某个 throwing 函数实际上在运行时是不会抛出错误的,在这种情况下,你可以在表达式前面写`try!`来禁用错误传递,这会把调用包装在一个断言不会有错误抛出的运行时断言中。如果实际上抛出了错误,你会得到一个运行时错误。 有时你知道某个 throwing 函数实际上在运行时是不会抛出错误的,在这种情况下,你可以在表达式前面写`try!`来禁用错误传递,这会把调用包装在一个不会有错误抛出的运行时断言中。如果真的抛出了错误,你会得到一个运行时错误。
例如,下面的代码使用了`loadImage(_:)`函数,该函数从给定的路径加载图片资源,如果图片无法载入则抛出一个错误。在这种情况下,因为图片是和应用绑定的,运行时不会有错误抛出,所以适合禁用错误传递: 例如,下面的代码使用了`loadImage(_:)`函数,该函数从给定的路径加载图片资源,如果图片无法载入则抛出一个错误。在这种情况下,因为图片是和应用绑定的,运行时不会有错误抛出,所以适合禁用错误传递:

View File

@ -10,6 +10,9 @@
> 2.1 > 2.1
> 校对:[shanks](http://codebuild.me)2015-11-01 > 校对:[shanks](http://codebuild.me)2015-11-01
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-16
本页包含内容: 本页包含内容:
@ -89,9 +92,9 @@ var songCount = 0
for item in library { for item in library {
if item is Movie { if item is Movie {
++movieCount movieCount += 1
} else if item is Song { } else if item is Song {
++songCount songCount += 1
} }
} }
@ -164,12 +167,10 @@ Swift 为不确定类型提供了两种特殊的类型别名:
<a name="anyobject"></a> <a name="anyobject"></a>
### `AnyObject` 类型 ### `AnyObject` 类型
在工作中使用 Cocoa APIs 时,我们经常会接收到一个 `[AnyObject]` 类型的数组,或者说“一个任意类型对象的数组”。这是因为 Objective-C 没有明确的类型化数组。但是,你常常可以从 API 提供的信息来确定数组中对象的类型。 我们使用 Cocoa APIs 时,我们会接收到一个 `[AnyObject]` 类型的数组或者说“一个任意类型对象的数组”。Objective-C现在支持明确的数组类型但早期版本的Objective-C并没有这个功能。不管怎样你都可以确信API提供的信息能够正确的表明数组中的元素类型。
> 译者注
> 这段文档似乎没有及时更新,从 Xcode 7 和 Swift 2.0 开始,由于 Objective-C 引入了轻量泛型,集合类型已经可以类型化了,在 Swift 中使用 Cocoa API 也越来越少遇到 `AnyObject` 类型了。详情请参阅 [Lightweight Generics](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-ID173) 和 [Collection Classes](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6-ID69)。
在这些情况下,你可以使用强制形式的类型转换(`as`)来下转数组中的每一项到比 `AnyObject` 更明确的类型不需要可选解包optional unwrapping 在这些情况下,你可以使用强制形式的类型转换(`as!`)来下转数组中的每一项到比 `AnyObject` 更明确的类型不需要可选解包optional unwrapping
下面的示例定义了一个 `[AnyObject]` 类型的数组并填入三个 `Movie` 类型的实例: 下面的示例定义了一个 `[AnyObject]` 类型的数组并填入三个 `Movie` 类型的实例:

View File

@ -9,14 +9,17 @@
> 翻译+校对:[SergioChan](https://github.com/SergioChan) > 翻译+校对:[SergioChan](https://github.com/SergioChan)
> 2.1 > 2.1
> 校对:[shanks](http://codebuild.me)2015-11-01 > 校对:[shanks](http://codebuild.me)2015-11-01
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-16
本页包含内容: 本页包含内容:
- [嵌套类型实践](#nested_types_in_action) - [嵌套类型实践](#nested_types_in_action)
- [引用嵌套类型](#referring_to_nested_types) - [引用嵌套类型](#referring_to_nested_types)
枚举常被用于为特定类或结构体实现某些功能。类似的,也能够在某个复杂的类型中,方便定义工具类或结构体使用。为了实现这种功能Swift 允许你定义嵌套类型,可以在支持的类型中定义嵌套的枚举、类和结构体。 枚举常被用于为特定类或结构体实现某些功能。类似的,枚举可以方便定义工具类或结构体,从而为某个复杂的类型所使用。为了实现这种功能Swift 允许你定义嵌套类型,可以在支持的类型中定义嵌套的枚举、类和结构体。
要在一个类型中嵌套另一个类型,将嵌套类型的定义写在其外部类型的`{}`内,而且可以根据需要定义多级嵌套。 要在一个类型中嵌套另一个类型,将嵌套类型的定义写在其外部类型的`{}`内,而且可以根据需要定义多级嵌套。

View File

@ -10,6 +10,9 @@
> 2.1 > 2.1
> 校对:[shanks](http://codebuild.me) > 校对:[shanks](http://codebuild.me)
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-16
本页包含内容: 本页包含内容:
@ -106,7 +109,7 @@ print("A marathon is \(aMarathon) meters long")
扩展能为类添加新的便利构造器,但是它们不能为类添加新的指定构造器或析构器。指定构造器和析构器必须总是由原始的类实现来提供。 扩展能为类添加新的便利构造器,但是它们不能为类添加新的指定构造器或析构器。指定构造器和析构器必须总是由原始的类实现来提供。
> 注意 > 注意
如果你使用扩展为一个值类型添加构造器,该值类型的原始实现中未定义任何定制的构造器时,你可以在扩展中的构造器里调用逐一成员构造器。如果该值类型为所有存储属性提供了默认值,你还可以在扩展中的构造器里调用默认构造器。 如果你使用扩展为一个值类型添加构造器,同时该值类型的原始实现中未定义任何定制的构造器所有存储属性提供了默认值,那么我们就可以在扩展中的构造器里调用默认构造器和逐一成员构造器
正如在[值类型的构造器代理](./14_Initialization.html#initializer_delegation_for_value_types)中描述的,如果你把定制的构造器写在值类型的原始实现中,上述规则将不再适用。 正如在[值类型的构造器代理](./14_Initialization.html#initializer_delegation_for_value_types)中描述的,如果你把定制的构造器写在值类型的原始实现中,上述规则将不再适用。
下面的例子定义了一个用于描述几何矩形的结构体 `Rect`。这个例子同时定义了两个辅助结构体 `Size``Point`,它们都把 `0.0` 作为所有属性的默认值: 下面的例子定义了一个用于描述几何矩形的结构体 `Rect`。这个例子同时定义了两个辅助结构体 `Size``Point`,它们都把 `0.0` 作为所有属性的默认值:
@ -223,11 +226,10 @@ someInt.square()
```swift ```swift
extension Int { extension Int {
subscript(var digitIndex: Int) -> Int { subscript(digitIndex: Int) -> Int {
var decimalBase = 1 var decimalBase = 1
while digitIndex > 0 { for _ in 0..<digitIndex {
decimalBase *= 10 decimalBase *= 10
--digitIndex
} }
return (self / decimalBase) % 10 return (self / decimalBase) % 10
} }
@ -295,7 +297,7 @@ func printIntegerKinds(numbers: [Int]) {
print("") print("")
} }
printIntegerKinds([3, 19, -27, 0, -6, 0, 7]) printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
// 打印 “+ + - 0 - 0 +” // 打印 “+ + - 0 - 0 +
``` ```
函数 `printIntegerKinds(_:)` 接受一个 `Int` 数组,然后对该数组进行迭代。在每次迭代过程中,对当前整数的计算型属性 `kind` 的值进行评估,并打印出适当的描述。 函数 `printIntegerKinds(_:)` 接受一个 `Int` 数组,然后对该数组进行迭代。在每次迭代过程中,对当前整数的计算型属性 `kind` 的值进行评估,并打印出适当的描述。

View File

@ -11,6 +11,9 @@
> 2.1 > 2.1
> 翻译:[小铁匠Linus](https://github.com/kevin833752) > 翻译:[小铁匠Linus](https://github.com/kevin833752)
> 校对:[shanks](http://codebuild.me)2015-11-01 > 校对:[shanks](http://codebuild.me)2015-11-01
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-16
本页包含内容: 本页包含内容:
@ -65,11 +68,11 @@ class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
<a name="property_requirements"></a> <a name="property_requirements"></a>
## 属性要求 ## 属性要求
协议可以要求采纳协议的类型提供特定名称和类型的实例属性或类型属性。协议不指定属性是存储型属性还是计算型属性,它只指定属性的名称和类型。此外,协议还指定属性是读的还是可读可写的。 协议可以要求采纳协议的类型提供特定名称和类型的实例属性或类型属性。协议不指定属性是存储型属性还是计算型属性,它只指定属性的名称和类型。此外,协议还指定属性是读的还是可读可写的。
如果协议要求属性是可读可写的,那么该属性不能是常量属性或只读的计算型属性。如果协议只要求属性是读的,那么该属性不仅可以是读的,如果代码需要的话,还可以是可写的。 如果协议要求属性是可读可写的,那么该属性不能是常量属性或只读的计算型属性。如果协议只要求属性是读的,那么该属性不仅可以是读的,如果代码需要的话,还可以是可写的。
协议通常`var` 关键字来声明变量属性,在类型声明后加上 `{ set get }` 来表示属性是可读可写的,读属性则用 `{ get }` 来表示: 协议总是`var` 关键字来声明变量属性,在类型声明后加上 `{ set get }` 来表示属性是可读可写的,读属性则用 `{ get }` 来表示:
```swift ```swift
protocol SomeProtocol { protocol SomeProtocol {
@ -94,7 +97,7 @@ protocol FullyNamed {
} }
``` ```
`FullyNamed` 协议除了要求采纳协议的类型提供 `fullName` 属性外,并没有其他特别的要求。这个协议表示,任何采纳 `FullyNamed` 的类型,都必须有一个读的 `String` 类型的实例属性 `fullName` `FullyNamed` 协议除了要求采纳协议的类型提供 `fullName` 属性外,并没有其他特别的要求。这个协议表示,任何采纳 `FullyNamed` 的类型,都必须有一个读的 `String` 类型的实例属性 `fullName`
下面是一个采纳 `FullyNamed` 协议的简单结构体: 下面是一个采纳 `FullyNamed` 协议的简单结构体:
@ -155,7 +158,7 @@ protocol RandomNumberGenerator {
`RandomNumberGenerator` 协议并不关心每一个随机数是怎样生成的,它只要求必须提供一个随机数生成器。 `RandomNumberGenerator` 协议并不关心每一个随机数是怎样生成的,它只要求必须提供一个随机数生成器。
如下所示,下边是一个采纳 `RandomNumberGenerator` 协议的类。该类实现了一个叫做 *线性同余生成器linear congruential generator* 的伪随机数算法。 如下所示,下边是一个采纳并符合 `RandomNumberGenerator` 协议的类。该类实现了一个叫做 *线性同余生成器linear congruential generator* 的伪随机数算法。
```swift ```swift
class LinearCongruentialGenerator: RandomNumberGenerator { class LinearCongruentialGenerator: RandomNumberGenerator {
@ -348,7 +351,7 @@ protocol DiceGameDelegate {
`DiceGame` 协议可以被任意涉及骰子的游戏采纳。`DiceGameDelegate` 协议可以被任意类型采纳,用来追踪 `DiceGame` 的游戏过程。 `DiceGame` 协议可以被任意涉及骰子的游戏采纳。`DiceGameDelegate` 协议可以被任意类型采纳,用来追踪 `DiceGame` 的游戏过程。
如下所示,`SnakesAndLadders` 是 [Control Flow](./05_Control_Flow.html) 章节引入的蛇梯棋游戏的新版本。新版本使用 `Dice` 实例作为骰子,并且实现了 `DiceGame``DiceGameDelegate` 协议,后者用来记录游戏的过程: 如下所示,`SnakesAndLadders` 是 [控制流](./05_Control_Flow.html) 章节引入的蛇梯棋游戏的新版本。新版本使用 `Dice` 实例作为骰子,并且实现了 `DiceGame``DiceGameDelegate` 协议,后者用来记录游戏的过程:
```swift ```swift
class SnakesAndLadders: DiceGame { class SnakesAndLadders: DiceGame {
@ -383,9 +386,9 @@ class SnakesAndLadders: DiceGame {
} }
``` ```
关于这个蛇梯棋游戏的详细描述请参阅 [Control Flow](./05_Control_Flow.html) 章节中的 [Break](./05_Control_Flow.html#break) 部分。 关于这个蛇梯棋游戏的详细描述请参阅 [控制流](./05_Control_Flow.html) 章节中的 [Break](./05_Control_Flow.html#break) 部分。
这个版本的游戏封装到了 `SnakesAndLadders` 类中,该类采纳了 `DiceGame` 协议,并且提供了相应的读的 `dice` 属性和 `play()` 方法。( `dice` 属性在构造之后就不再改变,且协议只要求 `dice`读的,因此将 `dice` 声明为常量属性。) 这个版本的游戏封装到了 `SnakesAndLadders` 类中,该类采纳了 `DiceGame` 协议,并且提供了相应的读的 `dice` 属性和 `play()` 方法。( `dice` 属性在构造之后就不再改变,且协议只要求 `dice`读的,因此将 `dice` 声明为常量属性。)
游戏使用 `SnakesAndLadders` 类的 `init()` 构造器来初始化游戏。所有的游戏逻辑被转移到了协议中的 `play()` 方法,`play()` 方法使用协议要求的 `dice` 属性提供骰子摇出的值。 游戏使用 `SnakesAndLadders` 类的 `init()` 构造器来初始化游戏。所有的游戏逻辑被转移到了协议中的 `play()` 方法,`play()` 方法使用协议要求的 `dice` 属性提供骰子摇出的值。
@ -408,7 +411,7 @@ class DiceGameTracker: DiceGameDelegate {
print("The game is using a \(game.dice.sides)-sided dice") print("The game is using a \(game.dice.sides)-sided dice")
} }
func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) { func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
++numberOfTurns numberOfTurns += 1
print("Rolled a \(diceRoll)") print("Rolled a \(diceRoll)")
} }
func gameDidEnd(game: DiceGame) { func gameDidEnd(game: DiceGame) {
@ -470,8 +473,8 @@ extension Dice: TextRepresentable {
现在所有 `Dice` 的实例都可以看做 `TextRepresentable` 类型: 现在所有 `Dice` 的实例都可以看做 `TextRepresentable` 类型:
```swift ```swift
let d12 = Dice(sides: 12,generator: LinearCongruentialGenerator()) let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator())
print(d12. textualDescription) print(d12.textualDescription)
// 打印 “A 12-sided dice” // 打印 “A 12-sided dice”
``` ```
@ -652,7 +655,7 @@ wishHappyBirthday(birthdayPerson)
* `as?` 返回一个可选值,当实例符合某个协议时,返回类型为协议类型的可选值,否则返回 `nil` * `as?` 返回一个可选值,当实例符合某个协议时,返回类型为协议类型的可选值,否则返回 `nil`
* `as!` 将实例强制向下转换到某个协议类型,如果强转失败,会引发运行时错误。 * `as!` 将实例强制向下转换到某个协议类型,如果强转失败,会引发运行时错误。
下面的例子定义了一个 `HasArea` 协议,该协议定义了一个 `Double` 类型的读属性 `area` 下面的例子定义了一个 `HasArea` 协议,该协议定义了一个 `Double` 类型的读属性 `area`
```swift ```swift
protocol HasArea { protocol HasArea {
@ -726,7 +729,7 @@ for object in objects {
> 注意 > 注意
> 可选的协议要求只能用在标记 `@objc` 特性的协议中。 > 可选的协议要求只能用在标记 `@objc` 特性的协议中。
> 该特性表示协议将暴露给 Objective-C 代码,详情参见[`Using Swift with Cocoa and Objective-C(Swift 2.1)`](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216)。即使你不打算和 Objective-C 有什么交互,如果你想要指定可选的协议要求,那么还是要为协议加上 `@obj` 特性。 > 该特性表示协议将暴露给 Objective-C 代码,详情参见[`Using Swift with Cocoa and Objective-C(Swift 2.2)`](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216)。即使你不打算和 Objective-C 有什么交互,如果你想要指定可选的协议要求,那么还是要为协议加上 `@objc` 特性。
> 还需要注意的是,标记 `@objc` 特性的协议只能被继承自 Objective-C 类的类或者 `@objc` 类采纳,其他类以及结构体和枚举均不能采纳这种协议。 > 还需要注意的是,标记 `@objc` 特性的协议只能被继承自 Objective-C 类的类或者 `@objc` 类采纳,其他类以及结构体和枚举均不能采纳这种协议。
下面的例子定义了一个名为 `Counter` 的用于整数计数的类,它使用外部的数据源来提供每次的增量。数据源由 `CounterDataSource` 协议定义,包含两个可选要求: 下面的例子定义了一个名为 `Counter` 的用于整数计数的类,它使用外部的数据源来提供每次的增量。数据源由 `CounterDataSource` 协议定义,包含两个可选要求:
@ -795,7 +798,7 @@ for _ in 1...4 {
// 12 // 12
``` ```
上述代码新建了一个 `Counter` 实例,并将它的数据源设置为一个 `TreeSource` 的实例,然后调用 `increment()` 方法四次。和预期一样,每次调用都会将 `count` 的值增加 `3`. 上述代码新建了一个 `Counter` 实例,并将它的数据源设置为一个 `ThreeSource` 的实例,然后调用 `increment()` 方法四次。和预期一样,每次调用都会将 `count` 的值增加 `3`.
下面是一个更为复杂的数据源 `TowardsZeroSource`,它将使得最后的值变为 `0` 下面是一个更为复杂的数据源 `TowardsZeroSource`,它将使得最后的值变为 `0`
@ -877,7 +880,7 @@ extension PrettyTextRepresentable {
<a name="adding_constraints_to_protocol_extensions"></a> <a name="adding_constraints_to_protocol_extensions"></a>
### 为协议扩展添加限制条件 ### 为协议扩展添加限制条件
在扩展协议的时候,可以指定一些限制条件,只有采纳协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用 `where` 子句来描述,正如[Where子句](./23_Generics.html#where_clauses))中所描述的。 在扩展协议的时候,可以指定一些限制条件,只有采纳协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用 `where` 子句来描述,正如[Where子句](./23_Generics.html#where_clauses)中所描述的。
例如,你可以扩展 `CollectionType` 协议,但是只适用于集合中的元素采纳了 `TextRepresentable` 协议的情况: 例如,你可以扩展 `CollectionType` 协议,但是只适用于集合中的元素采纳了 `TextRepresentable` 协议的情况:

View File

@ -12,6 +12,10 @@
> 2.1 > 2.1
> 校对:[shanks](http://codebuild.me)2015-11-01 > 校对:[shanks](http://codebuild.me)2015-11-01
> 2.2:翻译+校对:[Lanford](https://github.com/LanfordCai)2016-04-08 [SketchK](https://github.com/SketchK) 2016-05-16
> 3.0:翻译+校对:[chenmingjia](https://github.com/chenmingjia)2016-09-12
本页包含内容: 本页包含内容:
- [泛型所解决的问题](#the_problem_that_generics_solve) - [泛型所解决的问题](#the_problem_that_generics_solve)
@ -24,9 +28,9 @@
- [关联类型](#associated_types) - [关联类型](#associated_types)
- [Where 子句](#where_clauses) - [Where 子句](#where_clauses)
泛型代码可以让你编写适用自定义需求以及任意类型灵活可重用的函数类型。它的可以让你避免重复的代码,用一种清晰和抽象的方式来表达代码的意图。 泛型代码让你能够根据自定义需求,编写出适用于任意类型灵活可重用的函数类型。它让你避免代码的重复,用一种清晰和抽象的方式来表达代码的意图。
泛型是 Swift 强大特性之一,许多 Swift 标准库是通过泛型代码构建的。事实上泛型的使用贯穿了整本语言手册只是你可能没有发现而已。例如Swift 的 `Array``Dictionary` 都是泛型集合。你可以创建一个 `Int` 数组,也可创建一个 `String` 数组,甚至可以是任意其他 Swift 类型的数组。同样的,你也可以创建存储任意指定类型的字典。 泛型是 Swift 强大特性之一,许多 Swift 标准库是通过泛型代码构建的。事实上泛型的使用贯穿了整本语言手册只是你可能没有发现而已。例如Swift 的 `Array``Dictionary` 都是泛型集合。你可以创建一个 `Int` 数组,也可创建一个 `String` 数组,甚至可以是任意其他 Swift 类型的数组。同样的,你也可以创建存储任意指定类型的字典。
<a name="the_problem_that_generics_solve"></a> <a name="the_problem_that_generics_solve"></a>
## 泛型所解决的问题 ## 泛型所解决的问题
@ -96,11 +100,11 @@ func swapTwoInts(inout a: Int, inout _ b: Int)
func swapTwoValues<T>(inout a: T, inout _ b: T) func swapTwoValues<T>(inout a: T, inout _ b: T)
``` ```
这个函数的泛型版本使用了占位类型名(在这里用字母 `T` 来表示)来代替实际类型名(例如 `Int``String``Double`)。占位类型名没有指明 `T` 必须是什么类型,但是它指明了 `a``b` 必须是同一类型 `T`无论 `T` 代表什么类型。只有 `swapTwoValues(_:_:)` 函数在调用时,才能根据所传入的实际类型决定 `T` 所代表的类型。 这个函数的泛型版本使用了占位类型名(在这里用字母 `T` 来表示)来代替实际类型名(例如 `Int``String``Double`)。占位类型名没有指明 `T` 必须是什么类型,但是它指明了 `a``b` 必须是同一类型 `T`,无论 `T` 代表什么类型。只有 `swapTwoValues(_:_:)` 函数在调用时,才能根据所传入的实际类型决定 `T` 所代表的类型。
另外一个不同之处在于这个泛型函数名后面跟着占位类型名(`T`而且是用尖括号括起来`<T>`)。这个尖括号告诉 Swift 那个 `T``swapTwoValues(_:_:)` 函数定义的一个占位类型名,因此 Swift 不会去查找名为 `T` 的实际类型。 另外一个不同之处在于这个泛型函数名`swapTwoValues(_:_:)`后面跟着占位类型名(`T`用尖括号括起来(`<T>`)。这个尖括号告诉 Swift 那个 `T``swapTwoValues(_:_:)` 函数定义的一个占位类型名,因此 Swift 不会去查找名为 `T` 的实际类型。
`swapTwoValues(_:_:)` 函数现在可以像 `swapTwoInts(_:_:)` 那样调用,可以传入任意类型的值,只要两个值的类型相同`swapTwoValues(_:_:)` 函数被调用时,`T` 所代表的类型都会由传入的值的类型推断出来。 `swapTwoValues(_:_:)` 函数现在可以像 `swapTwoInts(_:_:)` 那样调用,不同的是它能接受两个任意类型的值,条件是这两个值有着相同的类型。`swapTwoValues(_:_:)` 函数被调用时,`T` 所代表的类型都会由传入的值的类型推断出来。
在下面的两个例子中,`T` 分别代表 `Int``String` 在下面的两个例子中,`T` 分别代表 `Int``String`
@ -131,22 +135,22 @@ swapTwoValues(&someString, &anotherString)
<a name="naming_type_parameters"></a> <a name="naming_type_parameters"></a>
## 命名类型参数 ## 命名类型参数
在大多数情况下,类型参数具有一个描述性名字,例如 `Dictionary<Key, Value>` 中的 `Key``Value`,以及 `Array<Element>` 中的 `Element`,这可以告诉阅读代码的人这些类型参数和泛型函数之间的关系。然而,当它们之间的关系没有意义时,通常使用单一的字母来命名,例如 `T``U``V`,正如上面演示的 `swapTwoValues(_:_:)` 函数中的 `T` 一样。 在大多数情况下,类型参数具有一个描述性名字,例如 `Dictionary<Key, Value>` 中的 `Key``Value`,以及 `Array<Element>` 中的 `Element`,这可以告诉阅读代码的人这些类型参数和泛型函数之间的关系。然而,当它们之间没有意义的关系时,通常使用单字母来命名,例如 `T``U``V`,正如上面演示的 `swapTwoValues(_:_:)` 函数中的 `T` 一样。
> 注意 > 注意
请始终使用大写字母开头的驼峰命名法(例如 `T``MyTypeParameter`)来为类型参数命名,以表明它们是占位类型,而不是一个值。 请始终使用大写字母开头的驼峰命名法(例如 `T``MyTypeParameter`)来为类型参数命名,以表明它们是占位类型,而不是一个值。
<a name="generic_types"></a> <a name="generic_types"></a>
## 泛型类型 ## 泛型类型
除了泛型函数Swift 还允许你定义泛型类型。这些自定义类、结构体和枚举可以适用于任何类型,如同 `Array``Dictionary` 的用法 除了泛型函数Swift 还允许你定义泛型类型。这些自定义类、结构体和枚举可以适用于任何类型,类似于 `Array``Dictionary`
这部分内容将向你展示如何编写一个名为 `Stack` (栈)的泛型集合类型。栈是一系列值的有序集合,和 `Array` 类似,但它相比 Swift 的 `Array` 类型有更多的操作限制。数组允许对其中任意位置的元素执行插入或删除操作。而栈只允许在集合的末端添加新的元素(称之为入栈)。同样的,栈也只能从末端移除元素(称之为出栈)。 这部分内容将向你展示如何编写一个名为 `Stack` (栈)的泛型集合类型。栈是一系列值的有序集合,和 `Array` 类似,但它相比 Swift 的 `Array` 类型有更多的操作限制。数组允许在数组的任意位置插入新元素或是删除其中任意位置的元素。而栈只允许在集合的末端添加新的元素(称之为入栈)。类似的,栈也只能从末端移除元素(称之为出栈)。
> 注意 > 注意
栈的概念已被 `UINavigationController` 类用来模拟视图控制器的导航结构。你通过调用 `UINavigationController``pushViewController(_:animated:)` 方法来添加新的视图控制器到导航栈,通过 `popViewControllerAnimated(_:)` 方法来从导航栈中移除某个视图控制器。每当你需要一个严格的“后进先出”方式来管理集合,栈都是最实用的模型。 栈的概念已被 `UINavigationController` 类用来构造视图控制器的导航结构。你通过调用 `UINavigationController``pushViewController(_:animated:)` 方法来添加新的视图控制器到导航栈,通过 `popViewControllerAnimated(_:)` 方法来从导航栈中移除视图控制器。每当你需要一个严格的“后进先出”方式来管理集合,栈都是最实用的模型。
下图展示了一个栈的push和出栈pop的行为 下图展示了一个栈的push和出栈pop的行为
![此处输入图片的描述](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/stackPushPop_2x.png) ![此处输入图片的描述](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/stackPushPop_2x.png)
@ -154,9 +158,9 @@ swapTwoValues(&someString, &anotherString)
2. 第四个值被压入到栈的顶部。 2. 第四个值被压入到栈的顶部。
3. 现在有四个值在栈中,最近入栈的那个值在顶部。 3. 现在有四个值在栈中,最近入栈的那个值在顶部。
4. 栈中最顶部的那个值被移除,或称之为出栈。 4. 栈中最顶部的那个值被移除,或称之为出栈。
5. 移除掉一个值后,现在栈再一次只有三个值。 5. 移除掉一个值后,现在栈只有三个值
下面展示了如何编写一个非泛型版本的栈,在这种情况下是 `Int` 型的栈: 下面展示了如何编写一个非泛型版本的栈, `Int` 型的栈为例
```swift ```swift
struct IntStack { struct IntStack {
@ -188,15 +192,15 @@ struct Stack<Element> {
} }
``` ```
注意,`Stack` 基本上和 `IntStack` 相同,只是用占位类型参数 `Element` 代替了实际的 `Int` 类型。这类型参数包裹在一对尖括号里(`<Element>`,紧跟在结构体名后面 注意,`Stack` 基本上和 `IntStack` 相同,只是用占位类型参数 `Element` 代替了实际的 `Int` 类型。这类型参数包裹在紧随结构体名的一对尖括号里(`<Element>`)。
`Element`尚未提供的类型定义了一个占位名。这种尚未提供的类型可以在结构体的定义中通过 `Element` 来引用。在这种情况下`Element` 在如下三个地方被用作占位符: `Element`提供的类型定义了一个占位名。这种提供的类型可以在结构体的定义中通过 `Element` 来引用。在这个例子中`Element` 在如下三个地方被用作占位符:
- 创建 `items` 属性,使用 `Element` 类型的空数组对其进行初始化。 - 创建 `items` 属性,使用 `Element` 类型的空数组对其进行初始化。
- 指定 `push(_:)` 方法的一参数 `item` 的类型必须是 `Element` 类型。 - 指定 `push(_:)` 方法的一参数 `item` 的类型必须是 `Element` 类型。
- 指定 `pop()` 方法的返回值类型必须是 `Element` 类型。 - 指定 `pop()` 方法的返回值类型必须是 `Element` 类型。
由于 `Stack` 是泛型类型,因此可以用来创建 Swift 中任意有效类型的栈,如同 `Array``Dictionary` 由于 `Stack` 是泛型类型,因此可以用来创建 Swift 中任意有效类型的栈,就像 `Array``Dictionary` 那样
你可以通过在尖括号中写出栈中需要存储的数据类型来创建并初始化一个 `Stack` 实例。例如,要创建一个 `String` 类型的栈,可以写成 `Stack<String>()` 你可以通过在尖括号中写出栈中需要存储的数据类型来创建并初始化一个 `Stack` 实例。例如,要创建一个 `String` 类型的栈,可以写成 `Stack<String>()`
@ -227,7 +231,7 @@ let fromTheTop = stackOfStrings.pop()
<a name="extending_a_generic_type"></a> <a name="extending_a_generic_type"></a>
## 扩展一个泛型类型 ## 扩展一个泛型类型
当你扩展一个泛型类型的时候,你并不需要在扩展的定义中提供类型参数列表。更加方便的是,原始类型定义中声明的类型参数列表在扩展中可以直接使用,并且这些来自原始类型中的参数名称会被用作原始定义中类型参数的引用。 当你扩展一个泛型类型的时候,你并不需要在扩展的定义中提供类型参数列表。原始类型定义中声明的类型参数列表在扩展中可以直接使用,并且这些来自原始类型中的参数名称会被用作原始定义中类型参数的引用。
下面的例子扩展了泛型类型 `Stack`,为其添加了一个名为 `topItem` 的只读计算型属性,它将会返回当前栈顶端的元素而不会将其从栈中移除: 下面的例子扩展了泛型类型 `Stack`,为其添加了一个名为 `topItem` 的只读计算型属性,它将会返回当前栈顶端的元素而不会将其从栈中移除:
@ -243,7 +247,7 @@ extension Stack {
注意,这个扩展并没有定义一个类型参数列表。相反的,`Stack` 类型已有的类型参数名称 `Element`,被用在扩展中来表示计算型属性 `topItem` 的可选类型。 注意,这个扩展并没有定义一个类型参数列表。相反的,`Stack` 类型已有的类型参数名称 `Element`,被用在扩展中来表示计算型属性 `topItem` 的可选类型。
计算型属性 `topItem` 现在可以用来访问任意 `Stack` 实例的顶端元素而不是移除它: 计算型属性 `topItem` 现在可以用来访问任意 `Stack` 实例的顶端元素且不移除它:
```swift ```swift
if let topItem = stackOfStrings.topItem { if let topItem = stackOfStrings.topItem {
@ -255,18 +259,18 @@ if let topItem = stackOfStrings.topItem {
<a name="type_constraints"></a> <a name="type_constraints"></a>
## 类型约束 ## 类型约束
`swapTwoValues(_:_:)` 函数和 `Stack` 类型可以作用于任何类型。不过,有的时候如果能将使用在泛型函数和泛型类型中的类型,强制约束为某种特定类型,将会是非常有用的。类型约束可以指定一个类型参数必须继承自指定类,或者符合一个特定的协议或协议组合。 `swapTwoValues(_:_:)` 函数和 `Stack` 类型可以作用于任何类型。不过,有的时候如果能将使用在泛型函数和泛型类型中的类型添加一个特定类型约束,将会是非常有用的。类型约束可以指定一个类型参数必须继承自指定类,或者符合一个特定的协议或协议组合。
例如Swift 的 `Dictionary` 类型对字典的键的类型做了些限制。在[字典](./04_Collection_Types.html#dictionaries)的描述中,字典的键的类型必须是可哈希的。也就是说,必须有一种方法能作为其唯一表示。`Dictionary` 之所以需要其键是可哈希的,是为了便于检查字典是否已经包含某个特定键的值。如无此要求,`Dictionary` 将无法判断是否可以插入或者替换某个指定键的值,也不能查找到已经存储在字典中的指定键的值。 例如Swift 的 `Dictionary` 类型对字典的键的类型做了些限制。在[字典](./04_Collection_Types.html#dictionaries)的描述中,字典的键的类型必须是可哈希`hashable`的。也就是说,必须有一种方法能唯一表示`Dictionary` 的键之所以是可哈希的,是为了便于检查字典是否已经包含某个特定键的值。若没有这个要求,`Dictionary` 将无法判断是否可以插入或者替换某个指定键的值,也不能查找到已经存储在字典中的指定键的值。
这个要求强制加上了一个类型约束作用于 `Dictionary` 的键类型上,其键类型必须符合 `Hashable` 协议,这是 Swift 标准库中定义的一个特定协议。所有的 Swift 基本类型(例如 `String``Int``Double``Bool`)默认都是可哈希的。 为了实现这个要求,一个类型约束被强制加到 `Dictionary` 的键类型上,要求其键类型必须符合 `Hashable` 协议,这是 Swift 标准库中定义的一个特定协议。所有的 Swift 基本类型(例如 `String``Int``Double``Bool`)默认都是可哈希的。
当你创建自定义泛型类型时,你可以定义你自己的类型约束,这些约束将提供更为强大的泛型编程能力。抽象概念,例如可哈希的,描述的是类型在概念上的特征,而不是它们的显式类型。 当你创建自定义泛型类型时,你可以定义你自己的类型约束,这些约束将提供更为强大的泛型编程能力。抽象概念,例如可哈希的,描述的是类型在概念上的特征,而不是它们的显式类型。
<a name="type_constraint_syntax"></a> <a name="type_constraint_syntax"></a>
### 类型约束语法 ### 类型约束语法
你可以在一个类型参数名后面放置一个类名或者协议名,通过冒号分隔,从而定义类型约束,它们将为类型参数列表的一部分。这种基本的类型约束作用于泛型函数时的语法如下所示(作用于泛型类型时的语法与之相同): 你可以在一个类型参数名后面放置一个类名或者协议名,并用冒号进行分隔,定义类型约束,它们将为类型参数列表的一部分。对泛型函数添加类型约束的基本语法如下所示(作用于泛型类型时的语法与之相同):
```swift ```swift
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) { func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
@ -279,7 +283,7 @@ func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
<a name="type_constraints_in_action"></a> <a name="type_constraints_in_action"></a>
### 类型约束实践 ### 类型约束实践
这里有个名为 `findStringIndex` 的非泛型函数,该函数的功能是在 `String` 值的数组中查找给定 `String` 值的索引。若查找到匹配的字符串,`findStringIndex(_:_:)` 函数返回该字符串在数组中的索引值,反之则返回 `nil` 这里有个名为 `findStringIndex` 的非泛型函数,该函数的功能是在一个 `String` 数组中查找给定 `String` 值的索引。若查找到匹配的字符串,`findStringIndex(_:_:)` 函数返回该字符串在数组中的索引值,则返回 `nil`
```swift ```swift
func findStringIndex(array: [String], _ valueToFind: String) -> Int? { func findStringIndex(array: [String], _ valueToFind: String) -> Int? {
@ -302,9 +306,9 @@ if let foundIndex = findStringIndex(strings, "llama") {
// 打印 “The index of llama is 2” // 打印 “The index of llama is 2”
``` ```
如果只能查找字符串在数组中的索引,用处不是很大。不过,你可以写出相同功能的泛型函数 `findIndex(_:_:)`用占位类型 `T` 替换 `String` 类型。 如果只能查找字符串在数组中的索引,用处不是很大。不过,你可以用占位类型 `T` 替换 `String` 类型来写出具有相同功能的泛型函数 `findIndex(_:_:)`
下面展示了 `findStringIndex(_:_:)` 函数的泛型版本 `findIndex(_:_:)`。请注意这个函数仍然返回 `Int?`是因为函数返回的是一个可选的索引数,而不是从数组中得到的一个可选值。需要提醒的是,这个函数无法通过编译,原因在例子后面说明: 下面展示了 `findStringIndex(_:_:)` 函数的泛型版本 `findIndex(_:_:)`。请注意这个函数返回值的类型仍然是 `Int?`是因为函数返回的是一个可选的索引数,而不是从数组中得到的一个可选值。需要提醒的是,这个函数无法通过编译,原因在例子后面说明:
```swift ```swift
func findIndex<T>(array: [T], _ valueToFind: T) -> Int? { func findIndex<T>(array: [T], _ valueToFind: T) -> Int? {
@ -317,9 +321,9 @@ func findIndex<T>(array: [T], _ valueToFind: T) -> Int? {
} }
``` ```
上面所写的函数无法通过编译。这个问题出在相等性检查上,即 `if value == valueToFind`。不是所有的 Swift 类型都可以用等式符(`==`)进行比较。例如,如果你创建一个你自己的类或结构体来表示一个复杂的数据模型,那么 Swift 无法猜到对于这个类或结构体而言“相等”意味着什么。正因如此,这部分代码无法保证适用于每个可能的类型 `T`,当你试图编译这部分代码时会出现相应的错误。 上面所写的函数无法通过编译。问题出在相等性检查上,即 "`if value == valueToFind`"。不是所有的 Swift 类型都可以用等式符(`==`)进行比较。比如说,如果你创建一个自定义的类或结构体来表示一个复杂的数据模型,那么 Swift 无法猜到对于这个类或结构体而言“相等”意味着什么。正因如此,这部分代码无法保证适用于每个可能的类型 `T`,当你试图编译这部分代码时会出现相应的错误。
不过所有的这些并不会让我们无从下手。Swift 标准库中定义了一个 `Equatable` 协议,该协议要求任何符合该协议的类型必须实现等式符(`==`,从而能对符合该协议的类型的任意两个值进行比较。所有的 Swift 标准类型自动支持 `Equatable` 协议。 不过所有的这些并不会让我们无从下手。Swift 标准库中定义了一个 `Equatable` 协议,该协议要求任何遵循该协议的类型必须实现等式符(`==`及不等符(`!=`),从而能对该类型的任意两个值进行比较。所有的 Swift 标准类型自动支持 `Equatable` 协议。
任何 `Equatable` 类型都可以安全地使用在 `findIndex(_:_:)` 函数中,因为其保证支持等式操作符。为了说明这个事实,当你定义一个函数时,你可以定义一个 `Equatable` 类型约束作为类型参数定义的一部分: 任何 `Equatable` 类型都可以安全地使用在 `findIndex(_:_:)` 函数中,因为其保证支持等式操作符。为了说明这个事实,当你定义一个函数时,你可以定义一个 `Equatable` 类型约束作为类型参数定义的一部分:
@ -334,7 +338,7 @@ func findIndex<T: Equatable>(array: [T], _ valueToFind: T) -> Int? {
} }
``` ```
`findIndex(_:_:)` 中的这个单一类型参数写做 `T: Equatable`,也就意味着“任何符合 `Equatable` 协议的 `T` 类型”。 `findIndex(_:_:)` 唯一的类型参数写做 `T: Equatable`,也就意味着“任何符合 `Equatable` 协议的类型 `T` ”。
`findIndex(_:_:)` 函数现在可以成功编译了,并且可以作用于任何符合 `Equatable` 的类型,如 `Double``String` `findIndex(_:_:)` 函数现在可以成功编译了,并且可以作用于任何符合 `Equatable` 的类型,如 `Double``String`
@ -348,7 +352,7 @@ let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea")
<a name="associated_types"></a> <a name="associated_types"></a>
## 关联类型 ## 关联类型
定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分将会非常有用。关联类型为协议的一部分,为某个类型提供了一个占位名(或者说别名),其代表的实际类型在协议被采纳时才会被指定。你可以通过 `typealias` 关键字来指定关联类型。 定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分将会非常有用。关联类型为协议的某个类型提供了一个占位名(或者说别名),其代表的实际类型在协议被采纳时才会被指定。你可以通过 `associatedtype` 关键字来指定关联类型。
<a name="associated_types_in_action"></a> <a name="associated_types_in_action"></a>
### 关联类型实践 ### 关联类型实践
@ -357,26 +361,26 @@ let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea")
```swift ```swift
protocol Container { protocol Container {
typealias ItemType associatedtype ItemType
mutating func append(item: ItemType) mutating func append(item: ItemType)
var count: Int { get } var count: Int { get }
subscript(i: Int) -> ItemType { get } subscript(i: Int) -> ItemType { get }
} }
``` ```
`Container` 协议定义了三个任何采纳协议的类型必须提供的功能: `Container` 协议定义了三个任何采纳了该协议的类型(即容器)必须提供的功能:
- 必须可以通过 `append(_:)` 方法添加一个新元素到容器里。 - 必须可以通过 `append(_:)` 方法添加一个新元素到容器里。
- 必须可以通过 `count` 属性获取容器中元素的数量,并返回一个 `Int` 值。 - 必须可以通过 `count` 属性获取容器中元素的数量,并返回一个 `Int` 值。
- 必须可以通过接受 `Int` 索引值的下标检索到每一个元素。 - 必须可以通过索引值类型为 `Int` 的下标检索到容器中的每一个元素。
这个协议没有指定容器中元素该如何存储,以及元素必须是何种类型。这个协议只指定了三个任何采纳 `Container` 协议的类型必须提供的功能。采纳协议的类型在满足这三个条件的情况下也可以提供其他额外的功能。 这个协议没有指定容器中元素该如何存储,以及元素必须是何种类型。这个协议只指定了三个任何遵从 `Container` 协议的类型必须提供的功能。遵从协议的类型在满足这三个条件的情况下也可以提供其他额外的功能。
任何采纳 `Container` 协议的类型必须能够指定其存储的元素的类型,必须保证只有正确类型的元素可以加进容器中,必须明确通过其下标返回的元素的类型。 任何遵从 `Container` 协议的类型必须能够指定其存储的元素的类型,必须保证只有正确类型的元素可以加进容器中,必须明确通过其下标返回的元素的类型。
为了定义这三个条件,`Container` 协议需要在不知道容器中元素的具体类型的情况下引用这种类型。`Container` 协议需要指定任何通过 `append(_:)` 方法添加到容器中的元素和容器中的元素是相同类型,并且通过容器下标返回的元素的类型也是这种类型。 为了定义这三个条件,`Container` 协议需要在不知道容器中元素的具体类型的情况下引用这种类型。`Container` 协议需要指定任何通过 `append(_:)` 方法添加到容器中的元素和容器中的元素是相同类型,并且通过容器下标返回的元素的类型也是这种类型。
为了达到目的,`Container` 协议声明了一个关联类型 `ItemType`,写作 `typealias ItemType`。这个协议无法定义 `ItemType` 是什么类型的别名,这个信息将留给采纳协议的类型来提供。尽管如此,`ItemType` 别名提供了一种方式来引用 `Container` 中元素的类型,并将之用于 `append(_:)` 方法和下标,从而保证任何 `Container`预期行为都能够被执行。 为了达到这个目的,`Container` 协议声明了一个关联类型 `ItemType`,写作 `associatedtype ItemType`。这个协议无法定义 `ItemType` 是什么类型的别名,这个信息将留给遵从协议的类型来提供。尽管如此,`ItemType` 别名提供了一种方式来引用 `Container` 中元素的类型,并将之用于 `append(_:)` 方法和下标,从而保证任何 `Container` 的行为都能够正如预期地被执行。
下面是先前的非泛型的 `IntStack` 类型,这一版本采纳并符合了 `Container` 协议: 下面是先前的非泛型的 `IntStack` 类型,这一版本采纳并符合了 `Container` 协议:
@ -406,11 +410,11 @@ struct IntStack: Container {
`IntStack` 结构体实现了 `Container` 协议的三个要求,其原有功能也不会和这些要求相冲突。 `IntStack` 结构体实现了 `Container` 协议的三个要求,其原有功能也不会和这些要求相冲突。
此外,`IntStack` 指定 `ItemType``Int` 类型,即 `typealias ItemType = Int`,从而将 `Container` 协议中抽象的 `ItemType` 类型转换为具体的 `Int` 类型。 此外,`IntStack` 在实现 `Container` 的要求时,指定 `ItemType``Int` 类型,即 `typealias ItemType = Int`,从而将 `Container` 协议中抽象的 `ItemType` 类型转换为具体的 `Int` 类型。
由于 Swift 的类型推断,你实际上不用在 `IntStack` 的定义中声明 `ItemType``Int`。因为 `IntStack` 符合 `Container` 协议的所有要求Swift 只需通过 `append(_:)` 方法的 `item` 参数类型和下标返回值的类型,就可以推断出 `ItemType` 的具体类型。事实上,如果你在上面的代码中删除了 `typealias ItemType = Int` 这一行,一切仍旧可以正常工作,因为 Swift 清楚地知道 `ItemType` 应该是种类型。 由于 Swift 的类型推断,你实际上不用在 `IntStack` 的定义中声明 `ItemType``Int`。因为 `IntStack` 符合 `Container` 协议的所有要求Swift 只需通过 `append(_:)` 方法的 `item` 参数类型和下标返回值的类型,就可以推断出 `ItemType` 的具体类型。事实上,如果你在上面的代码中删除了 `typealias ItemType = Int` 这一行,一切仍旧可以正常工作,因为 Swift 清楚地知道 `ItemType` 应该是种类型。
你也可以泛型 `Stack` 结构体符合 `Container` 协议: 你也可以泛型 `Stack` 结构体遵从 `Container` 协议:
```swift ```swift
struct Stack<Element>: Container { struct Stack<Element>: Container {
@ -442,7 +446,7 @@ struct Stack<Element>: Container {
[通过扩展添加协议一致性](./22_Protocols.html#adding_protocol_conformance_with_an_extension)中描述了如何利用扩展让一个已存在的类型符合一个协议,这包括使用了关联类型的协议。 [通过扩展添加协议一致性](./22_Protocols.html#adding_protocol_conformance_with_an_extension)中描述了如何利用扩展让一个已存在的类型符合一个协议,这包括使用了关联类型的协议。
Swift 的 `Array` 已经提供 `append(_:)` 方法,一个 `count` 属性,以及一个接受 `Int` 型索引值的可用来检索数组元素的下标。这三个功能都符合 `Container` 协议的要求,也就意味着你可以扩展 `Array` 去符合 `Container` 协议,只需简单地声明 `Array` 采纳该协议即可。你可以通过一个空扩展来实现这点,正如[通过扩展采纳协议](./22_Protocols.html#declaring_protocol_adoption_with_an_extension)中的描述: Swift 的 `Array` 类型已经提供 `append(_:)` 方法,一个 `count` 属性,以及一个接受 `Int` 型索引值的下标用以检索其元素。这三个功能都符合 `Container` 协议的要求,也就意味着你只需简单地声明 `Array` 采纳该协议就可以扩展 `Array`,使其遵从 `Container` 协议。你可以通过一个空扩展来实现这点,正如[通过扩展采纳协议](./22_Protocols.html#declaring_protocol_adoption_with_an_extension)中的描述:
```swift ```swift
extension Array: Container {} extension Array: Container {}
@ -455,30 +459,29 @@ extension Array: Container {}
[类型约束](#type_constraints)让你能够为泛型函数或泛型类型的类型参数定义一些强制要求。 [类型约束](#type_constraints)让你能够为泛型函数或泛型类型的类型参数定义一些强制要求。
为关联类型定义约束也是非常有用的。你可以在参数列表中通过 `where` 子句为关联类型定义约束。一个 `where` 子句能够使一个关联类型符合某个特定的协议,以及某个特定的类型参数和关联类型必须类型相同。你可以通过将 `where` 关键字紧跟在类型参数列表后面来定义 `where` 子句,`where` 子句后跟一个或者多个针对关联类型的约束,以及一个或多个类型参数和关联类型间的相等关系。 为关联类型定义约束也是非常有用的。你可以在参数列表中通过 `where` 子句为关联类型定义约束。你能通过 `where` 子句要求一个关联类型遵从某个特定的协议,以及某个特定的类型参数和关联类型必须类型相同。你可以通过将 `where` 关键字紧跟在类型参数列表后面来定义 `where` 子句,`where` 子句后跟一个或者多个针对关联类型的约束,以及一个或多个类型参数和关联类型间的相等关系。你可以在函数体或者类型的大括号之前添加 where 子句。
下面的例子定义了一个名为 `allItemsMatch` 的泛型函数,用来检查两个 `Container` 实例是否包含相同顺序的相同元素。如果所有的元素能够匹配,那么返回 `true`,否则返回 `false` 下面的例子定义了一个名为 `allItemsMatch` 的泛型函数,用来检查两个 `Container` 实例是否包含相同顺序的相同元素。如果所有的元素能够匹配,那么返回 `true`,否则返回 `false`
被检查的两个 `Container` 可以不是相同类型的容器(虽然它们可以相同),但它们必须拥有相同类型的元素。这个要求通过一个类型约束以及一个 `where` 子句来表示: 被检查的两个 `Container` 可以不是相同类型的容器(虽然它们可以相同),但它们必须拥有相同类型的元素。这个要求通过一个类型约束以及一个 `where` 子句来表示:
```swift ```swift
func allItemsMatch< func allItemsMatch<C1: Container, C2: Container>
C1: Container, C2: Container (_ someContainer: C1, _ anotherContainer: C2) -> Bool
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable> where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
(someContainer: C1, _ anotherContainer: C2) -> Bool {
// 检查两个容器含有相同数量的元素 // 检查两个容器含有相同数量的元素
if someContainer.count != anotherContainer.count { if someContainer.count != anotherContainer.count {
return false return false
} }
// 检查每一对元素是否相等 // 检查每一对元素是否相等
for i in 0..<someContainer.count { for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] { if someContainer[i] != anotherContainer[i] {
return false return false
} }
} }
// 所有元素都匹配,返回 true // 所有元素都匹配,返回 true
return true return true
} }
@ -486,7 +489,7 @@ func allItemsMatch<
这个函数接受 `someContainer``anotherContainer` 两个参数。参数 `someContainer` 的类型为 `C1`,参数 `anotherContainer` 的类型为 `C2``C1``C2` 是容器的两个占位类型参数,函数被调用时才能确定它们的具体类型。 这个函数接受 `someContainer``anotherContainer` 两个参数。参数 `someContainer` 的类型为 `C1`,参数 `anotherContainer` 的类型为 `C2``C1``C2` 是容器的两个占位类型参数,函数被调用时才能确定它们的具体类型。
这个函数的类型参数列表还定义了两个类型参数的要求: 这个函数的类型参数列表还定义了两个类型参数的要求:
- `C1` 必须符合 `Container` 协议(写作 `C1: Container`)。 - `C1` 必须符合 `Container` 协议(写作 `C1: Container`)。
- `C2` 必须符合 `Container` 协议(写作 `C2: Container`)。 - `C2` 必须符合 `Container` 协议(写作 `C2: Container`)。
@ -504,7 +507,7 @@ func allItemsMatch<
第三个和第四个要求结合起来意味着 `anotherContainer` 中的元素也可以通过 `!=` 操作符来比较,因为它们和 `someContainer` 中的元素类型相同。 第三个和第四个要求结合起来意味着 `anotherContainer` 中的元素也可以通过 `!=` 操作符来比较,因为它们和 `someContainer` 中的元素类型相同。
这些要求让 `allItemsMatch(_:_:)` 函数能够比较两个容器,即使它们是不同的容器类型。 这些要求让 `allItemsMatch(_:_:)` 函数能够比较两个容器,即使它们的容器类型不同
`allItemsMatch(_:_:)` 函数首先检查两个容器是否拥有相同数量的元素,如果它们的元素数量不同,那么一定不匹配,函数就会返回 `false` `allItemsMatch(_:_:)` 函数首先检查两个容器是否拥有相同数量的元素,如果它们的元素数量不同,那么一定不匹配,函数就会返回 `false`
@ -530,4 +533,4 @@ if allItemsMatch(stackOfStrings, arrayOfStrings) {
// 打印 “All items match.” // 打印 “All items match.”
``` ```
上面的例子创建一个 `Stack` 实例来存储一些 `String` 值,然后将三个字符串压入栈中。这个例子还通过数组字面量创建了一个 `Array` 实例,数组中包含三个同栈中一样的字符串。即使栈和数组是不同的类型,但它们都符合 `Container` 协议,而且它们都包含相同类型的值。因此你可以用这两个容器作为参数来调用 `allItemsMatch(_:_:)` 函数。在上面的例子中,`allItemsMatch(_:_:)` 函数正确地显示了这两个容器中的所有元素都是相互匹配的。 上面的例子创建一个 `Stack` 实例来存储一些 `String` 值,然后将三个字符串压入栈中。这个例子还通过数组字面量创建了一个 `Array` 实例,数组中包含同栈中一样的三个字符串。即使栈和数组是不同的类型,但它们都遵从 `Container` 协议,而且它们都包含相同类型的值。因此你可以用这两个容器作为参数来调用 `allItemsMatch(_:_:)` 函数。在上面的例子中,`allItemsMatch(_:_:)` 函数正确地显示了这两个容器中的所有元素都是相互匹配的。

View File

@ -11,6 +11,9 @@
> 2.1 > 2.1
> 翻译:[Prayer](https://github.com/futantan) > 翻译:[Prayer](https://github.com/futantan)
> 校对:[shanks](http://codebuild.me)2015-11-01 > 校对:[shanks](http://codebuild.me)2015-11-01
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-17
本页内容包括: 本页内容包括:
@ -276,7 +279,7 @@ struct TrackedString {
private(set) var numberOfEdits = 0 private(set) var numberOfEdits = 0
var value: String = "" { var value: String = "" {
didSet { didSet {
numberOfEdits++ numberOfEdits += 1
} }
} }
} }
@ -306,7 +309,7 @@ public struct TrackedString {
public private(set) var numberOfEdits = 0 public private(set) var numberOfEdits = 0
public var value: String = "" { public var value: String = "" {
didSet { didSet {
numberOfEdits++ numberOfEdits += 1
} }
} }
public init() {} public init() {}

View File

@ -10,6 +10,9 @@
> 2.1 > 2.1
> 校对:[shanks](http://codebuild.me)2015-11-01 > 校对:[shanks](http://codebuild.me)2015-11-01
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-17
本页内容包括: 本页内容包括:
@ -181,7 +184,7 @@ let blueComponent = pink & 0x0000FF // blueComponent 是 0x99即 153
其次,使用二进制补码可以使负数的按位左移和右移运算得到跟正数同样的效果,即每向左移一位就将自身的数值乘以 2每向右一位就将自身的数值除以 2。要达到此目的对有符号整数的右移有一个额外的规则 其次,使用二进制补码可以使负数的按位左移和右移运算得到跟正数同样的效果,即每向左移一位就将自身的数值乘以 2每向右一位就将自身的数值除以 2。要达到此目的对有符号整数的右移有一个额外的规则
* 当对整数进行按位右移运算时,遵循与无符号整数相同的规则,但是对于移位产生的空白位使用符号位进行填充,而不是用 `0` * 当对整数进行按位右移运算时,遵循与无符号整数相同的规则,但是对于移位产生的空白位使用符号位进行填充,而不是用 `0`
![Art/bitshiftSigned_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSigned_2x.png "Art/bitshiftSigned_2x.png") ![Art/bitshiftSigned_2x.png](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/bitshiftSigned_2x.png "Art/bitshiftSigned_2x.png")
@ -342,7 +345,7 @@ let combinedVector = vector + anotherVector
<a name="prefix_and_postfix_operators"></a> <a name="prefix_and_postfix_operators"></a>
### 前缀和后缀运算符 ### 前缀和后缀运算符
上个例子演示了一个双目中缀运算符的自定义实现。类与结构体也能提供标准单目运算符的实现。单目运算符只运算一个值。当运算符出现在值之前时,它就是前缀的(例如 `-a`),而当它出现在值之后时,它就是后缀的(例如 `i++`)。 上个例子演示了一个双目中缀运算符的自定义实现。类与结构体也能提供标准单目运算符的实现。单目运算符只运算一个值。当运算符出现在值之前时,它就是前缀的(例如 `-a`),而当它出现在值之后时,它就是后缀的(例如 `b!`)。
要实现前缀或者后缀运算符,需要在声明运算符函数的时候在 `func` 关键字之前指定 `prefix` 或者 `postfix` 修饰符: 要实现前缀或者后缀运算符,需要在声明运算符函数的时候在 `func` 关键字之前指定 `prefix` 或者 `postfix` 修饰符:
@ -383,23 +386,6 @@ original += vectorToAdd
// original 的值现在为 (4.0, 6.0) // original 的值现在为 (4.0, 6.0)
``` ```
还可以将赋值与 `prefix``postfix` 修饰符结合起来,下面的代码为 `Vector2D` 实例实现了前缀自增运算符:
```swift
prefix func ++ (inout vector: Vector2D) -> Vector2D {
vector += Vector2D(x: 1.0, y: 1.0)
return vector
}
```
这个前缀自增运算符使用了前面定义的加法赋值运算。它对 `Vector2D``x``y` 属性都进行了加 `1` 运算,再将结果返回:
```swift
var toIncrement = Vector2D(x: 3.0, y: 4.0)
let afterIncrement = ++toIncrement
// toIncrement 的值现在为 (4.0, 5.0)
// afterIncrement 的值同样为 (4.0, 5.0)
```
> 注意 > 注意
> 不能对默认的赋值运算符(`=`)进行重载。只有组合赋值运算符可以被重载。同样地,也无法对三目条件运算符 `a ? b : c` 进行重载。 > 不能对默认的赋值运算符(`=`)进行重载。只有组合赋值运算符可以被重载。同样地,也无法对三目条件运算符 `a ? b : c` 进行重载。

View File

@ -5,32 +5,41 @@
> 翻译:[superkam](https://github.com/superkam) > 翻译:[superkam](https://github.com/superkam)
> 校对:[numbbbbb](https://github.com/numbbbbb) > 校对:[numbbbbb](https://github.com/numbbbbb)
> 2.0 > 2.0
> 翻译+校对:[buginux](https://github.com/buginux) > 翻译+校对:[buginux](https://github.com/buginux)
> 2.1 > 2.1
> 翻译:[mmoaay](https://github.com/mmoaay) > 翻译:[mmoaay](https://github.com/mmoaay)
> 2.2
> 翻译+校对:[星夜暮晨](https://github.com/semperidem)2016-04-06
本页包含内容: 本页包含内容:
- [空白与注释](#whitespace_and_comments) - [空白与注释](#whitespace_and_comments)
- [标识符](#identifiers) - [标识符](#identifiers)
- [关键字和标点符号](#keywords) - [关键字和标点符号](#keywords)
- [字面量](#literals) - [字面量](#literals)
- [整数字面量](#integer_literals) - [整数字面量](#integer_literals)
- [浮点数字面量](#floating_point_literals) - [浮点数字面量](#floating_point_literals)
- [字符串字面量](#string_literals) - [字符串字面量](#string_literals)
- [运算符](#operators) - [运算符](#operators)
Swift 的“词法结构描述了能构成该语言中合法符号的字符序列。这些合法符号组成了语言中最底层的构建基块,并在之后的章节中用于描述语言的其他部分。一个合法符号由一个标识符、关键字、标点符号、字面量或运算符组成。 Swift 的*“词法结构 (lexical structure)”* 描述了能构成该语言中合法符号 (token) 的字符序列。这些合法符号组成了语言中最底层的构建基块,并在之后的章节中用于描述语言的其他部分。一个合法符号由一个标识符 (identifier)、关键字 (keyword)、标点符号 (punctuation)、字面量 (literal) 或运算符 (operator) 组成。
通常情况下,符号是根据随后将介绍的语法约束,由 Swift 源文件的输入文本中提取可能的最长子串生成。这种方法称为“最长匹配,或者“最大适合 通常情况下,通过考虑输入文本当中可能的最长子串,并且在随后将介绍的语法约束之下,根据随后将介绍的语法约束生成的,根据 Swift 源文件当中的字符来生成相应的“符号”。这种方法称为*“最长匹配 (longest match)”*,或者*“最大适合(maximal munch)”*
<a id="whitespace_and_comments"></a> <a id="whitespace_and_comments"></a>
## 空白与注释 ## 空白与注释
空白有两个用途:分隔源文件中的符号以及帮助区分运算符属于前缀还是后缀(参见 [运算符](#operators)在其他情况下则会被忽略。以下的字符会被当作空白空格U+0020、换行符U+000A、回车符U+000D、水平制表符U+0009、垂直制表符U+000B、换页符U+000C以及空U+0000 空白 (whitespace) 有两个用途:分隔源文件中的符号以及帮助区分运算符属于前缀还是后缀(参见 [运算符](#operators)),在其他情况下空白则会被忽略。以下的字符会被当作空白空格U+0020、换行符U+000A、回车符U+000D、水平制表符U+0009、垂直制表符U+000B、换页符U+000C以及空字符U+0000
注释被编译器当作空白处理。单行注释由 `//` 开始直至遇到换行符U+000A或者回车符U+000D。多行注释由 `/*` 开始,以 `*/` 结束。注释允许嵌套,但注释标记必须匹配。 注释被编译器当作空白处理。单行注释由 `//` 开始直至遇到换行符U+000A或者回车符U+000D。多行注释由 `/*` 开始,以 `*/` 结束。注释允许嵌套,但注释标记必须匹配。
@ -39,9 +48,9 @@ Swift 的“词法结构”描述了能构成该语言中合法符号的字符
<a id="identifiers"></a> <a id="identifiers"></a>
## 标识符 ## 标识符
标识符可以由以下的字符开始:大写或小写的字母 `A``Z`、下划线 `_`、基本多文种平面中的 Unicode 非组合字符以及基本多文种平面以外的非个人专用区字符。首字符之后,允许使用数字和 Unicode 字符组合 *标识符(identifier)* 可以由以下的字符开始:大写或小写的字母 `A``Z`、下划线 (`_`)、基本多文种平面 (Basic Multilingual Plane) 中非字符数字组合的 Unicode 字符以及基本多文种平面以外的非个人专用区字符。首字符之后,允许使用数字和组合 Unicode 字符。
使用保留字作为标识符,需要在其前后增加反引号 `` ` ``。例如,`class` 不是合法的标识符,但可以使用 `` `class` ``。反引号不属于标识符的一部分,`` `x` `` 和 `x` 表示同一标识符。 使用保留字作为标识符,需要在其前后增加反引号 (`` ` ``)。例如,`class` 不是合法的标识符,但可以使用 `` `class` ``。反引号不属于标识符的一部分,`` `x` `` 和 `x` 表示同一标识符。
闭包中如果没有明确指定参数名称,参数将被隐式命名为 `$0`、`$1`、`$2` 等等。这些命名在闭包作用域范围内是合法的标识符。 闭包中如果没有明确指定参数名称,参数将被隐式命名为 `$0`、`$1`、`$2` 等等。这些命名在闭包作用域范围内是合法的标识符。
@ -51,6 +60,7 @@ Swift 的“词法结构”描述了能构成该语言中合法符号的字符
> *标识符* → [*头部标识符*](#identifier-head) [*标识符字符组*](#identifier-characters)<sub>可选</sub> > *标识符* → [*头部标识符*](#identifier-head) [*标识符字符组*](#identifier-characters)<sub>可选</sub>
> *标识符* → \`[*头部标识符*](#identifier-head) [*标识符字符组*](#identifier-characters)<sub>可选</sub>\` > *标识符* → \`[*头部标识符*](#identifier-head) [*标识符字符组*](#identifier-characters)<sub>可选</sub>\`
> *标识符* → [*隐式参数名*](#implicit-parameter-name) > *标识符* → [*隐式参数名*](#implicit-parameter-name)
<a id="identifier-list"></a> <a id="identifier-list"></a>
> *标识符列表* → [*标识符*](#identifier) | [*标识符*](#identifier) **,** [*标识符列表*](#identifier-list) > *标识符列表* → [*标识符*](#identifier) | [*标识符*](#identifier) **,** [*标识符列表*](#identifier-list)
@ -76,7 +86,7 @@ Swift 的“词法结构”描述了能构成该语言中合法符号的字符
> *标识符字符* → 数值 0 - 9 > *标识符字符* → 数值 0 - 9
> *标识符字符* → U+0300U+036FU+1DC0U+1DFFU+20D0U+20FF或者 U+FE20U+FE2F > *标识符字符* → U+0300U+036FU+1DC0U+1DFFU+20D0U+20FF或者 U+FE20U+FE2F
> *标识符字符* → [*头部标识符*](#identifier-head) > *标识符字符* → [*头部标识符*](#identifier-head)
<a id="identifier-characters"></a> > <a id="identifier-characters"></a>
> *标识符字符组* → [*标识符字符*](#identifier-character) [*标识符字符组*](#identifier-characters)<sub>可选</sub> > *标识符字符组* → [*标识符字符*](#identifier-character) [*标识符字符组*](#identifier-characters)<sub>可选</sub>
<a id="implicit-parameter-name"></a> <a id="implicit-parameter-name"></a>
@ -85,20 +95,21 @@ Swift 的“词法结构”描述了能构成该语言中合法符号的字符
<a id="keywords"></a> <a id="keywords"></a>
## 关键字和标点符号 ## 关键字和标点符号
下面这些被保留的关键字不允许用作标识符,除非反引号转义,具体描述请参考 [标识符](#identifiers)。 下面这些被保留的关键字不允许用作标识符,除非使用反引号转义,具体描述请参考 [标识符](#identifiers)。除了 `inout``var` 以及 `let` 之外的关键字可以用作某个函数声明或者函数调用当中的外部参数名,不用添加反引号转义。
* 用在声明中的关键字: `associatedtype``class``deinit``enum``extension``func``import``init``inout``internal``let``operator``private``protocol``public``static``struct``subscript``typealias` 以及 `var`
* 用在语句中的关键字:`break``case``continue``default``defer``do``else``fallthrough``for``guard``if``in``repeat``return``switch``where` 以及 `while`
* 用在表达式和类型中的关键字:`as``catch``dynamicType``false``is``nil``rethrows``super``self``Self``throw``throws``true``try``#column``#file``#function` 以及 `#line`
* 用在模式中的关键字:`_`
* 以井字号 (`#`) 开头的关键字:`#available``#column``#else#elseif``#endif``#file``#function``#if``#line` 以及 `#selector`
* 特定上下文中被保留的关键字: `associativity``convenience``dynamic``didSet``final``get``infix``indirect``lazy``left``mutating``none``nonmutating``optional``override``postfix``precedence``prefix``Protocol``required``right``set``Type``unowned``weak` 以及 `willSet`。这些关键字在特定上下文之外可以被用做标识符。
* 用在声明中的关键字: `class``deinit``enum``extension``func``import``init``inout``internal``let``operator``private``protocol``public``static``struct``subscript``typealias``var`
* 用在语句中的关键字: `break``case``continue``default``defer``do``else``fallthrough``for``guard``if``in``repeat``return``switch``where``while`
* 用在表达式和类型中的关键字: `as``catch``dynamicType``false``is``nil``rethrows``super``self``Self``throw``throws``true``try``__COLUMN__``__FILE__``__FUNCTION__``__LINE__`
* 用在模式中的关键字:`_`
* 特定上下文中被保留的关键字: `associativity``convenience``dynamic``didSet``final``get``infix``indirect``lazy``left``mutating``none``nonmutating``optional``override``postfix``precedence``prefix``Protocol``required``right``set``Type``unowned``weak``willSet`,这些关键字在特定上下文之外可以被用做标识符。
以下符号被当作保留符号,不能用于自定义运算符: `(``)``{``}``[``]``.``,``:``;``=``@``#``&`(作为前缀运算符)、`->`、`` ` ``、`?`、`!`(作为后缀运算符)。 以下符号被当作保留符号,不能用于自定义运算符: `(``)``{``}``[``]``.``,``:``;``=``@``#``&`(作为前缀运算符)、`->`、`` ` ``、`?`、`!`(作为后缀运算符)。
<a id="literals"></a> <a id="literals"></a>
## 字面量 ## 字面量
字面量用来表示源码中某种特定类型的值,比如一个数字或字符串。 *字面量 (literal)* 用来表示源码中某种特定类型的值,比如一个数字或字符串。
下面是字面量的一些示例: 下面是字面量的一些示例:
@ -106,7 +117,7 @@ Swift 的“词法结构”描述了能构成该语言中合法符号的字符
42 // 整数字面量 42 // 整数字面量
3.14159 // 浮点数字面量 3.14159 // 浮点数字面量
"Hello, world!" // 字符串字面量 "Hello, world!" // 字符串字面量
true // 布尔值字面量 true // 布尔值字面量
``` ```
字面量本身并不包含类型信息。事实上,一个字面量会被解析为拥有无限的精度,然后 Swift 的类型推导会尝试去推导出这个字面量的类型。比如,在 `let x: Int8 = 42` 这个声明中Swift 使用了显式类型标注(`: Int8`)来推导出 `42` 这个整数字面量的类型是 `Int8`。如果没有可用的类型信息, Swift 则会从标准库中定义的字面量类型中推导出一个默认的类型。整数字面量的默认类型是 `Int`,浮点数字面量的默认类型是 `Double`,字符串字面量的默认类型是 `String`,布尔值字面量的默认类型是 `Bool`。比如,在 `let str = "Hello, world"` 这个声明中,字符串 `"Hello, world"` 的默认推导类型就是 `String`。 字面量本身并不包含类型信息。事实上,一个字面量会被解析为拥有无限的精度,然后 Swift 的类型推导会尝试去推导出这个字面量的类型。比如,在 `let x: Int8 = 42` 这个声明中Swift 使用了显式类型标注(`: Int8`)来推导出 `42` 这个整数字面量的类型是 `Int8`。如果没有可用的类型信息, Swift 则会从标准库中定义的字面量类型中推导出一个默认的类型。整数字面量的默认类型是 `Int`,浮点数字面量的默认类型是 `Double`,字符串字面量的默认类型是 `String`,布尔值字面量的默认类型是 `Bool`。比如,在 `let str = "Hello, world"` 这个声明中,字符串 `"Hello, world"` 的默认推导类型就是 `String`。
@ -114,26 +125,25 @@ true // 布尔值字面量
当为一个字面量值指定了类型标注的时候,这个标注的类型必须能通过这个字面量值实例化。也就是说,这个类型必须符合这些 Swift 标准库协议中的一个:整数字面量的 `IntegerLiteralConvertible` 协议、浮点数字面量的 `FloatingPointLiteralConvertible` 协议、字符串字面量的 `StringLiteralConvertible` 协议以及布尔值字面量的 `BooleanLiteralConvertible` 协议。比如,`Int8` 符合 `IntegerLiteralConvertible` 协议,因此它能在 `let x: Int8 = 42` 这个声明中作为整数字面量 `42` 的类型标注。 当为一个字面量值指定了类型标注的时候,这个标注的类型必须能通过这个字面量值实例化。也就是说,这个类型必须符合这些 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> <a id="numeric-literal"></a>
> *数值字面量* → **-**<sub>可选</sub> [*整数字面量*](#integer-literal) | **-**<sub>可选</sub> [*浮点数字面量*](#floating-point-literal) > *数值字面量* → **-**<sub>可选</sub> [*整数字面量*](#integer-literal) | **-**<sub>可选</sub> [*浮点数字面量*](#floating-point-literal)
<a id="boolean-literal"></a> > <a id="boolean-literal"></a>
> *布尔值字面量* → **true** | **false** > *布尔值字面量* → **true** | **false**
<a id="nil-literal"></a> > <a id="nil-literal"></a>
> *nil 字面量* → **nil** > *nil 字面量* → **nil**
<a id="integer_literals"></a> <a id="integer_literals"></a>
### 整数字面量 ### 整数字面量
整数字面量表示未指定精度整数的值。整数字面量默认用十进制表示,可以加前缀来指定其他的进制二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x`。 *整数字面量 (Integer Literals)* 表示未指定精度整数的值。整数字面量默认用十进制表示,可以加前缀来指定其他的进制二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x`。
十进制字面量包含数字 `0` 至 `9`。二进制字面量只包含 `0` 或 `1`,八进制字面量包含数字 `0` 至 `7`,十六进制字面量包含数字 `0` 至 `9` 以及字母 `A` 至 `F`(大小写均可)。 十进制字面量包含数字 `0` 至 `9`。二进制字面量只包含 `0` 或 `1`,八进制字面量包含数字 `0` 至 `7`,十六进制字面量包含数字 `0` 至 `9` 以及字母 `A` 至 `F`(大小写均可)。
负整数的字面量在整数字面量前加负号 `-`,比如 `-42`。 负整数的字面量在整数字面量前加负号 `-`,比如 `-42`。
整型字面面可以使用下划线 `_` 来增加数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。 整型字面面可以使用下划线 (`_`) 来增加数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`这同样也会被系统所忽略,并不会影响字面量的值。
除非特别指定,整数字面量的默认推导类型为 Swift 标准库类型中的 `Int`。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 [整数](../chapter2/01_The_Basics.html#integers)。 除非特别指定,整数字面量的默认推导类型为 Swift 标准库类型中的 `Int`。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 [整数](../chapter2/01_The_Basics.html#integers)。
@ -149,54 +159,54 @@ true // 布尔值字面量
> *二进制字面量* → **0b** [*二进制数字*](#binary-digit) [*二进制字面量字符组*](#binary-literal-characters)<sub>可选</sub> > *二进制字面量* → **0b** [*二进制数字*](#binary-digit) [*二进制字面量字符组*](#binary-literal-characters)<sub>可选</sub>
> <a id="binary-digit"></a> > <a id="binary-digit"></a>
> *二进制数字* → 数值 0 到 1 > *二进制数字* → 数值 0 到 1
<a id="binary-literal-character"></a> > <a id="binary-literal-character"></a>
> *二进制字面量字符* → [*二进制数字*](#binary-digit) | _ > *二进制字面量字符* → [*二进制数字*](#binary-digit) | _
<a id="binary-literal-characters"></a> > <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> <a id="octal-literal"></a>
> *八进制字面量* → **0o** [*八进字数字*](#octal-digit) [*八进制字符组*](#octal-literal-characters)<sub>可选</sub> > *八进制字面量* → **0o** [*八进字数字*](#octal-digit) [*八进制字符组*](#octal-literal-characters)<sub>可选</sub>
<a id="octal-digit"></a> > <a id="octal-digit"></a>
> *八进字数字* → 数值 0 到 7 > *八进字数字* → 数值 0 到 7
<a id="octal-literal-character"></a> > <a id="octal-literal-character"></a>
> *八进制字符* → [*八进字数字*](#octal-digit) | _ > *八进制字符* → [*八进字数字*](#octal-digit) | _
<a id="octal-literal-characters"></a> > <a id="octal-literal-characters"></a>
> *八进制字符组* → [*八进制字符*](#octal-literal-character) [*八进制字符组*](#octal-literal-characters)<sub>可选</sub> > *八进制字符组* → [*八进制字符*](#octal-literal-character) [*八进制字符组*](#octal-literal-characters)<sub>可选</sub>
<a id="decimal-literal"></a> <a id="decimal-literal"></a>
> *十进制字面量* → [*十进制数字*](#decimal-digit) [*十进制字符组*](#decimal-literal-characters)<sub>可选</sub> > *十进制字面量* → [*十进制数字*](#decimal-digit) [*十进制字符组*](#decimal-literal-characters)<sub>可选</sub>
<a id="decimal-digit"></a> > <a id="decimal-digit"></a>
> *十进制数字* → 数值 0 到 9 > *十进制数字* → 数值 0 到 9
<a id="decimal-digits"></a> > <a id="decimal-digits"></a>
> *十进制数字组* → [*十进制数字*](#decimal-digit) [*十进制数字组*](#decimal-digits)<sub>可选</sub> > *十进制数字组* → [*十进制数字*](#decimal-digit) [*十进制数字组*](#decimal-digits)<sub>可选</sub>
<a id="decimal-literal-character"></a> > <a id="decimal-literal-character"></a>
> *十进制字符* → [*十进制数字*](#decimal-digit) | _ > *十进制字符* → [*十进制数字*](#decimal-digit) | _
<a id="decimal-literal-characters"></a> > <a id="decimal-literal-characters"></a>
> *十进制字符组* → [*十进制字符*](#decimal-literal-character) [*十进制字符组*](#decimal-literal-characters)<sub>可选</sub> > *十进制字符组* → [*十进制字符*](#decimal-literal-character) [*十进制字符组*](#decimal-literal-characters)<sub>可选</sub>
<a id="hexadecimal-literal"></a> <a id="hexadecimal-literal"></a>
> *十六进制字面量* → **0x** [*十六进制数字*](#hexadecimal-digit) [*十六进制字面量字符组*](#hexadecimal-literal-characters)<sub>可选</sub> > *十六进制字面量* → **0x** [*十六进制数字*](#hexadecimal-digit) [*十六进制字面量字符组*](#hexadecimal-literal-characters)<sub>可选</sub>
<a id="hexadecimal-digit"></a> > <a id="hexadecimal-digit"></a>
> *十六进制数字* → 数值 0 到 9, 字母 a 到 f, 或 A 到 F > *十六进制数字* → 数值 0 到 9, 字母 a 到 f, 或 A 到 F
<a id="hexadecimal-literal-character"></a> > <a id="hexadecimal-literal-character"></a>
> *十六进制字符* → [*十六进制数字*](#hexadecimal-digit) | _ > *十六进制字符* → [*十六进制数字*](#hexadecimal-digit) | _
<a id="hexadecimal-literal-characters"></a> > <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> <a id="floating_point_literals"></a>
### 浮点数字面量 ### 浮点数字面量
浮点数字面量表示未指定精度浮点数的值。 *浮点数字面量 (Floating-point literals)* 表示未指定精度浮点数的值。
浮点数字面量默认用十进制表示(无前缀),也可以用十六进制表示(加前缀 `0x`)。 浮点数字面量默认用十进制表示(无前缀),也可以用十六进制表示(加前缀 `0x`)。
十进制浮点数字面量由十进制数字串后跟小数部分或指数部分(或两者皆有)组成。十进制小数部分由小数点 `.` 后跟十进制数字串组成。指数部分由大写或小写字母 `e` 为前缀后跟十进制数字串组成,这串数字表示 `e` 之前的数量乘以 10 的几次方。例如:`1.25e2` 表示 `1.25 10^2`,也就是 `125.0`;同样,`1.25e2` 表示 `1.25 10^2`,也就是 `0.0125`。 十进制浮点数字面量由十进制数字串后跟小数部分或指数部分(或两者皆有)组成。十进制小数部分由小数点 (`.`) 后跟十进制数字串组成。指数部分由大写或小写字母 `e` 为前缀后跟十进制数字串组成,这串数字表示 `e` 之前的数量乘以 10 的几次方。例如:`1.25e2` 表示 1.25 x 10²,也就是 `125.0`;同样,`1.25e2` 表示 1.25 x 10¯²,也就是 `0.0125`。
十六进制浮点数字面量由前缀 `0x` 后跟可选的十六进制小数部分以及十六进制指数部分组成。十六进制小数部分由小数点后跟十六进制数字串组成。指数部分由大写或小写字母 `p` 为前缀后跟十进制数字串组成,这串数字表示 `p` 之前的数量乘以 2 的几次方。例如:`0xFp2` 表示 `15 2^2`,也就是 `60`;同样,`0xFp-2` 表示 `15 2^-2`,也就是 `3.75`。 十六进制浮点数字面量由前缀 `0x` 后跟可选的十六进制小数部分以及十六进制指数部分组成。十六进制小数部分由小数点后跟十六进制数字串组成。指数部分由大写或小写字母 `p` 为前缀后跟十进制数字串组成,这串数字表示 `p` 之前的数量乘以 2 的几次方。例如:`0xFp2` 表示 15 x 2²,也就是 `60`;同样,`0xFp-2` 表示 15 x 2¯²,也就是 `3.75`。
负数的浮点数字面量由负号 `-` 和浮点数字面量组成,例如 `-42.5`。 负数的浮点数字面量由负号 (`-`) 和浮点数字面量组成,例如 `-42.5`。
浮点数字面量允许使用下划线 `_` 来增强数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。 浮点数字面量允许使用下划线 (`_`) 来增强数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。
除非特别指定,浮点数字面量的默认推导类型为 Swift 标准库类型中的 `Double`,表示 64 位浮点数。Swift 标准库也定义了 `Float` 类型,表示 32 位浮点数。 除非特别指定,浮点数字面量的默认推导类型为 Swift 标准库类型中的 `Double`,表示 64 位浮点数。Swift 标准库也定义了 `Float` 类型,表示 32 位浮点数。
@ -208,19 +218,19 @@ true // 布尔值字面量
<a id="decimal-fraction"></a> <a id="decimal-fraction"></a>
> *十进制分数* → **.** [*十进制字面量*](#decimal-literal) > *十进制分数* → **.** [*十进制字面量*](#decimal-literal)
<a id="decimal-exponent"></a> > <a id="decimal-exponent"></a>
> *十进制指数* → [*十进制指数 e*](#floating-point-e) [*正负号*](#sign)<sub>可选</sub> [*十进制字面量*](#decimal-literal) > *十进制指数* → [*十进制指数 e*](#floating-point-e) [*正负号*](#sign)<sub>可选</sub> [*十进制字面量*](#decimal-literal)
<a id="hexadecimal-fraction"></a> <a id="hexadecimal-fraction"></a>
> *十六进制分数* → **.** [*十六进制数字*](#hexadecimal-digit) [*十六进制字面量字符组*](#hexadecimal-literal-characters)<sub>可选</sub> > *十六进制分数* → **.** [*十六进制数字*](#hexadecimal-digit) [*十六进制字面量字符组*](#hexadecimal-literal-characters)<sub>可选</sub>
<a id="hexadecimal-exponent"></a> > <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> <a id="floating-point-e"></a>
> *十进制指数 e* → **e** | **E** > *十进制指数 e* → **e** | **E**
<a id="floating-point-p"></a> > <a id="floating-point-p"></a>
> *十六进制指数 p* → **p** | **P** > *十六进制指数 p* → **p** | **P**
<a id="sign"></a> > <a id="sign"></a>
> *正负号* → **+** | **-** > *正负号* → **+** | **-**
<a id="string_literals"></a> <a id="string_literals"></a>
@ -243,7 +253,7 @@ true // 布尔值字面量
* 单引号 `\'` * 单引号 `\'`
* Unicode 标量 `\u{`n`}`n 为一到八位的十六进制数字 * Unicode 标量 `\u{`n`}`n 为一到八位的十六进制数字
字符串字面量允许在反斜杠 `\` 后的括号 `()` 中插入表达式的值。插入表达式可以包含字符串字面量,但不能包含未转义的双引号 `"`、未转义的反斜线 `\`、回车符换行符。 字符串字面量允许在反斜杠 (`\`) 后的括号 `()` 中插入表达式的值。插入表达式可以包含字符串字面量,但不能包含未转义的双引号 (`"`)、未转义的反斜线 (`\`)、回车符以及换行符。
例如,以下所有字符串字面量的值都是相同的: 例如,以下所有字符串字面量的值都是相同的:
@ -271,23 +281,23 @@ let textB = "Hello world"
<a id="static-string-literal"></a> <a id="static-string-literal"></a>
> *静态字符串字面量* → **"**[*引用文本*](#quoted-text)<sub>可选</sub>**"** > *静态字符串字面量* → **"**[*引用文本*](#quoted-text)<sub>可选</sub>**"**
<a id="quoted-text"></a> > <a id="quoted-text"></a>
> *引用文本* → [*引用文本项*](#quoted-text-item) [*引用文本*](#quoted-text)<sub>可选</sub> > *引用文本* → [*引用文本项*](#quoted-text-item) [*引用文本*](#quoted-text)<sub>可选</sub>
<a id="quoted-text-item"></a> > <a id="quoted-text-item"></a>
> *引用文本项* → [*转义字符*](#escaped-character) > *引用文本项* → [*转义字符*](#escaped-character)
> *引用文本项* → 除了 **"**、**\\**、U+000A、U+000D 以外的所有 Unicode 字符 > *引用文本项* → 除了 **"**、**\\**、U+000A、U+000D 以外的所有 Unicode 字符
<a id="interpolated-string-literal"></a> <a id="interpolated-string-literal"></a>
> *插值字符串字面量* → **"**[*插值文本*](#interpolated-text)<sub>可选</sub>**"** > *插值字符串字面量* → **"**[*插值文本*](#interpolated-text)<sub>可选</sub>**"**
<a id="interpolated-text"></a> > <a id="interpolated-text"></a>
> *插值文本* → [*插值文本项*](#interpolated-text-item) [*插值文本*](#interpolated-text)<sub>可选</sub> > *插值文本* → [*插值文本项*](#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) > *插值文本项* → **\\****(**[*表达式*](./04_Expressions.html)**)** | [*引用文本项*](#quoted-text-item)
<a id="escaped-character"></a> <a id="escaped-character"></a>
> *转义字符* → **\\****0** | **\\****\\** | **\t** | **\n** | **\r** | **\\"** | **\\'** > *转义字符* → **\\****0** | **\\****\\** | **\t** | **\n** | **\r** | **\\"** | **\\'**
> *转义字符* → **\u {** [*unicode 标量数字*](#unicode-scalar-digits) **}** > *转义字符* → **\u {** [*unicode 标量数字*](#unicode-scalar-digits) **}**
<a id="unicode-scalar-digits"></a> > <a id="unicode-scalar-digits"></a>
> *unicode 标量数字* → 一到八位的十六进制数字 > *unicode 标量数字* → 一到八位的十六进制数字
<a id="operators"></a> <a id="operators"></a>
@ -295,17 +305,21 @@ let textB = "Hello world"
Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基础运算符](../chapter2/02_Basic_Operators.html) 和 [高级运算符](../chapter2/25_Advanced_Operators.html) 中进行了阐述。这一小节将描述哪些字符能用于自定义运算符。 Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基础运算符](../chapter2/02_Basic_Operators.html) 和 [高级运算符](../chapter2/25_Advanced_Operators.html) 中进行了阐述。这一小节将描述哪些字符能用于自定义运算符。
自定义运算符可以由以下其中之一的 ASCII 字符 `/``=``-``+``!``*``%``<``>``&``|``^``?` 以及 `~`,或者后面语法中规定的任一个 Unicode 字符开始。在第一个字符之后,允许使用组合型 Unicode 字符。也可以使用两个或者多个的点号来自定义运算符(比如,`....`)。虽然可以自定义包含问号 `?` 的运算符,但是这个运算符不能只包含单独的一个问号 自定义运算符可以由以下其中之一的 ASCII 字符 `/``=``-``+``!``*``%``<``>``&``|``^``?` 以及 `~`,或者后面语法中规定的任一个 Unicode 字符(其中包含了*数学运算符*、*零散符号(Miscellaneous Symbols)* 以及印刷符号 (Dingbats) 之类的 Unicode 块)开始。在第一个字符之后,允许使用组合型 Unicode 字符。
您也可以以点号 (`.`) 开头来定义自定义运算符。这些运算符可以包含额外的点,例如 `.+.`。如果某个运算符不是以点号开头的,那么它就无法再包含另外的点号了。例如,`+.+` 就会被看作为一个 `+` 运算符后面跟着一个 `.+` 运算符。
虽然您可以用问号 `?` 来自定义运算符,但是这个运算符不能只包含单独的一个问号。此外,虽然运算符可以包含一个惊叹号 `!`,但是前缀运算符不能够以问号或者惊叹号开头。
> 注意 > 注意
以下这些标记 `=``->``//``/*``*/``.``<`(前缀运算符)、`&``?``?`(中缀运算符)、`>`(后缀运算符)、`!``?` 是被系统保留的。这些符号不能被重载,也不能用于自定义运算符。 > 以下这些标记 `=``->``//``/*``*/`、`.`、`<`(前缀运算符)、`&`、`?`、`?`(中缀运算符)、`>`(后缀运算符)、`!` 、`?` 是被系统保留的。这些符号不能被重载,也不能用于自定义运算符。
运算符两侧的空白被用来区分该运算符是否为前缀运算符、后缀运算符或二元运算符。规则总结如下: 运算符两侧的空白被用来区分该运算符是否为前缀运算符、后缀运算符或二元运算符。规则总结如下:
* 如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:`a+b``a + b` 中的运算符 `+` 被看作二元运算符。 * 如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:`a+++b``a +++ b` 中的 `+++` 运算符会被看作二元运算符。
* 如果运算符只有左侧空白,将被看作前缀一元运算符。例如 `a ++b` 中的 `++` 被看作前缀一元运算符。 * 如果运算符只有左侧空白,将被看作一元前缀运算符。例如 `a +++b` 中的 `+++` 运算符会被看做是一元前缀运算符。
* 如果运算符只有右侧空白,将被看作后缀一元运算符。例如 `a++ b` 中的 `++` 被看作后缀一元运算符。 * 如果运算符只有右侧空白,将被看作一元后缀运算符。例如 `a+++ b` 中的 `+++` 运算符会被看作是一元后缀运算符。
* 如果运算符左侧没有空白并紧跟 `.`,将被看作后缀一元运算符。例如 `a++.b` 中的 `++` 被看作后缀一元运算符(即上式被视为 `a++ .b` `a ++ .b`)。 * 如果运算符左侧没有空白并紧跟 `.`,将被看作一元后缀运算符。例如 `a+++.b` 中的 `+++` 运算符会被视为一元后缀运算符(即上式被视为 `a+++ .b`不是 `a +++ .b`)。
鉴于这些规则,运算符前的字符 `(``[``{`,运算符后的字符 `)``]``}`,以及字符 `,``;``:` 都被视为空白。 鉴于这些规则,运算符前的字符 `(``[``{`,运算符后的字符 `)``]``}`,以及字符 `,``;``:` 都被视为空白。
@ -346,19 +360,19 @@ Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基
> *运算符字符* → U+FE00U+FE0F > *运算符字符* → U+FE00U+FE0F
> *运算符字符* → U+FE20U+FE2F > *运算符字符* → U+FE20U+FE2F
> *运算符字符* → U+E0100U+E01EF > *运算符字符* → U+E0100U+E01EF
<a id="operator-characters"></a> > <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> > <a id="dot-operator-character"></a>
> *点运算符字符* → **.** | [*运算符字符*](#operator-character) > *点运算符字符* → **.** | [*运算符字符*](#operator-character)
<a id="dot-operator-characters"></a> > <a id="dot-operator-characters"></a>
> *点运算符字符组* → [*点运算符字符*](#dot-operator-character) [*点运算符字符组*](#dot-operator-characters)<sub>可选</sub> > *点运算符字符组* → [*点运算符字符*](#dot-operator-character) [*点运算符字符组*](#dot-operator-characters)<sub>可选</sub>
<a id="binary-operator"></a> <a id="binary-operator"></a>
> *二元运算符* → [*运算符*](#operator) > *二元运算符* → [*运算符*](#operator)
<a id="prefix-operator"></a> > <a id="prefix-operator"></a>
> *前缀运算符* → [*运算符*](#operator) > *前缀运算符* → [*运算符*](#operator)
<a id="postfix-operator"></a> > <a id="postfix-operator"></a>
> *后缀运算符* → [*运算符*](#operator) > *后缀运算符* → [*运算符*](#operator)

View File

@ -11,6 +11,12 @@
> 2.1 > 2.1
> 翻译:[mmoaay](https://github.com/mmoaay) > 翻译:[mmoaay](https://github.com/mmoaay)
> 2.2
> 校对:[175](https://github.com/Brian175)
> 3.0
> 翻译+校对:[chenmingjia](https://github.com/chenmingjia)
本页包含内容: 本页包含内容:
- [前缀表达式](#prefix_expressions) - [前缀表达式](#prefix_expressions)
@ -27,6 +33,7 @@
- [隐式成员表达式](#implicit_member_expression) - [隐式成员表达式](#implicit_member_expression)
- [圆括号表达式](#parenthesized_expression) - [圆括号表达式](#parenthesized_expression)
- [通配符表达式](#wildcard_expression) - [通配符表达式](#wildcard_expression)
- [选择器表达式](#selector_expression)
- [后缀表达式](#postfix_expressions) - [后缀表达式](#postfix_expressions)
- [函数调用表达式](#function_call_expression) - [函数调用表达式](#function_call_expression)
- [构造器表达式](#initializer_expression) - [构造器表达式](#initializer_expression)
@ -37,9 +44,9 @@
- [强制取值表达式](#forced-Value_expression) - [强制取值表达式](#forced-Value_expression)
- [可选链表达式](#optional-chaining_expression) - [可选链表达式](#optional-chaining_expression)
Swift 中存在四种表达式:前缀表达式,二元表达式,基本表达式和后缀表达式。表达式可以返回一个值,还可以执行某些代码 Swift 中存在四种表达式:前缀表达式,二元表达式,基本表达式和后缀表达式。表达式返回一个值的同时还可以引发副作用
前缀表达式和二元表达式就是对某些表达式使用各种运算符。基本表达式是最短小的表达式,它提供了获取值的一种途径。后缀表达式则允许你建立复杂的表达式,例如函数调用和成员访问。每种表达式都在下面有详细论述。 通过前缀表达式和二元表达式可以对简单表达式使用各种运算符。基本表达式从概念上讲是最简单的一种表达式,它是一种访问值的方式。后缀表达式则允许你建立复杂的表达式,例如函数调用和成员访问。每种表达式都在下面有详细论述。
> 表达式语法 > 表达式语法
<a name="expression"></a> <a name="expression"></a>
@ -50,20 +57,20 @@ Swift 中存在四种表达式:前缀表达式,二元表达式,基本表
<a name="prefix_expressions"></a> <a name="prefix_expressions"></a>
## 前缀表达式 ## 前缀表达式
前缀表达式由可选的前缀运算符和表达式组成。前缀运算符只接收一个参数。 前缀表达式由可选的前缀运算符和表达式组成。前缀运算符只接收一个参数,表达式则紧随其后
关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.html) 和 [高级运算符](../chapter2/25_Advanced_Operators.html)。 关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.md) 和 [高级运算符](../chapter2/25_Advanced_Operators.md)。
关于 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)。 关于 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)。
除了标准库运算符,你也可以对某个变量使用 `&` 运算符,从而将其传递给函数的输入输出参数。 更多信息,请参阅 [输入输出参数](../chapter2/06_Functions.html#in_out_parameters)。 除了标准库运算符,你也可以对某个变量使用 `&` 运算符,从而将其传递给函数的输入输出参数。更多信息,请参阅 [输入输出参数](../chapter2/06_Functions.html#in_out_parameters)。
> 前缀表达式语法 > 前缀表达式语法
<a name="prefix-expression"></a> <a name="prefix-expression"></a>
> *前缀表达式* → [*前缀运算符*](02_Lexical_Structure.html#prefix-operator)<sub>可选</sub> [*后缀表达式*](#postfix-expression) > *前缀表达式* → [*前缀运算符*](02_Lexical_Structure.md#prefix-operator)<sub>可选</sub> [*后缀表达式*](#postfix-expression)
> *前缀表达式* → [*输入输出表达式*](#in-out-expression) > *前缀表达式* → [*输入输出表达式*](#in-out-expression)
<a name="in-out-expression"></a> <a name="in-out-expression"></a>
> *输入输出表达式* → **&** [*标识符*](02_Lexical_Structure.html#identifier) > *输入输出表达式* → **&** [*标识符*](02_Lexical_Structure.md#identifier)
<a name="try_operator"></a> <a name="try_operator"></a>
### try 运算符 ### try 运算符
@ -87,15 +94,14 @@ try 表达式由 `try` 运算符加上紧随其后的可抛出错误的表达式
在二进制运算符左侧的表达式被标记上 `try``try?` 或者 `try!` 时,这个运算符对整个二进制表达式都产生作用。也就是说,你可以使用括号来明确运算符的作用范围。 在二进制运算符左侧的表达式被标记上 `try``try?` 或者 `try!` 时,这个运算符对整个二进制表达式都产生作用。也就是说,你可以使用括号来明确运算符的作用范围。
```swift ```swift
sum = try someThrowingFunction() + anotherThrowingFunction() // try 对两个方法调用都产生作用 sum = try someThrowingFunction() + anotherThrowingFunction() // try 对两个函数调用都产生作用
sum = try (someThrowingFunction() + anotherThrowingFunction()) // try 对两个方法调用都产生作用 sum = try (someThrowingFunction() + anotherThrowingFunction()) // try 对两个函数调用都产生作用
sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误try 只对第一个方法调用产生作用 sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误try 只对第一个函数调用产生作用
``` ```
`try` 表达式不能出现在二进制运算符的的右侧,除非二进制运算符是赋值运算符或者 `try` 表达式是被圆括号括起来的。 `try` 表达式不能出现在二进制运算符的的右侧,除非二进制运算符是赋值运算符或者 `try` 表达式是被圆括号括起来的。
关于 `try``try?``try!` 的更多信息,以及如何使用的例子,请参阅 [错误处理](../chapter2/18_Error_Handling.html)。 关于 `try``try?``try!` 的更多信息,以及如何使用的例子,请参阅 [错误处理](../chapter2/18_Error_Handling.html)。
> try 表达式语法 > try 表达式语法
<a name="try-operator"></a> <a name="try-operator"></a>
> *try 运算符* → **try** | **try?** | **try!** > *try 运算符* → **try** | **try?** | **try!**
@ -103,20 +109,20 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误try
<a name="binary_expressions"></a> <a name="binary_expressions"></a>
## 二元表达式 ## 二元表达式
二元表达式形式如下: 二元表达式由中缀运算符和左右参数表达式组成。形式如下:
> `左侧参数` `二元运算符` `右侧参数` > `左侧参数` `二元运算符` `右侧参数`
关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.html) 和 [高级运算符](../chapter2/25_Advanced_Operators.html)。 关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.md) 和 [高级运算符](../chapter2/25_Advanced_Operators.md)。
关于 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)。 关于 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))`。 > 在解析时,一个二元表达式将作为一个扁平列表表示,然后根据运算符的优先级,再进一步进行组合。例如,`2 + 3 * 5` 首先被看作具有五个元素的列表,即 `2`、`+`、`3`、`*`、`5`,随后根据运算符优先级组合为 `(2 + (3 * 5))`。
<a name="binary-expression"></a> <a name="binary-expression"></a>
> 二元表达式语法 > 二元表达式语法
> *二元表达式* → [*二元运算符*](02_Lexical_Structure.html#binary-operator) [*前缀表达式*](#prefix-expression) > *二元表达式* → [*二元运算符*](02_Lexical_Structure.md#binary-operator) [*前缀表达式*](#prefix-expression)
> *二元表达式* → [*赋值运算符*](#assignment-operator) [*try运算符*](#try-operator)<sub>可选</sub> [*前缀表达式*](#prefix-expression) > *二元表达式* → [*赋值运算符*](#assignment-operator) [*try运算符*](#try-operator)<sub>可选</sub> [*前缀表达式*](#prefix-expression)
> *二元表达式* → [*条件运算符*](#conditional-operator) [*try运算符*](#try-operator)<sub>可选</sub> [*前缀表达式*](#prefix-expression) > *二元表达式* → [*条件运算符*](#conditional-operator) [*try运算符*](#try-operator)<sub>可选</sub> [*前缀表达式*](#prefix-expression)
> *二元表达式* → [*类型转换运算符*](#type-casting-operator) > *二元表达式* → [*类型转换运算符*](#type-casting-operator)
@ -130,7 +136,7 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误try
> `表达式` = `值` > `表达式` = `值`
右边的值会被赋值给左边的表达式。如果左边表达式是一个元组,那么右边必须是一个具有同样元素个数的元组。嵌套元组也是允许的。 右边的值会被赋值给左边的表达式。如果左边表达式是一个元组,那么右边必须是一个具有同样元素个数的元组。嵌套元组也是允许的。)右边的值中的每一部分都会被赋值给左边的表达式中的相应部分。例如:
```swift ```swift
(a, _, (b, c)) = ("test", 9.45, (12, 3)) (a, _, (b, c)) = ("test", 9.45, (12, 3))
@ -152,7 +158,7 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误try
如果条件为真,那么对第一个表达式进行求值并返回结果。否则,对第二个表达式进行求值并返回结果。未使用的表达式不会进行求值。 如果条件为真,那么对第一个表达式进行求值并返回结果。否则,对第二个表达式进行求值并返回结果。未使用的表达式不会进行求值。
关于使用三元条件运算符的例子,请参阅 [三元条件运算符](../chapter2/02_Basic_Operators.html#ternary_conditional_operator)。 关于使用三元条件运算符的例子,请参阅 [三元条件运算符](../chapter2/02_Basic_Operators.md#ternary_conditional_operator)。
> 三元条件运算符语法 > 三元条件运算符语法
<a name="conditional-operator"></a> <a name="conditional-operator"></a>
@ -161,11 +167,11 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误try
<a name="type-casting_operators"></a> <a name="type-casting_operators"></a>
### 类型转换运算符 ### 类型转换运算符
有 4 种类型转换运算符:`is``as``? ``!`。它们有如下的形式: 有 4 种类型转换运算符:`is``as``as? ``as!`。它们有如下的形式:
> `表达式` is `类型` > `表达式` is `类型`
`表达式` as `类型` `表达式` as `类型`
`表达式` is? `类型` `表达式` as? `类型`
`表达式` as! `类型` `表达式` as! `类型`
`is` 运算符在运行时检查表达式能否向下转化为指定的类型,如果可以则返回 `ture`,否则返回 `false` `is` 运算符在运行时检查表达式能否向下转化为指定的类型,如果可以则返回 `ture`,否则返回 `false`
@ -187,25 +193,25 @@ f(x as Any)
// 打印 “Function for Any” // 打印 “Function for Any”
``` ```
桥接可将 Swift 标准库中的类型(例如 `String`)作为一个与之相关的 Foundation 类型(例如 `NSString`)来使用,而不需要新建一个实例。关于桥接的更多信息,请参阅 [*Using Swift with Cocoa and Objective-C*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中的 [Working with Cocoa Data Types](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)。 桥接可将 Swift 标准库中的类型(例如 `String`)作为一个与之相关的 Foundation 类型(例如 `NSString`)来使用,而不需要新建一个实例。关于桥接的更多信息,请参阅 [*Using Swift with Cocoa and Objective-C (Swift2.2)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中的 [Working with Cocoa Data Types](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)。
`as?` 运算符有条件地执行类型转换,返回目标类型的可选值。在运行时,如果转换成功,返回的可选值将包含转换后的值,否则返回 `nil`。如果在编译时就能确定转换一定会成功或是失败,则会编译错。 `as?` 运算符有条件地执行类型转换,返回目标类型的可选值。在运行时,如果转换成功,返回的可选值将包含转换后的值,否则返回 `nil`。如果在编译时就能确定转换一定会成功或是失败,则会导致编译错。
`as!` 运算符执行强制类型转换,返回目标类型的非可选值。如果转换失败,则会导致运行时错误。表达式 `x as T` 效果等同于 `(x as? T)!` `as!` 运算符执行强制类型转换,返回目标类型的非可选值。如果转换失败,则会导致运行时错误。表达式 `x as! T` 效果等同于 `(x as? T)!`
关于类型转换的更多内容和例子,请参阅 [类型转换](../chapter2/19_Type_Casting.html)。 关于类型转换的更多内容和例子,请参阅 [类型转换](../chapter2/19_Type_Casting.md)。
<a name="type-casting-operator"></a> <a name="type-casting-operator"></a>
> 类型转换运算符语法 > 类型转换运算符语法
> *类型转换运算符* → **is** [*类型*](03_Types.html#type) > *类型转换运算符* → **is** [*类型*](03_Types.md#type)
> *类型转换运算符* → **as** [*类型*](03_Types.html#type) > *类型转换运算符* → **as** [*类型*](03_Types.md#type)
> *类型转换运算符* → **is** **?** [*类型*](03_Types.html#type) > *类型转换运算符* → **as** **?** [*类型*](03_Types.md#type)
> *类型转换运算符* → **as** **!** [*类型*](03_Types.html#type) > *类型转换运算符* → **as** **!** [*类型*](03_Types.md#type)
<a name="primary_expressions"></a> <a name="primary_expressions"></a>
## 基本表达式 ## 基本表达式
基本表达式是最基本的表达式。 它们可以跟前缀表达式、二元表达式、后缀表达式以及其他基本表达式组合使用。 基本表达式是最基本的表达式。它们可以单独使用,也可以跟前缀表达式、二元表达式、后缀表达式组合使用。
> 基本表达式语法 > 基本表达式语法
<a name="primary-expression"></a> <a name="primary-expression"></a>
@ -217,6 +223,7 @@ f(x as Any)
> *基本表达式* → [*圆括号表达式*](#parenthesized-expression) > *基本表达式* → [*圆括号表达式*](#parenthesized-expression)
> *基本表达式* → [*隐式成员表达式*](#implicit-member-expression) > *基本表达式* → [*隐式成员表达式*](#implicit-member-expression)
> *基本表达式* → [*通配符表达式*](#wildcard-expression) > *基本表达式* → [*通配符表达式*](#wildcard-expression)
> *基本表达式* → [*选择器表达式*](#selector-expression)
<a name="literal_expression"></a> <a name="literal_expression"></a>
### 字面量表达式 ### 字面量表达式
@ -225,17 +232,19 @@ f(x as Any)
字面量 | 类型 | 值 字面量 | 类型 | 值
:------------- | :---------- | :---------- :------------- | :---------- | :----------
`__FILE__` | `String` | 所在的文件名 `#file` | `String` | 所在的文件名
`__LINE__` | `Int` | 所在的行数 `#line` | `Int` | 所在的行数
`__COLUMN__` | `Int` | 所在的列数 `#column` | `Int` | 所在的列数
`__FUNCTION__` | `String` | 所在的声明的名字 `#function` | `String` | 所在的声明的名字
对于 `__FUNCTION__`,在函数中会返回当前函数的名字,在方法中会返回当前方法的名字,在属性的存取器中会返回属性的名字,在特殊的成员如 `init``subscript` 中会返回这个关键字的名字,在某个文件中会返回当前模块的名字 `#line`除了上述含义外,还有另一种含义。当它出现在单独一行时,会被理解成行控制语句,请参阅[线路控制语句](../chapter3/10_Statements.md#线路控制语句)
`__FUNCTION__` 作为函数或者方法的默认参数值时,该字面量的值取决于函数或方法调用时所处的环境 对于 `function`,在函数中会返回当前函数的名字,在方法中会返回当前方法的名字,在属性的存取器中会返回属性的名字,在特殊的成员如 `init``subscript` 中会返回这个关键字的名字,在某个文件中会返回当前模块的名字
`#function` 作为函数或者方法的默认参数值时,该字面量的值取决于函数或方法的调用环境。
```swift ```swift
func logFunctionName(string: String = __FUNCTION__) { func logFunctionName(string: String = #function) {
print(string) print(string)
} }
func myFunction() { func myFunction() {
@ -248,7 +257,7 @@ myFunction() // 打印 “myFunction()”
> [`值 1`, `值 2`, `...`] > [`值 1`, `值 2`, `...`]
数组中的最后一个表达式可以紧跟一个逗号。数组字面量的类型是 `[T]`,这个 `T` 就是数组中元素的类型。如果数组中包含多种类型,`T` 则是跟这些类型最近的的公共父类型。空数组字面量由一组方括号定义,可用来创建特定类型的空数组。 数组中的最后一个表达式可以紧跟一个逗号。数组字面量的类型是 `[T]`,这个 `T` 就是数组中元素的类型。如果数组中包含多种类型,`T` 则是跟这些类型最近的的公共父类型。空数组字面量由一组方括号定义,可用来创建特定类型的空数组。
```swift ```swift
var emptyArray: [Double] = [] var emptyArray: [Double] = []
@ -258,7 +267,7 @@ var emptyArray: [Double] = []
> [`键 1` : `值 1`, `键 2` : `值 2`, `...`] > [`键 1` : `值 1`, `键 2` : `值 2`, `...`]
字典中的最后一个表达式可以紧跟一个逗号。字典字面量的类型是 `[Key : Value]``Key` 表示键的类型,`Value` 表示值的类型。如果字典中包含多种类型,那么 `Key` 表示的类型则为所有键最接近的公共父类型,`Value` 也是同样如此。一个空的字典字面量由方括号中加一个冒号组成(`[:]`),从而与空数组字面量区分开,可以使用空字典字面量来创建特定类型的字典。 字典中的最后一个表达式可以紧跟一个逗号。字典字面量的类型是 `[Key : Value]``Key` 表示键的类型,`Value` 表示值的类型。如果字典中包含多种类型,那么 `Key` 表示的类型则为所有键最接近的公共父类型,`Value` 与之相似。一个空的字典字面量由方括号中加一个冒号组成(`[:]`),从而与空数组字面量区分开,可以使用空字典字面量来创建特定类型的字典。
```swift ```swift
var emptyDictionary: [String : Double] = [:] var emptyDictionary: [String : Double] = [:]
@ -269,7 +278,7 @@ var emptyDictionary: [String : Double] = [:]
<a name="literal-expression"></a> <a name="literal-expression"></a>
> *字面量表达式* → [*字面量*](02_Lexical_Structure.md#literal) > *字面量表达式* → [*字面量*](02_Lexical_Structure.md#literal)
> *字面量表达式* → [*数组字面量*](#array-literal) | [*字典字面量*](#dictionary-literal) > *字面量表达式* → [*数组字面量*](#array-literal) | [*字典字面量*](#dictionary-literal)
> *字面量表达式* → **\_\_FILE\_\_** | **\_\_LINE\_\_** | **\_\_COLUMN\_\_** | **\_\_FUNCTION\_\_** > *字面量表达式* → **#file** | **#line** | **#column** | **#function**
<a name="array-literal"></a> <a name="array-literal"></a>
> *数组字面量* → **[** [*数组字面量项列表*](#array-literal-items)<sub>可选</sub> **]** > *数组字面量* → **[** [*数组字面量项列表*](#array-literal-items)<sub>可选</sub> **]**
@ -322,10 +331,14 @@ struct Point {
> self 表达式语法 > self 表达式语法
<a name="self-expression"></a> <a name="self-expression"></a>
> *self 表达式* → **self** > *self 表达式* → **self** | [*self 方法表达式*](#self-method-expression) [*self 下标表达式*](#self-subscript-expression) | [*self 构造器表达式*](#self-initializer-expression)
> *self 表达式* → **self** **.** [*标识符*](02_Lexical_Structure.md#identifier) >
> *self 表达式* → **self** **[** [*表达式*](#expression) **]** <a name="self-method-expression"></a>
> *self 表达式* → **self** **.** **init** > *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**
<a name="superclass_expression"></a> <a name="superclass_expression"></a>
### 超类表达式 ### 超类表达式
@ -353,7 +366,7 @@ struct Point {
<a name="closure_expression"></a> <a name="closure_expression"></a>
### 闭包表达式 ### 闭包表达式
闭包表达式会创建一个闭包,在其他语言中也叫匿名函数。跟函数一样,闭包包含了待执行的代码,不同的是闭包还会捕获所在环境中的常量和变量。它的形式如下: 闭包表达式会创建一个闭包,在其他语言中也叫 *lambda*匿名函数。跟函数一样,闭包包含了待执行的代码,不同的是闭包还会捕获所在环境中的常量和变量。它的形式如下:
```swift ```swift
{ (parameters) -> return type in { (parameters) -> return type in
@ -365,9 +378,9 @@ struct Point {
闭包还有几种特殊的形式,能让闭包使用起来更加简洁: 闭包还有几种特殊的形式,能让闭包使用起来更加简洁:
- 闭包可以省略它的参数和返回值的类型。如果省略了参数名和参数类型,也要省略 `in` 关键字。如果被省略的类型无法被编译器推断,那么就会导致编译错误。 - 闭包可以省略它的参数和返回值的类型。如果省略了参数名和所有的类型,也要省略 `in` 关键字。如果被省略的类型无法被编译器推断,那么就会导致编译错误。
- 闭包可以省略参数名,参数会被隐式命名为 `$` 上其索引位置,例如 `$0``$1``$2` 分别表示第一个、第二个、第三个参数,以此类推。 - 闭包可以省略参数名,参数会被隐式命名为 `$` 上其索引位置,例如 `$0``$1``$2` 分别表示第一个、第二个、第三个参数,以此类推。
- 如果闭包中只包含一个表达式,那么该表达式的结果就会自动成为闭包的返回值。表达式结果的类型也会被推断为闭包的返回类型。 - 如果闭包中只包含一个表达式,那么该表达式的结果就会被视为闭包的返回值。表达式结果的类型也会被推断为闭包的返回类型。
下面几个闭包表达式是等价的: 下面几个闭包表达式是等价的:
@ -391,11 +404,11 @@ myFunction { $0 + $1 }
#### 捕获列表 #### 捕获列表
默认情况下,闭包会通过强引用捕获所在环境中的常量和变量。你可以通过一个捕获列表来显式指定它的捕获行为。 默认情况下,闭包会捕获附近作用域中的常量和变量,并使用强引用指向它们。你可以通过一个捕获列表来显式指定它的捕获行为。
捕获列表在参数列表之前,由中括号括起来,里面是由逗号分隔的一系列表达式。一旦使用了捕获列表,就必须使用 `in` 关键字,即使省略了参数名、参数类型返回类型。 捕获列表在参数列表之前,由中括号括起来,里面是由逗号分隔的一系列表达式。一旦使用了捕获列表,就必须使用 `in` 关键字,即使省略了参数名、参数类型返回类型。
捕获列表中的条目会在闭包创建时被初始化。每一个条目都会闭包所在环境中的同名常量或者变量初始化。例如下面的代码示例中,捕获列表包含 `a` 而不包含 `b`,这将导致这两个变量具有不同的行为。 捕获列表中的会在闭包创建时被初始化。每一都会闭包附近作用域中的同名常量或者变量的值初始化。例如下面的代码示例中,捕获列表包含 `a` 而不包含 `b`,这将导致这两个变量具有不同的行为。
```swift ```swift
var a = 0 var a = 0
@ -412,7 +425,7 @@ closure()
在示例中,变量 `b` 只有一个,然而,变量 `a` 有两个,一个在闭包外,一个在闭包内。闭包内的变量 `a` 会在闭包创建时用闭包外的变量 `a` 的值来初始化,除此之外它们并无其他联系。这意味着在闭包创建后,改变某个 `a` 的值都不会对另一个 `a` 的值造成任何影响。与此相反,闭包内外都是同一个变量 `b`,因此在闭包外改变其值,闭包内的值也会受影响。 在示例中,变量 `b` 只有一个,然而,变量 `a` 有两个,一个在闭包外,一个在闭包内。闭包内的变量 `a` 会在闭包创建时用闭包外的变量 `a` 的值来初始化,除此之外它们并无其他联系。这意味着在闭包创建后,改变某个 `a` 的值都不会对另一个 `a` 的值造成任何影响。与此相反,闭包内外都是同一个变量 `b`,因此在闭包外改变其值,闭包内的值也会受影响。
如果闭包捕获的值引用语义,则又会有所不同。例如,下面示例中,有两个变量 `x`,一个在闭包外,一个在闭包内,由于它们的值是引用语义,虽然这是两个不同的变量,它们却都引用着同一实例。 如果闭包捕获的值具有引用语义有所不同。例如,下面示例中,有两个变量 `x`,一个在闭包外,一个在闭包内,由于它们的值是引用语义,虽然这是两个不同的变量,它们却都引用着同一实例。
```swift ```swift
class SimpleClass { class SimpleClass {
@ -438,7 +451,7 @@ myFunction { [weak self] in print(self!.title) } // 以弱引用捕获
myFunction { [unowned self] in print(self.title) } // 以无主引用捕获 myFunction { [unowned self] in print(self.title) } // 以无主引用捕获
``` ```
在捕获列表中,也可以使用任意表达式来赋值。该表达式会在闭包被创建时进行求值,闭包会按照指定的引用类型来捕获表达式的值。例如: 在捕获列表中,也可以任意表达式的值绑定到一个常量上。该表达式会在闭包被创建时进行求值,闭包会按照指定的引用类型来捕获表达式的值。例如:
```swift ```swift
// 以弱引用捕获 self.parent 并赋值给 parent // 以弱引用捕获 self.parent 并赋值给 parent
@ -471,7 +484,7 @@ myFunction { [weak parent = self.parent] in print(parent!.title) }
<a name="implicit_member_expression"></a> <a name="implicit_member_expression"></a>
### 隐式成员表达式 ### 隐式成员表达式
在可以判断出类型的上下文中,隐式成员表达式访问某个类型的成员(例如某个枚举成员或某个类型方法)的简洁方法,形式如下: 若类型可被推断出来,可以使用隐式成员表达式访问某个类型的成员(例如某个枚举成员或某个类型方法),形式如下:
> .`成员名称` > .`成员名称`
@ -489,7 +502,7 @@ x = .AnotherValue
<a name="parenthesized_expression"></a> <a name="parenthesized_expression"></a>
### 圆括号表达式 ### 圆括号表达式
圆括号表达式由多个逗号分隔的子表达式组成。每个子表达式前面可以有一个标识符,用冒号隔开,其形式如下: 圆括号表达式由圆括号和其中多个逗号分隔的子表达式组成。每个子表达式前面可以有一个标识符,用冒号隔开。圆括号表达式形式如下:
> (`标识符 1` : `表达式 1`, `标识符 2` : `表达式 2`, `...`) > (`标识符 1` : `表达式 1`, `标识符 2` : `表达式 2`, `...`)
@ -506,7 +519,7 @@ x = .AnotherValue
<a name="wildcard_expression"></a> <a name="wildcard_expression"></a>
### 通配符表达式 ### 通配符表达式
通配符表达式用来忽略传递进来的某个参数。例如下面的代码中,`10`传递`x``20` 被忽略: 通配符表达式可以在赋值过程中显式忽略某个值。例如下面的代码中,`10`赋值`x``20` 被忽略:
```swift ```swift
(x, _) = (10, 20) (x, _) = (10, 20)
@ -517,12 +530,62 @@ x = .AnotherValue
<a name="wildcard-expression"></a> <a name="wildcard-expression"></a>
> *通配符表达式* → **_** > *通配符表达式* → **_**
<a name="selector_expression"></a>
### 选择器表达式
选择器表达式可以让你通过选择器来引用在Objective-C中方法(method)和属性(property)的setter和getter方法。
> \#selector(方法名)
\#selector(getter: 属性名)
\#selector(setter: 属性名)
方法名和属性名必须是存在于 Objective-C 运行时中的方法和属性的引用。选择器表达式的返回值是一个 Selector 类型的实例。例如:
```swift
class SomeClass: NSObject {
let property: String
@objc(doSomethingWithInt:)
func doSomething(_ x: Int) { }
init(property: String) {
self.property = property
}
}
let selectorForMethod = #selector(SomeClass.doSomething(_:))
let selectorForPropertyGetter = #selector(getter: SomeClass.property)
```
当为属性的getter创建选择器时,属性名可以是变量属性或者常量属性的引用。但是当为属性的setter创建选择器时,属性名只可以是对变量属性的引用。
方法名称可以包含圆括号来进行分组,并使用as 操作符来区分具有相同方法名但类型不同的方法, 例如:
```swift
extension SomeClass {
@objc(doSomethingWithString:)
func doSomething(_ x: String) { }
}
let anotherSelector = #selector(SomeClass.doSomething(_:) as (SomeClass) -> (String) -> Void)
```
由于选择器是在编译时创建的,因此编译器可以检查方法或者属性是否存在,以及是否在运行时暴露给了 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__ **(** [*setter:表达式*](#expression) **)**
<a name="postfix_expressions"></a> <a name="postfix_expressions"></a>
## 后缀表达式 ## 后缀表达式
后缀表达式就是在某个表达式的后面加上后缀运算符。严格地讲,每个基本表达式也是一个后缀表达式。 后缀表达式就是在某个表达式的后面运用后缀运算符或其他后缀语法。从语法构成上来看,基本表达式也是后缀表达式。
关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.html) 和 [高级运算符](../chapter2/25_Advanced_Operators.html)。 关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.md) 和 [高级运算符](../chapter2/25_Advanced_Operators.md)。
关于 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)。 关于 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)。
@ -582,12 +645,11 @@ myData.someMethod {$0 == 13}
> `表达式`.init(`构造器参数`) > `表达式`.init(`构造器参数`)
你可以在函数调用表达式中使用构造器表达式来初始化某个类型的新实例。也可以使用构造器表达式来代理超类构造器。 你可以在函数调用表达式中使用构造器表达式来初始化某个类型的新实例。也可以使用构造器表达式来代理超类构造器。
```swift ```swift
class SomeSubClass: SomeSuperClass { class SomeSubClass: SomeSuperClass {
init() { override init() {
// 此处为子类构造过程 // 此处为子类构造过程
super.init() super.init()
} }
@ -606,7 +668,6 @@ print(oneTwoThree)
如果通过名字来指定某个类型,可以不用构造器表达式而直接使用类型的构造器。在其他情况下,你必须使用构造器表达式。 如果通过名字来指定某个类型,可以不用构造器表达式而直接使用类型的构造器。在其他情况下,你必须使用构造器表达式。
```swift ```swift
let s1 = SomeType.init(data: 3) // 有效 let s1 = SomeType.init(data: 3) // 有效
let s2 = SomeType(data: 1) // 有效 let s2 = SomeType(data: 1) // 有效
@ -617,12 +678,13 @@ let s3 = someValue.dynamicType.init(data: 7) // 有效
> 构造器表达式语法 > 构造器表达式语法
<a name="initializer-expression"></a> <a name="initializer-expression"></a>
> *构造器表达式* → [*后缀表达式*](postfix-expression) **.** **init** > *构造器表达式* → [*后缀表达式*](#postfix-expression) **.** **init**
> *构造器表达式* → [*后缀表达式*](#postfix-expression) **.** **init** **(** [*参数名称*](#argument-names) **)**
<a name="explicit_member_expression"></a> <a name="explicit_member_expression"></a>
### 显式成员表达式 ### 显式成员表达式
显式成员表达式允许我们访问命名类型、元组或者模块的成员,形式如下: 显式成员表达式允许我们访问命名类型、元组或者模块的成员,形式如下:
> `表达式`.`成员名` > `表达式`.`成员名`
@ -636,7 +698,7 @@ let c = SomeClass()
let y = c.someProperty // 访问成员 let y = c.someProperty // 访问成员
``` ```
元组的成员会根据表示它们出现顺序的整数来隐式命名,以 0 开始,例如: 元组的成员会隐式地根据表示它们出现顺序的整数来命名,以 0 开始,例如:
```swift ```swift
var t = (10, 20, 30) var t = (10, 20, 30)
@ -646,22 +708,56 @@ t.0 = t.1
对于模块的成员来说,只能直接访问顶级声明中的成员。 对于模块的成员来说,只能直接访问顶级声明中的成员。
为了区分只有参数名有所不同的方法或构造器,在圆括号中写出参数名,参数名后紧跟一个冒号,对于没有参数名的参数,使用下划线代替参数名。而对于重载方法,则需使用类型标注进行区分。例如:
```swift
class SomeClass {
func someMethod(x: Int, y: Int) {}
func someMethod(x: Int, z: Int) {}
func overloadedMethod(x: Int, y: Int) {}
func overloadedMethod(x: Int, y: Bool) {}
}
let instance = SomeClass()
let a = instance.someMethod // 有歧义
let b = instance.someMethod(_:y:) // 无歧义
let d = instance.overloadedMethod // 有歧义
let d = instance.overloadedMethod(_:y:) // 有歧义
let d: (Int, Bool) -> Void = instance.overloadedMethod(_:y:) // 无歧义
```
如果点号(`.`)出现在行首,它会被视为显式成员表达式的一部分,而不是隐式成员表达式的一部分。例如如下代码所展示的被分为多行的链式方法调用:
```swift
let x = [10, 3, 20, 15, 4]
.sort()
.filter { $0 > 5 }
.map { $0 * 100 }
```
> 显式成员表达式语法 > 显式成员表达式语法
<a name="explicit-member-expression"></a> <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.html#identifier) [*泛型实参子句*](08_Generic_Parameters_and_Arguments.md#generic-argument-clause)<sub>可选</sub> > *显式成员表达式* → [*后缀表达式*](#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) **)**
>
<a name="argument-names"></a>
> *参数名称* → [*参数名*](#argument-name) [*参数名称*](#argument-names)<sub>可选</sub><br/>
<a name="argument-name"></a>
> *参数名* → [*标识符*](02_Lexical_Structure.md#identifier) **:**
<a name="postfix_self_expression"></a> <a name="postfix_self_expression"></a>
### 后缀 self 表达式 ### 后缀 self 表达式
后缀 `self` 表达式由某个表达式紧跟 `.self` 组成,形式如下: 后缀 `self` 表达式由某个表达式或类型名紧跟 `.self` 组成,形式如下:
> `表达式`.self > `表达式`.self
> `类型`.self > `类型`.self
第一种形式返回表达式的值。例如:`x.self` 返回 `x` 第一种形式返回表达式的值。例如:`x.self` 返回 `x`
第二种形式返回表示对应类型的值。我们可以用它来动态地获取某个实例的类型。例如,`SomeClass.self` 会返回 `SomeClass` 类型本身,你可以将其传递给相应函数或者方法作为参数。 第二种形式返回相应的类型。我们可以用它来获取某个实例的类型作为一个值来使用。例如,`SomeClass.self` 会返回 `SomeClass` 类型本身,你可以将其传递给相应函数或者方法作为参数。
> 后缀 self 表达式语法 > 后缀 self 表达式语法
<a name="postfix-self-expression"></a> <a name="postfix-self-expression"></a>
@ -670,11 +766,11 @@ t.0 = t.1
<a name="dynamic_type_expression"></a> <a name="dynamic_type_expression"></a>
### dynamicType 表达式 ### dynamicType 表达式
`dynamicType` 表达式由某个表达式紧跟 `.dynamicType` 组成形式如下 dynamicType 表达式由类似[函数调用表达式(Function Call Expression)](#function-call-expression)的特殊语法表达式组成,形式如下:
> `表达式`.dynamicType > type(of:`表达式`)
上述形式中的表达式不能是类型名。`dynamicType` 表达式会返回某个实例在运行时的类型,具体请看下面的子: 上述形式中的表达式不能是类型名。type(of:) 表达式会返回某个实例在运行时的类型,具体请看下面的子:
```swift ```swift
class SomeBaseClass { class SomeBaseClass {
@ -690,13 +786,13 @@ class SomeSubClass: SomeBaseClass {
let someInstance: SomeBaseClass = SomeSubClass() let someInstance: SomeBaseClass = SomeSubClass()
// someInstance 在编译时的静态类型为 SomeBaseClass // someInstance 在编译时的静态类型为 SomeBaseClass
// 在运行时的动态类型为 SomeSubClass // 在运行时的动态类型为 SomeSubClass
someInstance.dynamicType.printClassName() type(of: someInstance).printClassName()
// 打印 “SomeSubClass” // 打印 “SomeSubClass”
``` ```
> 动态类型表达式语法 > 动态类型表达式语法
<a name="dynamic-type-expression"></a> <a name="dynamic-type-expression"></a>
> *动态类型表达式* → [*后缀表达式*](#postfix-expression) **.** **dynamicType** > *动态类型表达式* → type(of:表达式) **.** **dynamicType**
<a name="subscript_expression"></a> <a name="subscript_expression"></a>
### 下标表达式 ### 下标表达式
@ -705,7 +801,7 @@ someInstance.dynamicType.printClassName()
> `表达式`[`索引表达式`] > `表达式`[`索引表达式`]
要获取下标表达式的值,可将索引表达式作为下标表达式的参数调用表达式类型的下标 getter。下标 setter 的调用方式与之一样。 要获取下标表达式的值,可将索引表达式作为下标表达式的参数调用下标 getter。下标 setter 的调用方式与之一样。
关于下标的声明,请参阅 [协议下标声明](05_Declarations.md#protocol_subscript_declaration)。 关于下标的声明,请参阅 [协议下标声明](05_Declarations.md#protocol_subscript_declaration)。
@ -745,11 +841,11 @@ someDictionary["a"]![0] = 100
> `表达式`? > `表达式`?
后缀 `?` 根据表达式生成可选链表达式而不会改变表达式的值。 后缀 `?` 运算符会根据表达式生成可选链表达式而不会改变表达式的值。
如果某个后缀表达式包含可选链表达式,那么它的执行过程会比较特殊。如果该可选链表达式的值是 `nil`,整个后缀表达式会直接返回 `nil`。如果该可选链表达式的值不是 `nil`,则返回可选链表达式解包后的值,并用于后缀表达式中剩余的表达式。在这两种情况下,整个后缀表达式的值都会是可选类型。 如果某个后缀表达式包含可选链表达式,那么它的执行过程会比较特殊。如果该可选链表达式的值是 `nil`,整个后缀表达式会直接返回 `nil`。如果该可选链表达式的值不是 `nil`,则返回可选链表达式解包后的值,并将该值用于后缀表达式中剩余的表达式。在这两种情况下,整个后缀表达式的值都会是可选类型。
如果某个后缀表达式中包含了可选链表达式,那么只有最外层的表达式会返回一个可选类型。例如,在下面的例子中,如果 `c` 不是 `nil`,那么它的值会被解包,然后通过 `.property` 访问它的属性,接着进一步通过 `.performAction()` 调用相应方法。整个 `c?.property.performAction()` 表达式返回一个可选类型的值。 如果某个后缀表达式中包含了可选链表达式,那么只有最外层的表达式会返回一个可选类型。例如,在下面的例子中,如果 `c` 不是 `nil`,那么它的值会被解包,然后通过 `.property` 访问它的属性,接着进一步通过 `.performAction()` 调用相应方法。整个 `c?.property.performAction()` 表达式返回一个可选类型的值,而不是多重可选类型
```swift ```swift
var c: SomeClass? var c: SomeClass?

File diff suppressed because it is too large Load Diff

View File

@ -100,7 +100,7 @@ protocol MyProtocol {
protocol MyRenamedProtocol { protocol MyRenamedProtocol {
// 这里是协议定义 // 这里是协议定义
} }
@available(*, unavailable, renamed="MyRenamedProtocol") @available(*, unavailable, renamed="MyRenamedProtocol")
typealias MyProtocol = MyRenamedProtocol typealias MyProtocol = MyRenamedProtocol
``` ```
@ -109,7 +109,7 @@ typealias MyProtocol = MyRenamedProtocol
如果 `available` 特性除了平台名称参数外,只指定了一个 `introduced` 参数,那么可以使用以下简写语法代替: 如果 `available` 特性除了平台名称参数外,只指定了一个 `introduced` 参数,那么可以使用以下简写语法代替:
> @available(`平台名称` `版本号`, *) > @available(`平台名称` `版本号`, * )
`available` 特性的简写语法可以简明地表达出声明在多个平台上的可用性。尽管这两种形式在功能上是相同的,但请尽可能地使用简写语法形式。 `available` 特性的简写语法可以简明地表达出声明在多个平台上的可用性。尽管这两种形式在功能上是相同的,但请尽可能地使用简写语法形式。
@ -122,13 +122,13 @@ class MyClass {
`objc` `objc`
该特性用于修饰任何可以在 Objective-C 中表示的声明。比如,非嵌套类、协议、非泛型枚举(仅限原始值为整型的枚举)、类和协议中的属性和方法(包括存取方法)、构造器、析构器以及下标。`objc` 特性告诉编译器这个声明可以在 Objective-C 代码中使用。 该特性用于修饰任何可以在 Objective-C 中表示的声明。比如,非嵌套类、协议、非泛型枚举(仅限原始值为整型的枚举)、类和协议中的属性和方法(包括存取方法)、构造器、析构器以及下标运算符`objc` 特性告诉编译器这个声明可以在 Objective-C 代码中使用。
标有 `objc` 特性的类必须继承自 Objective-C 中定义的类。如果你将 `objc` 特性应用于一个类或协议,它也会隐式地应用于类或协议中兼容 Objective-C 的成员。对于标记了 `objc` 特性的类,编译器会隐式地为它的子类添加 `objc` 特性。标记了 `objc` 特性的协议不能继承没有标记 `objc` 的协议。 标有 `objc` 特性的类必须继承自 Objective-C 中定义的类。如果你将 `objc` 特性应用于一个类或协议,它也会隐式地应用于类或协议中兼容 Objective-C 的成员。对于标记了 `objc` 特性的类,编译器会隐式地为它的子类添加 `objc` 特性。标记了 `objc` 特性的协议不能继承没有标记 `objc` 的协议。
如果你将 `objc` 特性应用于枚举,每一个枚举用例都会以枚举名称和用例名称组合的方式暴露在 Objective-C 代码中。例如,在 `Planet` 枚举中有一个名为 `Venus` 的用例,该用例暴露在 Objective-C 代码中时叫做 `PlanetVenus` 如果你将 `objc` 特性应用于枚举,每一个枚举用例都会以枚举名称和用例名称组合的方式暴露在 Objective-C 代码中。例如,在 `Planet` 枚举中有一个名为 `Venus` 的用例,该用例暴露在 Objective-C 代码中时叫做 `PlanetVenus`
`objc` 特性有一个可选的参数,由标识符构成。当你想把 `objc` 所修饰的实体以一个不同的名字暴露给 Objective-C 时,你就可以使用这个特性参数。你可以使用这个参数来命名类、协议、方法、存取方法以及构造器。下面的例子把 `ExampleClass` 中的 `enabled` 属性的取值方法暴露给 Objective-C名字是 `isEnabled`,而不是它原来的属性名。 `objc` 特性有一个可选的参数,由标识符构成。当你想把 `objc` 所修饰的实体以一个不同的名字暴露给 Objective-C 时,你就可以使用这个特性参数。你可以使用这个参数来命名类、枚举类型、枚举用例、协议、方法、存取方法以及构造器。下面的例子把 `ExampleClass` 中的 `enabled` 属性的取值方法暴露给 Objective-C名字是 `isEnabled`,而不是它原来的属性名。
```swift ```swift
@objc @objc

View File

@ -233,7 +233,7 @@ case ("0", "0"):
default: default:
print("The point is at (\(point.0), \(point.1)).") print("The point is at (\(point.0), \(point.1)).")
} }
// 打印 “(1, 2) is near the origin.” // 打印 “The point is at (1, 2).”
``` ```
> 表达式模式语法 > 表达式模式语法

View File

@ -8,6 +8,9 @@
> 2.0 > 2.0
> 翻译+校对:[wardenNScaiyi](https:github.com/wardenNScaiyi) > 翻译+校对:[wardenNScaiyi](https:github.com/wardenNScaiyi)
> 3.0
> 翻译+校对:[chenmingjia](https:github.com/chenmingjia)
本页包含内容: 本页包含内容:
- [泛型形参子句](#generic_parameter) - [泛型形参子句](#generic_parameter)
@ -21,10 +24,9 @@
<a name="generic_parameter"></a> <a name="generic_parameter"></a>
## 泛型形参子句 ## 泛型形参子句
泛型形参子句指定泛型类型或函数的类型形参,以及这些参数相关的约束和要求。泛型形参子句用尖括号(`<>`)包住,并且有以下两种形式: 泛型形参子句指定泛型类型或函数的类型形参,以及这些参数相关的约束和要求。泛型形参子句用尖括号(`<>`)包住,形式如下
> <`泛型形参列表`> > <`泛型形参列表`>
> <`泛型形参列表` where `类型要求`>
泛型形参列表中泛型形参用逗号分开,其中每一个采用以下形式: 泛型形参列表中泛型形参用逗号分开,其中每一个采用以下形式:
@ -36,7 +38,7 @@
```swift ```swift
func simpleMax<T: Comparable>(x: T, _ y: T) -> T { func simpleMax<T: Comparable>(_ x: T, _ y: T) -> T {
if x < y { if x < y {
return y return y
} }
@ -54,18 +56,19 @@ simpleMax(3.14159, 2.71828) // T 被推断为 Double 类型
<a name="where_clauses"></a> <a name="where_clauses"></a>
### Where 子句 ### Where 子句
要想对类型形参及其关联类型指定额外要求,可以在泛型形参列表之后添加 `where` 子句。`where` 子句由关键字 `where` 及其后的用逗号分隔的一个或多个要求组成。 要想对类型形参及其关联类型指定额外要求,可以在函数体或者类型的大括号之前添加 `where` 子句。`where` 子句由关键字 `where` 及其后的用逗号分隔的一个或多个要求组成。
`where` 子句中的要求用于指明该类型形参继承自某个类或符合某个协议或协议组合。尽管 `where` 子句提供了语法糖使其有助于表达类型形参上的简单约束(如 `T: Comparable` 等同于 `T where T: Comparable`,等等),但是依然可以用来对类型形参及其关联类型提供更复杂的约束。如,`<T where T: C, T: P>` 表示泛型类型 `T` 继承自类 `C` 且符合协议 `P` > `where` : `类型要求`
如上所述,可以强制约束类型形参的关联类型符合某个协议。例如 `<S: SequenceType where S.Generator.Element: Equatable>` 表示 `S` 符合 `SequenceType` 协议,而且 `S`关联类型 `S.Generator.Element` 符合 `Eauatable` 协议。这种约束确保了序列中的每个元素都是符合 `Equatable` 协议的。 `where` 子句中的要求用于指明该类型形参继承自某个类或符合某个协议或协议组合。尽管 `where` 子句提供了语法糖使其有助于表达类型形参上的简单约束(如 `<T: Comparable>` 等同于 `<T> where T: Comparable`,等等),但是依然可以用来对类型形参及其关联类型提供更复杂的约束,例如你可以强制形参的关联类型遵守协议,如,` <S: Sequence> where S.Iterator.Element: Equatable` 表示泛型类型 `S` 遵守`Sequence`协议并且关联类型`S.Iterator.Element`遵守`Equatable`协议,这个约束确保队列的每个元素都是符合 `Equatable` 协议的。
也可以用操作符 `==` 来指定两个类型必须相同。例如,泛型形参子句 `<S1: SequenceType, S2: SequenceType where S1.Generator.Element == S2.Generator.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)
> 泛型形参子句语法 > 泛型形参子句语法
@ -99,7 +102,7 @@ simpleMax(3.14159, 2.71828) // T 被推断为 Double 类型
> <`泛型实参列表`> > <`泛型实参列表`>
泛型实参列表中类型实参用逗号分开。类型实参是实际具体类型的名字用来替代泛型类型的泛型形参子句中的相应的类型形参。从而得到泛型类型的一个特化版本。例如Swift 标准库中的泛型字典类型定义如下: 泛型实参列表中类型实参用逗号分开。类型实参是实际具体类型的名字用来替代泛型类型的泛型形参子句中的相应的类型形参。从而得到泛型类型的一个特化版本。例如Swift 标准库中的泛型字典类型的的简化定义如下:
```swift ```swift
struct Dictionary<Key: Hashable, Value>: CollectionType, DictionaryLiteralConvertible { struct Dictionary<Key: Hashable, Value>: CollectionType, DictionaryLiteralConvertible {

View File

@ -9,10 +9,12 @@
> 2.0 > 2.0
> 翻译+校对:[littledogboy](https://github.com/littledogboy) > 翻译+校对:[littledogboy](https://github.com/littledogboy)
> 2.2
> 翻译:[chenmingbiao](https://github.com/chenmingbiao)
本页包含内容: 本页包含内容:
- [循环语句](#loop_statements) - [循环语句](#loop_statements)
- [For 语句](#for_statements)
- [For-In 语句](#for-in_statements) - [For-In 语句](#for-in_statements)
- [While 语句](#while_statements) - [While 语句](#while_statements)
- [Repeat-While 语句](#repeat-while_statements) - [Repeat-While 语句](#repeat-while_statements)
@ -26,15 +28,15 @@
- [Continue 语句](#continue_statement) - [Continue 语句](#continue_statement)
- [Fallthrough 语句](#fallthrough_statements) - [Fallthrough 语句](#fallthrough_statements)
- [Return 语句](#return_statements) - [Return 语句](#return_statements)
- [Available 语句](#availability_statements)
- [Throw 语句](#throw_statements) - [Throw 语句](#throw_statements)
- [Defer 语句](#defer_statements) - [Defer 语句](#defer_statements)
- [Do 语句](#do_statements) - [Do 语句](#do_statements)
- [编译器控制语句](#compiler_control_statements) - [编译器控制语句](#compiler_control_statements)
- [编译配置语句](#build_config_statements) - [编译配置语句](#build_config_statements)
- [源代码控制语句](#line_control_statements) - [控制语句](#line_control_statements)
- [可用性条件](#availability_condition)
在 Swift 中,有三种类型的语句:简单语句、编译器控制语句和控制流语句。简单语句是最常见的,用于构造表达式或者声明。编译器控制语句允许程序改变编译器的行为,包含编译配置语句和线路控制语句。 在 Swift 中,有三种类型的语句:简单语句、编译器控制语句和控制流语句。简单语句是最常见的,用于构造表达式或者声明。编译器控制语句允许程序改变编译器的行为,包含编译配置语句和控制语句。
控制流语句则用于控制程序执行的流程Swift 中有多种类型的控制流语句循环语句、分支语句和控制转移语句。循环语句用于重复执行代码块分支语句用于执行满足特定条件的代码块控制转移语句则用于改变代码的执行顺序。另外Swift 提供了 `do` 语句,用于构建局部作用域,还用于错误的捕获和处理;还提供了 `defer` 语句,用于退出当前作用域之前执行清理操作。 控制流语句则用于控制程序执行的流程Swift 中有多种类型的控制流语句循环语句、分支语句和控制转移语句。循环语句用于重复执行代码块分支语句用于执行满足特定条件的代码块控制转移语句则用于改变代码的执行顺序。另外Swift 提供了 `do` 语句,用于构建局部作用域,还用于错误的捕获和处理;还提供了 `defer` 语句,用于退出当前作用域之前执行清理操作。
@ -57,53 +59,20 @@
<a name="loop_statements"></a> <a name="loop_statements"></a>
## 循环语句 ## 循环语句
循环语句会根据特定的循环条件来重复执行代码块。Swift 提供种类型的循环语句:`for` 语句、`for-in` 语句、`while` 语句和 `repeat-while` 语句。 循环语句会根据特定的循环条件来重复执行代码块。Swift 提供种类型的循环语句:`for-in` 语句、`while` 语句和 `repeat-while` 语句。
通过 `break` 语句和 `continue` 语句可以改变循环语句的控制流。有关这两条语句,详情参见 [Break 语句](#break_statement) 和 [Continue 语句](#continue_statement)。 通过 `break` 语句和 `continue` 语句可以改变循环语句的控制流。有关这两条语句,详情参见 [Break 语句](#break_statement) 和 [Continue 语句](#continue_statement)。
> 循环语句语法 > 循环语句语法
<a name="loop-statement"></a> <a name="loop-statement"></a>
> *循环语句* → [*for 语句*](#for-statement)
> *循环语句* → [*for-in 语句*](#for-in-statement) > *循环语句* → [*for-in 语句*](#for-in-statement)
> *循环语句* → [*while 语句*](#while-statement) > *循环语句* → [*while 语句*](#while-statement)
> *循环语句* → [*repeat-while 语句*](#repeat-while-statement) > *循环语句* → [*repeat-while 语句*](#repeat-while-statement)
<a name="for_statements"></a>
### For 语句
`for` 语句只有在循环条件为真时重复执行代码块,同时计数器递增。
`for` 语句的形式如下:
```swift
for 初始化; 条件; 增量 {
语句
}
```
初始化、条件和增量语句之间必须以分号相隔,循环体中的语句必须以花括号包裹。
`for` 语句的执行流程如下:
1. 初始化只会被执行一次,通常用于声明和初始化在接下来的循环中需要使用的变量。
2. 判断条件的值。如果为 `true`,循环体中的语句将会被执行,然后转到第 3 步;如果为 `false`,循环体中的语句以及增量语句都不会被执行,`for` 语句至此执行完毕。
3. 执行增量语句,然后重复第 2 步。
在初始化语句中定义的变量仅在 `for` 循环的作用域内有效。
条件的结果必须符合 `BooleanType` 协议。
> for 语句语法
<a name="for-statement"></a>
> *for 语句* → **for** [*for初始条件*](#for-init)<sub>可选</sub> **;** [*表达式*](04_Expressions.md#expression)<sub>可选</sub> **;** [*表达式*](04_Expressions.md#expression)<sub>可选</sub> [*代码块*](05_Declarations.md#code-block)
> *for语句* → **for** **(** [*for初始条件*](#for-init)<sub>可选</sub> **;** [*表达式*](04_Expressions.md#expression)<sub>可选</sub> **;** [*表达式*](04_Expressions.md#expression)<sub>可选</sub> **)** [*代码块*](05_Declarations.md#code-block)
<a name="for-init"></a>
> *for 初始条件* → [*变量声明*](05_Declarations.md#variable-declaration) | [*表达式列表*](04_Expressions.md#expression-list)
<a name="for-in_statements"></a> <a name="for-in_statements"></a>
### For-In 语句 ### For-In 语句
`for-in` 语句会为集合(或符合 `Sequence` 协议的任意类型)中的每一项执行一次代码块。 `for-in` 语句会为集合(或实现了 `SequenceType` 协议的任意类型)中的每一项执行一次代码块。
`for-in` 语句的形式如下: `for-in` 语句的形式如下:
@ -113,7 +82,7 @@ for 项 in 集合 {
} }
``` ```
`for-in` 语句在循环开始前会调用集合表达式的 `generate()` 方法来获取一个符合 `Generator` 协议的类型的值。接下来循环开始,反复调用该值的 `next()` 方法。如果其返回值不是 `None`,它将会被赋给“项”,然后执行循环体语句,执行完毕后回到循环开始处,继续重复这一过程;否则,既不会赋值也不会执行循环体语句,`for-in` 语句至此执行完毕。 `for-in` 语句在循环开始前会调用集合表达式的 `generate()` 方法来获取一个实现了 `GeneratorType` 协议的类型的值。接下来循环开始,反复调用该值的 `next()` 方法。如果其返回值不是 `None`,它将会被赋给“项”,然后执行循环体语句,执行完毕后回到循环开始处,继续重复这一过程;否则,既不会赋值也不会执行循环体语句,`for-in` 语句至此执行完毕。
> for-in 语句语法 > for-in 语句语法
<a name="for-in-statement"></a> <a name="for-in-statement"></a>
@ -264,8 +233,8 @@ guard 条件 else {
语句 语句
} }
``` ```
`guard` 语句中条件的结果必须符合 `BooleanType` 协议,而且条件语句可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。 `guard` 语句中条件的结果必须符合 `BooleanType` 协议。另外,条件也可以是一条可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.html#optional_binding)。
`guard` 语句中进行可选绑定的常量或者变量,其可用范围从声明开始直到作用域结束。 `guard` 语句中进行可选绑定的常量或者变量,其可用范围从声明开始直到作用域结束。
@ -280,7 +249,7 @@ guard 条件 else {
> guard 语句语法 > guard 语句语法
<a name="guard-statement"></a> <a name="guard-statement"></a>
> *guard 语句* → **guard** [*条件子句*](#condition-clause) **else** [*代码块*](05_Declarations.md#code-block) > *guard 语句* → **guard** [*条件子句*](#condition-clause) **else** [*代码块*](05_Declarations.html#code-block)
<a name="switch_statements"></a> <a name="switch_statements"></a>
### Switch 语句 ### Switch 语句
@ -304,9 +273,9 @@ default:
`switch` 语句会先计算控制表达式的值,然后与每一个 `case` 的模式进行匹配。如果匹配成功,程序将会执行对应的 `case` 中的语句。另外,每一个 `case` 都不能为空,也就是说在每一个 `case` 中必须至少有一条语句。如果你不想在匹配到的 `case` 中执行代码,只需在该 `case` 中写一条 `break` 语句即可。 `switch` 语句会先计算控制表达式的值,然后与每一个 `case` 的模式进行匹配。如果匹配成功,程序将会执行对应的 `case` 中的语句。另外,每一个 `case` 都不能为空,也就是说在每一个 `case` 中必须至少有一条语句。如果你不想在匹配到的 `case` 中执行代码,只需在该 `case` 中写一条 `break` 语句即可。
可以用作控制表达式的值是十分灵活的。除了标量类型外,如 `Int``Character`,你可以使用任何类型的值,包括浮点数、字符串、元组、自定义类型的实例和可选类型,甚至是指定的 `Range` 或枚举类型中的成员值。关于如何在 `switch` 语句中使用这些类型,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章中的 [Switch](../chapter2/05_Control_Flow.html#switch)。 可以用作控制表达式的值是十分灵活的。除了标量类型外,如 `Int``Character`,你可以使用任何类型的值,包括浮点数、字符串、元组、自定义类型的实例和可选类型。控制表达式的值还可以用来匹配枚举类型中的成员值或是检查该值是否包含在指定的 `Range` 。关于如何在 `switch` 语句中使用这些类型,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章中的 [Switch](../chapter2/05_Control_Flow.html#switch)。
每个 `case` 的模式后面可以有一个 `where` 子句。`where` 子句由 `where` 关键字紧跟一个提供额外测试条件的表达式组成。因此,当且仅当控制表达式匹配一个 `case` 的模式且 `where` 子句的表达式为真时,`case` 中的语句才会被执行。在下面的例子中,控制表达式只会匹配包含两个相等元素的元组,例如 `(1, 1)` 每个 `case` 的模式后面可以有一个 `where` 子句。`where` 子句由 `where` 关键字紧跟一个提供额外条件的表达式组成。因此,当且仅当控制表达式匹配一个 `case` 的模式且 `where` 子句的表达式为真时,`case` 中的语句才会被执行。在下面的例子中,控制表达式只会匹配包含两个相等元素的元组,例如 `(1, 1)`
```swift ```swift
case let (x, y) where x == y: case let (x, y) where x == y:
@ -318,11 +287,11 @@ case let (x, y) where x == y:
`switch` 语句中 `case` 的匹配顺序和源代码中的书写顺序保持一致。因此,当多个模式都能匹配控制表达式时,只有第一个匹配的 `case` 中的代码会被执行。 `switch` 语句中 `case` 的匹配顺序和源代码中的书写顺序保持一致。因此,当多个模式都能匹配控制表达式时,只有第一个匹配的 `case` 中的代码会被执行。
#### Switch 语句必须是详尽的 #### Switch 语句不能有遗漏
在 Swift 中,`switch` 语句中控制表达式的每一个可能的值都必须至少有一个 `case` 与之对应。在某些无法面面俱到的情况下(例如,表达式的类型是 `Int`),你可以使用 `default` 分支满足该要求。 在 Swift 中,`switch` 语句中控制表达式的每一个可能的值都必须至少有一个 `case` 与之对应。在某些无法面面俱到的情况下(例如,表达式的类型是 `Int`),你可以使用 `default` 分支满足该要求。
#### 不存在隐式落 #### 不存在隐式落
当匹配到的 `case` 中的代码执行完毕后,`switch` 语句会直接退出,而不会继续执行下一个 `case` 。这就意味着,如果你想执行下一个 `case`,需要显式地在当前 `case` 中使用 `fallthrough` 语句。关于 `fallthrough` 语句的更多信息,请参阅 [Fallthrough 语句](#fallthrough_statements)。 当匹配到的 `case` 中的代码执行完毕后,`switch` 语句会直接退出,而不会继续执行下一个 `case` 。这就意味着,如果你想执行下一个 `case`,需要显式地在当前 `case` 中使用 `fallthrough` 语句。关于 `fallthrough` 语句的更多信息,请参阅 [Fallthrough 语句](#fallthrough_statements)。
@ -352,7 +321,7 @@ case let (x, y) where x == y:
你可以在循环语句或 `switch` 语句前面加上标签,它由标签名和紧随其后的冒号(`:`)组成。在 `break``continue` 后面跟上标签名可以显式地在循环语句或 `switch` 语句中改变相应的控制流。关于这两条语句用法,请参阅 [Break 语句](#break_statement) 和 [Continue 语句](#continue_statement)。 你可以在循环语句或 `switch` 语句前面加上标签,它由标签名和紧随其后的冒号(`:`)组成。在 `break``continue` 后面跟上标签名可以显式地在循环语句或 `switch` 语句中改变相应的控制流。关于这两条语句用法,请参阅 [Break 语句](#break_statement) 和 [Continue 语句](#continue_statement)。
标签的作用域在该标签所标记的语句内。可以使用带标签的语句,但只要使用它,在作用域内需保证标签名唯一。 标签的作用域在该标签所标记的语句内。可以嵌套使用带标签的语句,但标签名必须唯一。
关于使用带标签的语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章中的 [带标签的语句](../chapter2/05_Control_Flow.md#labeled_statements)。 关于使用带标签的语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章中的 [带标签的语句](../chapter2/05_Control_Flow.md#labeled_statements)。
@ -380,16 +349,16 @@ case let (x, y) where x == y:
<a name="break_statement"></a> <a name="break_statement"></a>
### Break 语句 ### Break 语句
`break` 语句用于终止循环语句或 `switch` 语句的执行。使用 `break` 语句时,可以只写 `break` 这个关键词,也可以在 `break` 后面跟上标签名,像下面这样: `break` 语句用于终止循环语句`if` 语句`switch` 语句的执行。使用 `break` 语句时,可以只写 `break` 这个关键词,也可以在 `break` 后面跟上标签名,像下面这样:
> break > break
> break `标签名` > break `标签名`
`break` 语句后面带标签名时,可用于终止由这个标签标记的循环语句或 `switch` 语句的执行。 `break` 语句后面带标签名时,可用于终止由这个标签标记的循环语句`if` 语句`switch` 语句的执行。
而只写 `break` 时,则会终止 `switch` 语句或包含 `break` 语句的最内层循环的执行。 而只写 `break` 时,则会终止 `switch` 语句或 `break` 语句所属的最内层循环语句的执行。不能使用 `break` 语句来终止未使用标签的 `if` 语句。
在这两种情况,控制权都会被传递给循环语句或 `switch` 语句后面的第一行语句。 无论哪种情况,控制权都会被转移给被终止的控制流语句后面的第一行语句。
关于使用 `break` 语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章的 [Break](../chapter2/05_Control_Flow.md#break) 和 [带标签的语句](../chapter2/05_Control_Flow.md#labeled_statements)。 关于使用 `break` 语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章的 [Break](../chapter2/05_Control_Flow.md#break) 和 [带标签的语句](../chapter2/05_Control_Flow.md#labeled_statements)。
@ -407,9 +376,9 @@ case let (x, y) where x == y:
`continue` 语句后面带标签名时,可用于终止由这个标签标记的循环中当前迭代的执行。 `continue` 语句后面带标签名时,可用于终止由这个标签标记的循环中当前迭代的执行。
而当只写 `continue` 时,可用于终止上下文中包含 `continue` 语句的最内层循环中当前迭代的执行。 而当只写 `continue` 时,可用于终止 `continue` 语句所属的最内层循环中当前迭代的执行。
在这两种情况下,控制权都会被传递给循环外面的第一行语句。 在这两种情况下,控制权都会被转移给循环语句的条件语句。
`for` 语句中,`continue` 语句执行后,增量表达式还是会被计算,这是因为每次循环体执行完毕后,增量表达式都会被计算。 `for` 语句中,`continue` 语句执行后,增量表达式还是会被计算,这是因为每次循环体执行完毕后,增量表达式都会被计算。
@ -424,7 +393,7 @@ case let (x, y) where x == y:
`fallthrough` 语句用于在 `switch` 语句中转移控制权。`fallthrough` 语句会把控制权从 `switch` 语句中的一个 `case` 转移到下一个 `case`。这种控制权转移是无条件的,即使下一个 `case` 的模式与 `switch` 语句的控制表达式的值不匹配。 `fallthrough` 语句用于在 `switch` 语句中转移控制权。`fallthrough` 语句会把控制权从 `switch` 语句中的一个 `case` 转移到下一个 `case`。这种控制权转移是无条件的,即使下一个 `case` 的模式与 `switch` 语句的控制表达式的值不匹配。
`fallthrough` 语句可出现在 `switch` 语句中的任意 `case` 中,但不能出现在最后一个 `case` 中。同时,`fallthrough` 语句也不能把控制权转移到使用了可选绑定的 `case` `fallthrough` 语句可出现在 `switch` 语句中的任意 `case` 中,但不能出现在最后一个 `case` 中。同时,`fallthrough` 语句也不能把控制权转移到使用了绑定的 `case`
关于在 `switch` 语句中使用 `fallthrough` 语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章的 [控制转移语句](../chapter2/05_Control_Flow.md#control_transfer_statements)。 关于在 `switch` 语句中使用 `fallthrough` 语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章的 [控制转移语句](../chapter2/05_Control_Flow.md#control_transfer_statements)。
@ -435,70 +404,30 @@ case let (x, y) where x == y:
<a name="return_statements"></a> <a name="return_statements"></a>
### Return 语句 ### Return 语句
`return` 语句用于在函数或方法的实现中将控制权转移调用,接着程序将会从调用者的位置继续向下执行。 `return` 语句用于在函数或方法的实现中将控制权转移调用函数或方法,接着程序将会从调用位置继续向下执行。
使用 `return` 语句时,可以只写 `return` 这个关键词,也可以在 `return` 后面跟上表达式,像下面这样: 使用 `return` 语句时,可以只写 `return` 这个关键词,也可以在 `return` 后面跟上表达式,像下面这样:
> return > return
> return `表达式` > return `表达式`
`return` 语句后面带表达式时,表达式的值将会返回给调用。如果表达式的值的类型与函数或者方法声明的返回类型不匹配Swift 则会在返回表达式的值之前将表达式的值的类型转换为返回类型。 `return` 语句后面带表达式时,表达式的值将会返回给调用函数或方法。如果表达式的值的类型与函数或者方法声明的返回类型不匹配Swift 则会在返回表达式的值之前将表达式的值的类型转换为返回类型。
> 注意 > 注意
> 正如 [可失败构造器](05_Declarations.md#failable_initializers) 中所描述的,`return nil` 在可失败构造器中用于表明构造失败。 > 正如 [可失败构造器](05_Declarations.md#failable_initializers) 中所描述的,`return nil` 在可失败构造器中用于表明构造失败。
而只写 `return` 时,仅仅是将控制权从该函数或方法转移给调用者,而不返回一个值(也就是说,函数或方法的返回类型为 `Void` 或者说 `()`)。 而只写 `return` 时,仅仅是从该函数或方法中返回,而不返回任何值(也就是说,函数或方法的返回类型为 `Void` 或者说 `()`)。
> return 语句语法 > return 语句语法
<a name="return-statement"></a> <a name="return-statement"></a>
> *return 语句* → **return** [*表达式*](04_Expressions.html#expression)<sub>可选</sub> > *return 语句* → **return** [*表达式*](04_Expressions.html#expression)<sub>可选</sub>
<a name="availability_statements"></a>
### Available 语句
可用性条件可作为 `if``while``guard` 语句的条件,可以在运行时基于特定的平台参数来查询 API 的可用性。
可用性条件的形式如下:
```swift
if #available(平台名称 版本, ..., *) {
如果 API 可用,则执行这部分语句
} else {
如果 API 不可用,则执行这部分语句
}
```
使用可用性条件来执行一个代码块时,取决于使用的接口在运行时是否可用。编译器会根据可用性条件提供的信息以及运行时的平台来决定是否执行相应的代码块。
可用性条件使用一系列逗号分隔的平台名称和版本。使用 `iOS``OSX`,以及 `watchOS` 等作为平台名称,并写上相应的版本号。`*` 参数是必须写的,用于处理未来的潜在平台。可用性条件确保了运行时的平台不低于条件中指定的平台版本时才执行代码块。
与布尔类型的条件不同,不能用逻辑运算符 `&&``||` 合并可用性条件。
> 可用性条件语法
<a name="availability-condition"></a>
> *可用性条件* → **#available** **(** [*可用性参数列表*](#availability-arguments) **)**
<a name="availability-arguments"></a>
> *可用性参数列表* → [*可用性参数*](#availability-argument) | [*可用性参数*](#availability-argument) **,** [*可用性参数列表*](#availability-arguments)
<a name="availability-argument"></a>
> *可用性参数* → [平台名称](#platform-name) [平台版本](#platform-version)
> *可用性条件* → __*__
<a name="platform-name"></a>
> *平台名称* → **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)
<a name="throw_statements"></a> <a name="throw_statements"></a>
### Throw 语句 ### Throw 语句
`throw` 语句出现在抛出函数或者抛出方法体内,或者类型被 `throws` 关键字标记的闭包表达式体内。 `throw` 语句出现在抛出函数或者抛出方法体内,或者类型被 `throws` 关键字标记的闭包表达式体内。
`throw` 语句使程序在当前作用域结束执行,并向外围作用域传播错误。抛出的错误会一直传,直到被 `do` 语句的 `catch` 子句处理掉。 `throw` 语句使程序在当前作用域结束执行,并向外围作用域传播错误。抛出的错误会一直传,直到被 `do` 语句的 `catch` 子句处理掉。
`throw` 语句由 `throw` 关键字紧跟一个表达式组成,如下所示: `throw` 语句由 `throw` 关键字紧跟一个表达式组成,如下所示:
@ -525,9 +454,9 @@ defer {
} }
``` ```
`defer` 语句中的语句无论程序控制如何转移都会执行。这意味着 `defer` 语句可以被使用在以下这些情况,例如关闭文件描述,或者即使抛出了错误也需要执行一些动作 `defer` 语句中的语句无论程序控制如何转移都会执行。在某些情况,例如,手动管理资源时,比如关闭文件描述,或者即使抛出了错误也需要执行一些操作时,就可以使用 `defer` 语句
如果多个 `defer` 语句出现在同一作用域内,那么它们执行的顺序与出现的顺序相反。给定作用域中的第一个 `defer` 语句,会在最后执行,这意味着最后执行`defer` 语句中涉及的资源可以被其他 `defer` 语句清理掉。 如果多个 `defer` 语句出现在同一作用域内,那么它们执行的顺序与出现的顺序相反。给定作用域中的第一个 `defer` 语句,会在最后执行,这意味着代码中最靠后`defer` 语句中引用的资源可以被其他 `defer` 语句清理掉。
```swift ```swift
func f() { func f() {
@ -550,9 +479,9 @@ f()
<a name="do_statements"></a> <a name="do_statements"></a>
## Do 语句 ## Do 语句
`do` 语句用于引入一个新的作用域,该作用域中可以含有一个或多个 `catch` 子句,`catch` 子句中定义了一些匹配错误条件的模式。`do` 语句作用域内定义的常量和变量只能在 `do` 语句作用域内使用。 `do` 语句用于引入一个新的作用域,该作用域中可以含有一个或多个 `catch` 子句,`catch` 子句中定义了一些匹配错误条件的模式。`do` 语句作用域内定义的常量和变量只能在 `do` 语句作用域内使用。
Swift 中的 `do` 语句与 C 中限定代码块界限的大括号(`{}`)很相似,并且在程序运行的时候并不会造成系统开销 Swift 中的 `do` 语句与 C 中限定代码块界限的大括号(`{}`)很相似,也并不会降低程序运行时的性能
`do` 语句的形式如下: `do` 语句的形式如下:
@ -567,11 +496,11 @@ do {
} }
``` ```
如同 `switch` 语句,编译器会判断 `catch` 子句是否有遗漏。如果 `catch` 子句没有遗漏,则认为错误被处理。否则,错误会自动传到外围作用域,被`catch` 句处理掉或者继续向外抛出,抛出函数必须以 `throws` 关键字声明。 如同 `switch` 语句,编译器会判断 `catch` 子句是否有遗漏。如果 `catch` 子句没有遗漏,则认为错误被处理。否则,错误会自动传到外围作用域,被`catch` 句处理掉或者被用 `throws` 关键字声明的抛出函数继续向外抛出
为了确保错误已经被处理,可以让 `catch` 子句使用匹配所有错误的模式,如通配符模式(`_`)。如果一个 `catch` 子句不指定一种具体模式,`catch` 子句会匹配任何错误,并绑定到名为 `error` 的局部量。有关在 `catch` 子句中使用模式的更多信息,请参阅 [模式](07_Patterns.md)。 为了确保错误已经被处理,可以让 `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> <a name="do-statement"></a>
@ -606,15 +535,18 @@ do {
`if` 语句的条件不同,编译配置的条件是在编译时进行判断的。只有编译配置在编译时判断为 `true` 的情况下,相应的语句才会被编译和执行。 `if` 语句的条件不同,编译配置的条件是在编译时进行判断的。只有编译配置在编译时判断为 `true` 的情况下,相应的语句才会被编译和执行。
编译配置可以是 `true``false` 的字面量,也可以是使用 `-D` 命令行标志的标识符,或者是下列表格中的任意一个平台测函数。 编译配置可以是 `true``false` 的字面量,也可以是使用 `-D` 命令行标志的标识符,或者是下列表格中的任意一个平台测函数。
| 函数 | 可用参数 | | 函数 | 可用参数 |
| --- | --- | | --- | --- |
| `os()` | `OSX`, `iOS`, `watchOS`, `tvOS` | | `os()` | `OSX`, `iOS`, `watchOS`, `tvOS`, `Linux` |
| `arch()` | `i386`, `x86_64`, `arm`, `arm64` | | `arch()` | `i386`, `x86_64`, `arm`, `arm64` |
| `swift()` | `>=` 后跟版本号 |
> 注意 `swift()`(语言版本检测函数)的版本号参数主要由主版本号和次版本号组成并且使用点号(`.`)分隔开,`>=` 和版本号之间不能有空格。
> `arch(arm)` 编译配置在 ARM 64位设备上不会返回 `true`。如果代码在 32 位的 iOS 模拟器上编译,`arch(i386)` 编译配置返回 `true`。
> 注意
> `arch(arm)` 平台检测函数在 ARM 64 位设备上不会返回 `true`。如果代码在 32 位的 iOS 模拟器上编译,`arch(i386)` 平台检测函数会返回 `true`。
你可以使用逻辑操作符 `&&``||``!` 来组合多个编译配置,还可以使用圆括号来进行分组。 你可以使用逻辑操作符 `&&``||``!` 来组合多个编译配置,还可以使用圆括号来进行分组。
@ -631,9 +563,9 @@ do {
``` ```
> 注意 > 注意
> 即使没有被编译,编译配置中的语句仍然会被解析。 > 即使没有被编译,编译配置中的语句仍然会被解析。然而,唯一的例外是编译配置语句中包含语言版本检测函数:仅当 `Swift` 编译器版本和语言版本检测函数中指定的版本号匹配时,语句才会被解析。这种设定能确保旧的编译器不会尝试去解析新 Swift 版本的语法。
<a name=""></a> <a name="build-config-statement"></a>
> 编译配置语句语法 > 编译配置语句语法
<a name="build-configuration-statement"></a> <a name="build-configuration-statement"></a>
@ -646,7 +578,8 @@ do {
> *单个编译配置 else 子句* → **#else** [*语句*](#statements)<sub>可选</sub> > *单个编译配置 else 子句* → **#else** [*语句*](#statements)<sub>可选</sub>
<a name="build-configuration"></a> <a name="build-configuration"></a>
> *编译配置* → [*平台测函数*](#platform-testing-function) > *编译配置* → [*平台测函数*](#platform-testing-function)
> *编译配置* → [*语言版本检测函数*](#language-version-testing-function)
> *编译配置* → [*标识符*](02_Lexical_Structure.md#identifier) > *编译配置* → [*标识符*](02_Lexical_Structure.md#identifier)
> *编译配置* → [*布尔值字面量*](02_Lexical_Structure.md#boolean-literal) > *编译配置* → [*布尔值字面量*](02_Lexical_Structure.md#boolean-literal)
> *编译配置* → **(** [*编译配置*](#build-configuration) **)** > *编译配置* → **(** [*编译配置*](#build-configuration) **)**
@ -655,33 +588,80 @@ do {
> *编译配置* → [*编译配置*](#build-configuration) **||** [*编译配置*](#build-configuration) > *编译配置* → [*编译配置*](#build-configuration) **||** [*编译配置*](#build-configuration)
<a name="platform-testing-function"></a> <a name="platform-testing-function"></a>
> *平台测函数* → **os** **(** [*操作系统*](#operating-system) **)** > *平台测函数* → **os** **(** [*操作系统*](#operating-system) **)**
> *平台测函数* → **arch** **(** [*架构*](#architecture) **)** > *平台测函数* → **arch** **(** [*架构*](#architecture) **)**
<a name="language-version-testing-function"></a>
> *语言版本检测函数* → **swift** **(** **>=** [*swift版本*](#swift-version) **)**
<a name="operating-system"></a> <a name="operating-system"></a>
> *操作系统* → **OSX** | **iOS** | **watchOS** | **tvOS** > *操作系统* → **OSX** | **iOS** | **watchOS** | **tvOS**
<a name="architecture"></a> <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)
<a name="line_control_statements"></a> <a name="line_control_statements"></a>
### 线路控制语句 ### 控制语句
线路控制语句用来为被编译的源代码指定一个与原始行号和文件名不同的行号和文件名。使用线路控制语句可以改变源代码的位置,以便进行分析和调试。 控制语句可以为被编译的源代码指定行号和文件名,从而改变源代码的定位信息,以便进行分析和调试。
线路控制语句形式如下: 控制语句形式如下:
> \#line `行号` `文件名` > \#line `行号` `文件名`
线路控制语句会改变之后的字面量表达式 `__LINE__``__FILE__` 的值。`行号` 是一个大于 0 的整形字面量,会改变 `__LINE__` 的值。`文件名` 是一个字符串字面量,会改变 `__FILE__` 的值。 控制语句会改变该语句之后的代码中的字面量表达式 `#line``#file` 所表示的值。`行号` 是一个大于 0 的整形字面量,会改变 `#line` 表达式的值。`文件名` 是一个字符串字面量,会改变 `#file` 表达式的值。
你可以通过 `#line` 语句,即不指定行号和文件名,将源代码的位置重置回默认的行号和文件名。 你可以只写 `#line`,而不指定行号和文件名,从而将源代码的定位信息重置回默认的行号和文件名。
线路控制语句必须独占一行,而且不能是源代码文件的最后一行。 `#line` 标记具有两种含义,作为行控制语句使用时必须独占一行,而且不能是源代码文件的最后一行。否则,它将作为字面量表达式使用,详情请参阅 [字面量表达式](04_Expressions.md#literal_expression)。
> 线路控制语句语法 <a name="line-control-statement"></a>
<a name="line-control-statements"></a> > 行控制语句语法
> *线路控制语句* → **#line**
> *线路控制语句* → **#line** [*行号*](#line-number) [*文件名*](#file-name) > *控制语句* → **#line**
> *行控制语句* → **#line** [*行号*](#line-number) [*文件名*](#file-name)
<a name="line-number"></a> <a name="line-number"></a>
> *行号* → 大于 0 的十进制整数 > *行号* → 大于 0 的十进制整数
<a name="file-name"></a> <a name="file-name"></a>
> *文件名* → [*静态字符串字面量*](02_Lexical_Structure.md#static-string-literal) > *文件名* → [*静态字符串字面量*](02_Lexical_Structure.md#static-string-literal)
<a name="availability_condition"></a>
### 可用性条件
可用性条件可作为 `if``while``guard` 语句的条件,可以在运行时基于特定的平台参数来查询 API 的可用性。
可用性条件的形式如下:
```swift
if #available(平台名称 版本, ..., *) {
如果 API 可用,则执行这部分语句
} else {
如果 API 不可用,则执行这部分语句
}
```
使用可用性条件来执行一个代码块时,取决于使用的 API 在运行时是否可用,编译器会根据可用性条件提供的信息来决定是否执行相应的代码块。
可用性条件使用一系列逗号分隔的平台名称和版本。使用 `iOS``OSX`,以及 `watchOS` 等作为平台名称,并写上相应的版本号。`*` 参数是必须写的,用于处理未来的潜在平台。可用性条件确保了运行时的平台不低于条件中指定的平台版本时才执行代码块。
与布尔类型的条件不同,不能用逻辑运算符 `&&``||` 组合可用性条件。
> 可用性条件语法
<a name="availability-condition"></a>
> *可用性条件* → **#available** **(** [*可用性参数列表*](#availability-arguments) **)**
<a name="availability-arguments"></a>
> *可用性参数列表* → [*可用性参数*](#availability-argument) | [*可用性参数*](#availability-argument) **,** [*可用性参数列表*](#availability-arguments)
<a name="availability-argument"></a>
> *可用性参数* → [平台名称](#platform-name) [平台版本](#platform-version)
> *可用性条件* → __*__
<a name="platform-name"></a>
> *平台名称* → **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)