177 Commits
v2.1 ... V3.0

Author SHA1 Message Date
1ad8b08ec6 Update README.md
更新到Swift3.0
2016-09-23 08:54:22 +08:00
ca165654c9 Merge pull request #655 from chenmingbiao/gh-pages
update chapter Protocols to swift 3.0
2016-09-21 10:46:20 +08:00
17757b41b4 update chapter Protocols to swift 3.0 2016-09-21 10:18:20 +08:00
df86057648 Merge pull request #652 from mmoaay/gh-pages
Swift 3.0 更新
2016-09-21 08:28:21 +08:00
24b39e0328 Merge pull request #654 from lettleprince/Swift-3.0-Types-update
Swift 3.0 Types update
2016-09-21 08:27:47 +08:00
a9d2f42e60 Merge pull request #653 from chenmingjia/gh-pages
Translate Declarations in Swift 3.0.
2016-09-21 08:27:00 +08:00
0a36aaf9c1 update swift 3 Types.
update swift 3 Types.
2016-09-20 20:01:26 +08:00
0ff5800a0f 微调
微调
2016-09-20 18:17:03 +08:00
c708b0e592 格式微调
格式微调
2016-09-20 18:07:52 +08:00
2cc398e0c0 Add Information about guard
Add Information  about  guard
2016-09-20 18:01:20 +08:00
2f2488877b 格式微调
格式微调
2016-09-20 17:40:28 +08:00
c4a033bbba Translate Declarations in Swift 3.0.
Translate Declarations in Swift 3.0.
2016-09-20 17:35:35 +08:00
2dd84524a7 Create Types branch. 2016-09-20 16:45:50 +08:00
c966b595d9 Swift 3.0 更新
增加优先级组(precedence groups)概念
2016-09-20 14:46:17 +08:00
94bdafd565 Merge pull request #1 from numbbbbb/gh-pages
merge
2016-09-20 11:33:56 +08:00
9c03037e3a Merge pull request #650 from WhoJave/gh-pages
Attributes_swift3.0
2016-09-20 06:54:48 +08:00
e71013d713 Merge pull request #651 from LanfordCai/develop
Update chapter 'Closures' to Swift 3.0
2016-09-20 06:53:39 +08:00
ed9a67ba9d update chapter 'Closure' to swift 3.0 2016-09-19 23:33:16 +08:00
0d462b58e0 Attributes_swift3.0 2016-09-19 22:39:24 +08:00
aaf2e886e5 Translate Declarations in Swift 3.0.
Translate Declarations in Swift 3.0.
2016-09-18 23:42:33 +08:00
64dd00b6f6 update chapter 'Closure' to swift 3.0 2016-09-17 22:11:29 +08:00
dc08ebf499 Merge pull request #649 from chenmingbiao/gh-pages
update chapter 'The Basics' to swift 3
2016-09-17 17:47:07 +08:00
6a9b24881e update chapter 'The Basics' to swift 3 2016-09-17 16:21:59 +08:00
498143e4f1 Merge pull request #648 from chenmingjia/gh-pages
[Translate] Translate Statement in Swift 3.0.
2016-09-16 10:59:27 +08:00
9573e34aa2 [Translate] Translate Statement in Swift 3.0.
Swift 3.0 Statement  已经补充完毕,主要是行控制语句部分更新和移除Boolean协议的讨论
2016-09-16 10:45:19 +08:00
64530ef5d0 [Translate] Translate Statement in Swift 3.0.
Swift 3.0 Statement  已经补充完毕,主要是行控制语句部分更新和移除Boolean协议的讨论
2016-09-16 10:43:41 +08:00
e24c2eb213 [Translate] Translate Statement in Swift 3.0.
Swift 3.0 Statement  已经补充完毕,主要是行控制语句部分更新和移除Boolean协议的讨论
2016-09-16 10:42:14 +08:00
4f4cc171e7 Update to swift 3.0 final (#647) 2016-09-14 09:56:06 +08:00
abe1c5d76e Merge pull request #646 from Realank/gh-pages
[Swift3] Control Flow翻译完成
2016-09-13 21:29:40 +08:00
00688ac1e5 update chapter 'control flow' to swift 3 2016-09-13 20:19:52 +08:00
845075d5ba update chapter 'control flow' to swift 3 2016-09-13 17:49:06 +08:00
e1e67c8efa update chapter 'control flow' to swift 3 2016-09-13 17:30:32 +08:00
66cc38231a Merge remote-tracking branch 'numbbbbb/gh-pages' into gh-pages 2016-09-13 14:31:51 +08:00
df2ea5ddc9 Merge pull request #644 from chenmingjia/gh-pages
[Translate] Translate Generic in Swift 3.0.
2016-09-12 23:45:41 +08:00
38a64d9f64 Merge pull request #645 from crayygy/develop
[Translate] Swift 3.0 Functions(fixed)
2016-09-12 23:40:37 +08:00
efd6a1d9c3 [Translate] Swift 3.0 Functions(fixed) 2016-09-12 20:22:22 +08:00
1b772fb16e [Translate] Translate Generic in Swift 3.0.
Swift 3.0 Generic 已经补充完毕,主要是 Generic Where Clauses 部分的更新
2016-09-12 19:51:30 +08:00
4edf9501b4 [Translate] Translate Generic in Swift 3.0.
Swift 3.0 Generic 已经补充完毕,主要是 Generic Where Clauses 部分的更新
2016-09-12 19:46:19 +08:00
7c36900e89 Merge pull request #643 from chenmingbiao/gh-pages
校对 Strings and Characters 章节内容
2016-09-12 18:39:56 +08:00
3aa5844ff8 fix 2016-09-12 18:15:38 +08:00
49cdfb7347 校对 Strings and Characters 章节内容 2016-09-12 18:11:08 +08:00
f1b76b38ed Merge pull request #641 from chenmingjia/gh-pages
[Translate] Translate Generic Parameters and Arguments  in Swift 3.0.
2016-09-12 12:28:11 +08:00
d46f2c2314 [Translate] Translate Expressions in Swift 3.0.
Swift 3.0 Expressions 已经补充完毕,主要是 Dynamic Type Expression和Selector Expression 部分的更新
2016-09-11 17:44:31 +08:00
0e160b98c5 [Translate] Translate Generic Parameters and Arguments in Swift 3.0.
Swift 3.0 Generic Parameters and Arguments 已经补充完毕,主要是 Generic Where Clauses 部分的更新
2016-09-11 17:18:21 +08:00
f928b61d09 优化语言参考-语句-guard语句的翻译 (#637) 2016-08-30 15:58:53 +08:00
289d8aadbf Merge pull request #636 from duanhong169/gh-pages
Update 07_Patterns.md
2016-08-12 09:24:21 +08:00
9d4403eada Update 07_Patterns.md 2016-08-10 15:12:24 +08:00
20b60b4224 Patch (#635)
* Update 14_Initialization.md

* Update 21_Extensions.md

* Update 22_Protocols.md
2016-08-10 13:23:37 +08:00
52151afb7f Update 07_Closures.md (#634)
* Update 07_Closures.md

* Update 09_Classes_and_Structures.md
2016-08-08 11:36:48 +08:00
e2a6b722d2 Merge pull request #633 from duanhong169/gh-pages
Update 01_The_Basics.md
2016-08-05 17:42:14 +08:00
083aa8b78b Update 05_Control_Flow.md 2016-08-05 16:02:56 +08:00
1d46a3ec76 Update 04_Collection_Types.md 2016-08-05 14:47:26 +08:00
2baad57b65 Update 01_The_Basics.md 2016-08-05 11:16:21 +08:00
26ad26472a Merge pull request #632 from duanhong169/patch-1
Patch 1
2016-08-03 21:33:43 +08:00
990352db8e Update 02_a_swift_tour.md 2016-08-03 14:12:33 +08:00
82d9cd0156 Update 02_a_swift_tour.md 2016-08-02 16:11:27 +08:00
b39b8a5222 Update 02_a_swift_tour.md 2016-08-02 10:34:52 +08:00
443976cbbf fix typo (#622) 2016-05-25 20:31:55 +08:00
63de595251 Update 05_Declarations.md (#619) 2016-05-20 17:33:23 +08:00
c848062fa2 Merge pull request #617 from SketchK/gh-pages
校正+翻译 From SketchK
2016-05-17 16:35:06 +08:00
988c3e0368 校正+翻译 From SketchK
主要内容
1 `访问控制`章节中的`检查类型`一节中代码更新(++号废弃)
2 `高级运算符`章节中的`AnyObjcect`一节中文字更新及错误纠正
2016-05-17 16:31:33 +08:00
728ae92a90 Merge pull request #616 from SketchK/gh-pages
校正+翻译 From SketchK
2016-05-16 22:15:04 +08:00
f5a6be2ca3 校正+翻译 From SketchK
主要内容
1 `类型转换`章节中的`检查类型`一节中代码更新(++号废弃)
2 `类型转换`章节中的`AnyObjcect`一节中文字更新及错误纠正
3 `嵌套类型`章节中的正文一节中文字有歧义
4 `扩展`章节中的`构造器`一节中的文字有误(翻译内容与原文不符)
5 `扩展`章节中的`下标`一节中的代码更新
6 `协议`章节中的`委托模式`一节中统一文字表述(Control Flow翻译为控制流)
7 `协议`章节中的`委托模式`一节中代码更新
8 `协议`章节中的`可选协议要求`一节中文字有误
9  校正`泛型`一章的内容,没有发现明显错误容
2016-05-16 21:54:23 +08:00
3a1a54cce9 Merge pull request #615 from SketchK/gh-pages
校正+翻译 From SketchK
2016-05-16 17:13:55 +08:00
ad5f1f66b4 校正+翻译 From SketchK
主要内容
1 `可选链`章节中的`使用可选链式调用代替强制展开`一节中文字内容纠正(之前的翻译存在歧义)
2 `可选链`章节中的`访问可选类型的下标`一节中的代码有误(++号废弃)
3 `错误处理`章节中的`用 throwing 函数传递错误`一节中的代码有误(代码与原文不符)
4 `错误处理`章节中的`用 throwing 函数传递错误`一节中漏翻译了部分内容
5 `错误处理`章节中的`表示并抛出`一节中的文字有误
6 `错误处理`章节中的`将错误转换成可选值`一节中文字内容纠正(之前的翻译存在歧义)
7 `错误处理`章节中的`禁用错误传递`一节中文字内容纠正(之前的翻译存在歧义)
2016-05-15 23:01:57 +08:00
7da13b9823 Merge pull request #614 from SketchK/gh-pages
校正+翻译 From SketchK
2016-05-14 22:01:01 +08:00
7cb0005c65 校正+翻译 From SketchK
主要内容
1 `构造过程`章节中的`值类型的构造器代理`一节中文字内容纠正(之前的翻译存在歧义)
2 `构造过程`章节中的`构造器的继承和重写`一节中的文字内容纠正(之前翻译存在歧义)
3 `构造过程`章节中的`可失败构造器init!`一节中的代码有误(多了()符号)
4 `构造过程`章节中的`通过闭包或函数设置属性的默认值`一节中的代码未更新(与原文代码不符)
5 `析构过程`章节中`析构器实践`一节中的文字内容纠正(之前翻译存在歧义)
6 `析构过程`章节中`析构器实践`一节中的代码未更新(与原文代码不符)
7 校正了"自动引用计数"一章的内容,没有发现明显错误
2016-05-14 21:52:50 +08:00
a5f99e7160 Merge pull request #613 from SketchK/gh-pages
校正+翻译 From SketchK
2016-05-13 20:03:34 +08:00
cdb3267dff 校正+翻译 From SketchK
主要内容
1 `枚举`章节中的`递归枚举`一节中文章内容更新(原文与当前翻译内容不相符)
2 `属性`章节中的`存储属性`一节中的文字内容纠正(原文与当前翻译内容不相符)
3 `方法`章节中的`实例方法`的代码有误(++符号废弃)
4 `方法`章节中`self属性`的代码有误作(++符号废弃)
5 `继承`章节中`防止重写`一节中的文字内容纠正(原文与当前翻译中的内容不相符)
6  校正了"类和结构体","下标"两章的内容,没有发现明显错误
2016-05-13 20:00:31 +08:00
ba7e7d5b03 Merge pull request #611 from forrestchang/develop
fix(source/chapter2/10_Properties.md): typo
2016-05-13 18:03:53 +08:00
dadae616fa fix(source/chapter2/10_Properties.md): typo 2016-05-13 16:44:32 +08:00
70ff24980d Merge pull request #610 from SketchK/gh-pages
校正+翻译 From SketchK
2016-05-12 21:00:37 +08:00
019cb42f13 校正+翻译 From SketchK
主要内容
6 `闭包`章节中`单表达式闭包隐式返回`一节内容翻译有误(原文与当前翻译中的内容不相符)
7 `闭包`章节中`运算符函数`一节内容翻译有误(原文与当前翻译中的内容不相符)
2016-05-12 20:53:27 +08:00
4976c25347 校正+翻译 From SketchK
主要内容
1 `控制流`章节中的`while循环`一节中的代码更新(废弃了++)
2 `控制流`章节中的`repeat-while循环`一节中的代码更新(废弃了++)
3 `控制流`章节中的`if`代码有误(原文中与当前翻译中数值不相符)
4 `函数`章节中`常量与变量参数`一节已经被废弃,进行了删除操作
5 `函数`章节中`输入与输出参数`一节内容进行更新(原文与当前翻译中的内容不相符)
6 `闭包`章节中`单表达式闭包隐式返回`一节内容翻译有误(原文与当前翻译中的内容不相符)
7 `闭包`章节中`运算符函数`一节内容翻译有误(原文与当前翻译中的内容不相符)
8 `闭包`章节中`尾随闭包`一节内容更新(废弃了var修饰符,原文与当前翻译中内容不符,)
2016-05-12 20:50:37 +08:00
24488696b5 Merge pull request #609 from zhungxd/fix
Fix img not found. Update img URL.
2016-05-12 17:33:48 +08:00
b9d3f5ead6 Fix img not found. Update img URL. 2016-05-12 17:29:56 +08:00
fc6f33567d update 13_Inheritance.md. 修正标题错误 (#608) 2016-05-12 15:36:35 +08:00
bb316a341f Merge pull request #607 from SketchK/gh-pages
校正部分细节 from SketchK
2016-05-11 17:31:14 +08:00
0bdade3c66 校正:
1.A Swift Tour中的"错误处理"一节的代码 do 与 { 中间缺少了空格
2.Basic Operators一节中 "开头"的"或让 i 值加 1 的便捷自增运算符 ++i 等"在原文中已经删除
3.Basic Operators一节中 "术语"的"后置运算符需紧跟在操作对象之后(如 i++)"改为了(如 c!)
4.Basic Operators一节中 "自增和自减运算"的内容已被删除
5.Strings and Characters一节中"前缀/后缀相等"的"++"号已经被替换为"+=1"
6.Collection Types一节中的"通过两个数组相加创建一个数组"的代码与原文不符,且当前代码在运行时会报错
2016-05-11 17:24:25 +08:00
3fa3dd7af4 merge 2016-05-10 16:17:25 +08:00
667587bfca merge 2016-05-10 16:17:10 +08:00
16af2fa033 merge 2016-05-10 16:13:43 +08:00
6a9571cb8e 更新了语法变更历史内容 (#605)
* 1. 校对了翻译内容,删除了与 Swift 语言本身无关的相关变动
2. 页面内容适配为与 https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/RevisionHistory.html 相关的组织结构

* 1. 更新了 Swift 2.0 的相关语法变动
2. 更新了 Swift 2.1 的相关语法变动
3. 更新了 Swift 2.2 的相关语法变动
2016-05-09 20:51:13 +08:00
1502ff17f3 Update 04_Expressions.md 2016-05-08 11:35:00 +08:00
4685efc35d Update 04_Expressions.md
Fix some urls, add and correct some contents.
2016-05-08 11:31:16 +08:00
8234c6959b Merge pull request #603 from 949478479/gh-pages
Update 04_Expressions.md
2016-05-06 11:25:28 +08:00
fa6bc5c329 Update 04_Expressions.md 2016-05-06 10:58:59 +08:00
9267bf9c13 Merge pull request #602 from 949478479/gh-pages
Update 10_Statements.md
2016-05-05 08:26:34 +08:00
26c5b2ac9a Update 10_Statements.md 2016-05-04 23:40:54 +08:00
7110724b12 Merge pull request #601 from numbbbbb/release
Update CNAME
2016-04-28 10:54:26 +08:00
1801b6d920 Merge pull request #600 from SemperIdem/gh-pages
Declarations -> 2.2
2016-04-21 20:40:11 +08:00
e04533d2a8 Declarations -> 2.2 2016-04-21 13:22:21 +08:00
88befb0b59 Update README.md 2016-04-20 12:52:01 +08:00
dd265db62b Merge pull request #598 from Abitofcomedy/改错
改错
2016-04-20 12:51:24 +08:00
f270792d59 改错
类型方法代码写成static func 原文为 class func
2016-04-15 19:46:47 +08:00
8459508d67 update 2.2 history log date 2016-04-11 15:52:52 +08:00
42f3319e75 update 2.2 history log date 2016-04-11 15:51:03 +08:00
8bf061f8db Merge pull request #597 from saitjr/gh-pages
Fix format error
2016-04-11 10:15:42 +08:00
66c6e1287a fix format error 2016-04-11 09:58:22 +08:00
08838d2754 Merge pull request #596 from saitjr/gh-pages
Update to 2.2 (Properties)
2016-04-10 20:55:05 +08:00
b883da0fc2 proofreading 2016-04-10 20:07:24 +08:00
de0d455b03 add space 2016-04-10 19:22:17 +08:00
9522480e31 update to 2.2 2016-04-10 19:11:30 +08:00
11c71dbd52 Merge pull request #595 from Brian175/gh-pages
Update 02_a_swift_tour.md
2016-04-10 09:59:07 +08:00
556458dcb7 Update 02_a_swift_tour.md 2016-04-09 23:43:37 +08:00
93239d8927 update 2.2 history log date 2016-04-09 09:29:50 +08:00
cdf4de78d6 Update 23_Generics.md
update 2.2 history log
2016-04-09 09:26:42 +08:00
c625c336bc Merge pull request #594 from LanfordCai/develop
Updated Language Guide -- Generics
2016-04-08 14:42:20 +08:00
d6fd41d42d 校对 2016-04-08 14:18:39 +08:00
d415e542d9 Merge pull request #593 from SemperIdem/gh-pages
Language Reference - Lexical Structure
2016-04-06 15:59:51 +08:00
8aa9f9c591 Language Reference - Lexical Structure
Update to Swift 2.2
2016-04-06 12:10:26 +08:00
0bdce33f18 Merge pull request #590 from chenmingbiao/develop
update Language Reference - Statements
2016-04-05 14:39:20 +08:00
a403c3c38e Merge pull request #589 from LinusLing/gh-pages
update Control Flow
2016-04-05 14:39:13 +08:00
e2048a5bd6 Merge pull request #591 from Cee/gh-pages
Added information about comparing tuples.
2016-04-05 14:39:07 +08:00
53df155db4 Merge pull request #592 from futantan/develop
Language Reference - Attributes
2016-04-05 14:38:58 +08:00
1e58639fd4 Updated the discussion of the @objc attribute in the Declaration Attributes section to note that enumerations and enumeration cases can use this attribute. 2016-04-04 19:37:58 +08:00
Cee
2aece1573b Added information about comparing tuples. 2016-04-04 15:55:07 +08:00
63baf59044 update Language Reference - Statements 2016-04-03 17:02:09 +08:00
13ba7b263a update Language Reference - Statements 2016-04-03 16:55:44 +08:00
8063e80cd4 update Control Flow 2016-04-03 14:14:15 +08:00
ba7ce72dab Merge pull request #588 from colourful987/develop
update initialization
2016-04-03 07:19:25 +08:00
3984a8eba0 update initialization
delete 类的可失败构造器小节;update 构造失败的传递小节。
2016-04-02 23:35:39 +08:00
94e9ce8896 Update CNAME 2016-03-14 20:55:53 +08:00
6adcf58883 Merge pull request #586 from xinqiu/patch-1
这个地方用许多比较好
2016-03-14 12:24:02 +08:00
8038c724fd 这个地方用许多比较好
The contents of a String can be accessed in various ways
一个`String`的内容可以用许多方式读取.
way加了s,说明前面的various指的是多种。
2016-03-13 23:12:57 +08:00
673a930825 Update CNAME 2016-03-08 15:19:43 +08:00
a4d4563a3e Update README.md 2016-02-21 18:56:59 +08:00
6030252de2 Merge pull request #578 from 949478479/patch-1
根据评论反馈修改个别字词的翻译
2016-02-21 18:53:04 +08:00
2e699265cd 根据评论反馈修改个别字词的翻译 2016-02-06 04:45:24 +08:00
1183b3a790 Merge pull request #576 from Realank/gh-pages
[校对]校对"自动引用计数"章节
2016-01-28 13:10:24 +08:00
4b26824274 Merge remote-tracking branch 'numbbbbb/gh-pages' into gh-pages 2016-01-28 11:22:07 +08:00
8dca4a9691 [校对]“自动引用计数”章节翻译修改
[校对]“自动引用计数”章节翻译修改
2016-01-23 16:33:29 +08:00
af5190d20b Merge pull request #574 from Realank/gh-pages
修改构造器章节的问题
2016-01-23 09:29:10 +08:00
7fa09edc82 删除多余的字 2016-01-23 00:35:53 +08:00
b78c821847 修改"构造过程"章节错误 2016-01-23 00:30:43 +08:00
779529b4d3 修改格式 2016-01-21 16:59:16 +08:00
50593cc385 Merge pull request #573 from heqichang/develop
修改一处语句
2016-01-20 20:44:10 +08:00
fd2fbe9656 校对一处语句 2016-01-20 17:45:52 +08:00
3793e84c51 修改版本记录的纰漏 2016-01-20 09:55:04 +08:00
c1cad1243f 修改翻译和格式 2016-01-19 22:09:13 +08:00
5d26bb572b 修改格式 2016-01-19 21:29:42 +08:00
b8dd7477d0 修改格式 2016-01-19 21:26:02 +08:00
482e136e62 修改表述防止歧义 2016-01-19 21:10:08 +08:00
8301f0146a Merge pull request #572 from GithubChinaCH/develop
add end
2016-01-19 17:20:45 +08:00
64ea022b1b add end 2016-01-19 17:14:19 +08:00
d45fb7a362 Merge pull request #571 from overtrue-forks/gh-pages
“下标脚本“ -> “下标”
2016-01-19 10:48:59 +08:00
d78047411a 下标脚本 -> 下标 #570 2016-01-19 10:47:26 +08:00
1c031cc068 Merge pull request #570 from Realank/gh-pages
修改“下标脚本”为“下标”
2016-01-19 10:00:20 +08:00
3aae7178fe 修改“下标脚本”为“下标” 2016-01-19 09:43:16 +08:00
b51e163ad3 Merge pull request #569 from Realank/gh-pages
[校对]“闭包”~“方法”章节校对和翻译错误修改
2016-01-18 18:27:18 +08:00
42887ea878 修改“方法”章节的翻译错误 2016-01-18 17:19:05 +08:00
ea0fe35ec6 修改错误单词 2016-01-18 15:55:43 +08:00
ddc5308a78 Revert "删除不需要的日期"
This reverts commit 9cc876ef9c.
2016-01-18 13:47:42 +08:00
d150eb864b 规范化命名
Deinitializer=》析构器
2016-01-18 13:20:33 +08:00
9cc876ef9c 删除不需要的日期 2016-01-18 11:30:24 +08:00
6dcaac7e3f 修改错字 2016-01-18 09:26:03 +08:00
50e0d61d7a Merge pull request #568 from qinix/fix-typo
fixed a typo
2016-01-15 17:29:56 +08:00
1a38c70cb3 fixed a typo 2016-01-15 15:55:49 +08:00
9f9c8e9bea Merge pull request #567 from Realank/gh-pages
[校对]"字符串和字符"、"集合类型"等章节校对
2016-01-15 11:02:41 +08:00
999d92438f 优化表达 2016-01-15 07:52:45 +08:00
4dcc7205a2 替换绕口语句
这句话有点绕口,换一种表达
2016-01-15 07:45:11 +08:00
30638918c3 删除多余的字 2016-01-15 07:29:35 +08:00
d3e873f2b4 修改病句 2016-01-14 20:38:12 +08:00
9dc335493e 修正集合类型章节的错误 2016-01-14 20:03:11 +08:00
908b206f33 修正错误
叹号后面应该有空格的
2016-01-14 17:19:10 +08:00
64d9b13501 添加署名 2016-01-14 16:22:00 +08:00
c470ad343e 修改表格标题错误 2016-01-14 16:09:42 +08:00
1472d14d63 修改描述不准确
“ which is a collection of values of type UnicodeScalar.”
这里描述不准确
2016-01-14 16:07:36 +08:00
88ecb81a47 修改错误字符
小狗字符错显示成了?乱码,开头几个字符用逗号隔开可读性更强,就像英文版一样
2016-01-14 16:00:13 +08:00
ba6f7a207e 修改刚刚修改的错误
多了几个括号
2016-01-14 15:55:31 +08:00
b15859e7d1 调整格式
定义类名次,加上``引用和英文描述
2016-01-14 15:54:02 +08:00
a6cd183aba 修改防止歧义
使用地域更易理解,不然容易与之前的索引混淆,并且加上英文原文
2016-01-14 15:47:12 +08:00
e863958b5b 内容错误
terminator不应该有空格,否则每个单词之间会有两个空格,英文版也没有空格
2016-01-14 15:39:25 +08:00
781c484681 修改翻译
“You access and modify a string through its methods and properties”
翻译不准确,并且少了一个字“通”
2016-01-14 15:21:34 +08:00
86a67b7545 修改翻译
REGIONAL应该翻译为地域性
2016-01-14 15:16:28 +08:00
716a0d08eb 修改类型描述
虽然英文原文是“its Boolean isEmpty property”,但是按照翻译的语义,应该是指的具体返回值的类型Bool
2016-01-14 13:57:00 +08:00
4c22e27a11 Markdown 格式修正. 2016-01-05 20:47:23 +08:00
41 changed files with 3889 additions and 3737 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
=============================================
中文版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)
@ -9,17 +9,36 @@
# 在线阅读
使用Gitbook制作可以直接[在线阅读](http://swiftguide.cn/)。
使用 Gitbook 制作,可以直接[在线阅读](http://swiftguide.cn/)。
# 当前阶段
已经更新到Swift 2.1。
# 2.1译者记录
已经更新到 Swift 3.0。 2016-09-23
# 3.0 译者记录
相关[issue](https://github.com/numbbbbb/the-swift-programming-language-in-chinese/issues/628)
- Functions: [crayygy](https://github.com/crayygy)
- Control Flow: [Realank](https://github.com/Realank)
- Closures: [LanfordCai](https://github.com/LanfordCai)
- Protocols: [chenmingbiao](https://github.com/chenmingbiao)
- The Basics:[chenmingbiao](https://github.com/chenmingbiao)
- Advanced Operators: [mmoaay](https://github.com/mmoaay)
Language Reference:
- Attributes: [WhoJave](https://github.com/WhoJave)
- Statements: [chenmingjia](https://github.com/chenmingjia)
- Declarations: [chenmingjia](https://github.com/chenmingjia)
- Expressions: [chenmingjia](https://github.com/chenmingjia)
- Types: [lettleprince](https://github.com/lettleprince)
- Generic Parameters and Arguments: [chenmingjia](https://github.com/chenmingjia)
# 2.1 & 2.2 译者记录
详见各章节开头位置。
# 2.0译者记录
# 2.0 译者记录
- About Swift [xtymichael](https://github.com/xtymichael)
- A Swift Tour[xtymichael](https://github.com/xtymichael)
@ -59,7 +78,7 @@
- Generic Parameters and Arguments[wardenNScaiyi](https://github.com/wardenNScaiyi)
- Summary of the Grammar[miaosiqi](https://github.com/miaosiqi)
# 1.0译者记录
# 1.0 译者记录
* 欢迎使用 Swift
* 关于 Swift ([numbbbbb])

View File

@ -76,7 +76,7 @@ println(sayHelloAgain("Anna"))
func halfOpenRangeLength(start: Int, end: Int) -> Int {
return end - start
}
println(halfOpenRangeLength(1, 10))
println(halfOpenRangeLength(1, end:10))
// prints "9"
```

View File

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

View File

@ -17,7 +17,7 @@
* [类和结构体](chapter2/09_Classes_and_Structures.md)
* [属性](chapter2/10_Properties.md)
* [方法](chapter2/11_Methods.md)
* [下标脚本](chapter2/12_Subscripts.md)
* [下标](chapter2/12_Subscripts.md)
* [继承](chapter2/13_Inheritance.md)
* [构造过程](chapter2/14_Initialization.md)
* [析构过程](chapter2/15_Deinitialization.md)

View File

@ -9,6 +9,9 @@
> 2.0
> 翻译+校对:[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)
@ -17,6 +20,7 @@
- [对象和类Objects and Classes](#objects_and_classes)
- [枚举和结构体Enumerations and Structures](#enumerations_and_structures)
- [协议和扩展Protocols and Extensions](#protocols_and_extensions)
- [错误处理Error Handling](#error_handling)
- [泛型Generics](#generics)
通常来说编程语言教程中的第一个程序应该在屏幕上打印“Hello, world”。在 Swift 中,可以用一行代码实现:
@ -30,8 +34,7 @@ print("Hello, world!")
这个教程会通过一系列编程例子来让你对 Swift 有初步了解,如果你有什么不理解的地方也不用担心——任何本章介绍的内容都会在后面的章节中详细讲解。
> 注意:
> 为了获得最好的体验,在 Xcode 当中使用代码预览功能。代码预览功能可以让你编辑代码并实时看到运行结果。
> <a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.playground.zip">下载Playground</a>
> 在 Mac 上,下载 Playground 并双击文件在 Xcode 里打开:[https://developer.apple.com/go/?id=swift-tour](https://developer.apple.com/go/?id=swift-tour)
<a name="simple_values"></a>
## 简单值
@ -194,7 +197,7 @@ print(largest)
```
> 练习:
> 添加另一个变量来记录现在和之前最大数字的类型
> 添加另一个变量来记录最大数字的种类(kind),同时仍然记录这个最大数字的值
使用`while`来重复运行一段代码直到不满足条件。循环条件也可以在结尾,保证能至少循环一次。
@ -212,20 +215,14 @@ repeat {
print(m)
```
你可以在循环中使用`..<`来表示范围,也可以使用传统的写法,两者是等价的:
你可以在循环中使用`..<`来表示范围
```swift
var firstForLoop = 0
var total = 0
for i in 0..<4 {
firstForLoop += i
total += i
}
print(firstForLoop)
var secondForLoop = 0
for var i = 0; i < 4; ++i {
secondForLoop += i
}
print(secondForLoop)
print(total)
```
使用`..<`创建的范围不包含上界,如果想包含的话需要使用`...`
@ -540,7 +537,7 @@ let aceRawValue = ace.rawValue
> 练习:
> 写一个函数,通过比较它们的原始值来比较两个`Rank`值。
在上面的例子中,枚举原始值的类型是`Int`,所以你只需要设置第一个原始值。剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。使用`rawValue`属性来访问一个枚举成员的原始值。
默认情况下Swift 按照从 0 开始每次加 1 的方式为原始值进行赋值,不过你可以通过显式赋值进行改变。在上面的例子中,`Ace`被显式赋值为 1并且剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。使用`rawValue`属性来访问一个枚举成员的原始值。
使用`init?(rawValue:)`初始化构造器在原始值和枚举值之间进行转换。
@ -550,7 +547,7 @@ if let convertedRank = Rank(rawValue: 3) {
}
```
枚举的成员值是实际值,并不是原始值的另一种表达方法。实际上,以防原始值没有意义,你不需要设置
枚举的成员值是实际值,并不是原始值的另一种表达方法。实际上,如果没有比较有意义的原始值,你不需要提供原始值
```swift
enum Suit {
@ -601,24 +598,24 @@ let threeOfSpadesDescription = threeOfSpades.simpleDescription()
```swift
enum ServerResponse {
case Result(String, String)
case Error(String)
case Failure(String)
}
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 {
case let .Result(sunrise, sunset):
let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
let serverResponse = "Failure... \(error)"
case let .Failure(message):
print("Failure... \(message)")
}
```
> 练习:
> 给`ServerResponse`和`switch`添加第三种情况。
注意如何从`ServerResponse`中提取日升和日落时间并用得到的值用来和`switch`的情况作比较
注意日升和日落时间是如何从`ServerResponse`中提取到并与`switch``case`相匹配的
<a name="protocols_and_extensions"></a>
## 协议和扩展
@ -689,6 +686,88 @@ print(protocolValue.simpleDescription)
即使`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>
## 泛型

View File

@ -1,37 +1,44 @@
# Swift 版本历史记录
# Swift 文档修订历史
---
> 1.0
> 翻译:[成都老码团队翻译组-Arya](http://weibo.com/littlekok/)
> 校对:[成都老码团队翻译组-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)
- [XCode6.3正式版 Swift语法文档更新](#xcode6_3)
- [XCode6.2正式版 Swift语法文档更新](#xcode6_2)
- [XCode6.2 Beta3 Swift语法文档更新](#xcode6_2_Beta3)
- [XCode6.2 Beta2 Swift语法文档更新](#xcode6_2_Beta2)
- [XCode6.2 Beta1 Swift语法文档更新](#xcode6_2_Beta1)
- [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)
- [Swift 2.2 更新](#swift_2_2)
- [Swift 2.1 更新](#swift_2_1)
- [Swift 2.0 更新](#swift_2_0)
- [Swift 1.2 更新](#swift_1_2)
- [Swift 1.1 更新](#swift_1_1)
- [Swift 1.0 更新](#swift_1_0)
以下部分是针对XCode6每一次Beta版本直至正式版发布Swift语法部分的更新归类
<a name="xcode6_4_Beta"></a>
### XCode6.4 Beta中Swift语法更新
***注意苹果在这个版本发布后没有及时的更新Swift Programming Language文档,以下是[老码团队](http://weibo.com/u/5241713117)通过XCode6.4 Beta Release Note总结的更改说明***
<a name="swift_2_2"></a>
### Swift 2.2 更新
<table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead>
@ -42,10 +49,55 @@
</thead>
<tbody>
<tr>
<td scope="row">2015-04-13</td>
<td scope="row">2016-03-21</td>
<td><ul class="list-bullet">
<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>
</ul>
</td>
@ -53,9 +105,195 @@
</tbody>
</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>
### 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">
<thead>
@ -68,6 +306,9 @@
<tr>
<td scope="row">2015-4-8</td>
<td><ul class="list-bullet">
<li>
更新至 Swift 1.2。
</li>
<li>
Swift现在自身提供了一个<code>Set</code>集合类型,更多信息请看<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/CollectionTypes.html#//apple_ref/doc/uid/TP40014097-CH8-ID484">集合</a>
@ -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>
</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>
更新了常量和常量属性在声明和构造时的规则,更多信息,请看<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>
更新了<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>
</ul>
</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>
<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>
</li>
<li>
@ -157,139 +376,9 @@
</tbody>
</table>
<a name="xcode6_2_Beta3"></a>
### XCode6.2 Beta3中Swift语法更新
***注意苹果在这个版本发布后没有及时的更新Swift Programming Language文档,以下是[老码团队](http://weibo.com/u/5241713117)通过XCode6.2 Beta3 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-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系列版本合并***
<a name="swift_1_1"></a>
### Swift 1.1 更新
<table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead>
@ -303,112 +392,32 @@
<td scope="row">2014-10-16</td>
<td><ul class="list-bullet">
<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>
增加了一个关于协议的<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>
`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>
</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>
带有原始值的枚举类型增加了一个<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>
</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>
增加了一个新的关于<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>
自定义运算符现在可以包含`?`字符,更新的<a href="http://developer.apple.com/library/etc/redirect/xcode/devtools/419f35/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html">运算符(Operators)</a>章节描述了改进后的规则,并且从<a href="http://developer.apple.com/library/etc/redirect/xcode/devtools/419f35/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html">自定义运算符(Custom Operators)</a>章节删除了重复的运算符有效字符集合
</li>
</ul>
</td>
</tr>
</tbody>
</table>
<a name="xcode6_beta7"></a>
### XCode6 Beta7中Swift语法更新
***注意苹果在这个版本发布后没有及时的更新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语法更新
<a name="swift_1_0"></a>
### Swift 1.0 更新
<table class="graybox" border="0" cellspacing="0" cellpadding="5">
<thead>
@ -421,6 +430,9 @@
<tr>
<td scope="row">2014-08-18</td>
<td><ul class="list-bullet">
<li>
发布新的文档用以详述 Swift 1.0苹果公司针对iOS和OS X应用的全新开发语言。
</li>
<li>
在章节协议中,增加新的小节:<a href="https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-XID_397">对构造器的规定Initializer Requirements</a>
</li>
@ -436,26 +448,6 @@
<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>特性的一些信息
</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>
<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>
@ -510,26 +502,6 @@
<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>添加了更多的信息.
</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>
加入新的章节 <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>
@ -572,26 +544,6 @@
<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>.
</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>
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>
@ -616,52 +568,6 @@
<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>
</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>
</td>
</tr>

View File

@ -11,6 +11,14 @@
> 2.1
> 翻译:[Prayer](https://github.com/futantan)
> 校对:[shanks](http://codebuild.me)[overtrue](https://github.com/overtrue)
>
> 2.2
> 校对:[SketchK](https://github.com/SketchK)
>
> 3.0
> 校对:[CMB](https://github.com/chenmingbiao)
>
> 版本日期2016-09-13
本页包含内容:
@ -42,27 +50,27 @@
- [错误处理](#error_handling)
- [断言](#assertions)
Swift 是一门开发 iOS, OS X 和 watchOS 应用的新语言。然而,如果你有 C 或者 Objective-C 开发经验的话,你会发现 Swift 的很多内容都是你熟悉的。
Swift 是一门开发 iOS, macOS, watchOS 和 tvOS 应用的新语言。然而,如果你有 C 或者 Objective-C 开发经验的话,你会发现 Swift 的很多内容都是你熟悉的。
Swift 包含了 C 和 Objective-C 上所有基础数据类型,`Int`表示整型值;`Double``Float`表示浮点型值;`Bool`是布尔型值;`String`是文本型数据。Swift 还提供了三个基本的集合类型,`Array``Set``Dictionary`,详见[集合类型](04_Collection_Types.html)。
Swift 包含了 C 和 Objective-C 上所有基础数据类型,`Int`表示整型值; `Double``Float` 表示浮点型值; `Bool` 是布尔型值;`String` 是文本型数据。 Swift 还提供了三个基本的集合类型,`Array` `Set``Dictionary` ,详见[集合类型](04_Collection_Types.html)。
就像 C 语言一样Swift 使用变量来进行存储并通过变量名来关联值。在 Swift 中,广泛的使用着值不可变的变量,它们就是常量,而且比 C 语言的常量更强大。在 Swift 中,如果你要处理的值不需要改变,那使用常量可以让你的代码更加安全并且更清晰地表达你的意图。
除了我们熟悉的类型Swift 还增加了 Objective-C 中没有的高阶数据类型比如元组Tuple。元组可以让你创建或者传递一组数据比如作为函数的返回值时你可以用一个元组可以返回多个值。
Swift 还增加了可选Optional类型用于处理值缺失的情况。可选表示“那儿有一个值并且它等于 x ”或者“那儿没有值”。可选有点像在 Objective-C 中使用`nil`,但是它可以用在任何类型上,不仅仅是类。可选类型比 Objective-C 中的`nil`指针更加安全也更具表现力,它是 Swift 许多强大特性的重要组成部分。
Swift 还增加了可选Optional类型用于处理值缺失的情况。可选表示 “那儿有一个值,并且它等于 x ” 或者 “那儿没有值” 。可选有点像在 Objective-C 中使用 `nil` ,但是它可以用在任何类型上,不仅仅是类。可选类型比 Objective-C 中的 `nil` 指针更加安全也更具表现力,它是 Swift 许多强大特性的重要组成部分。
Swift 是一门类型安全的语言可选类型就是一个很好的例子。Swift 可以让你清楚地知道值的类型。如果你的代码期望得到一个`String`,类型安全会阻止你不小心传入一个`Int`。你可以在开发阶段尽早发现并修正错误。
Swift 是一门类型安全的语言可选类型就是一个很好的例子。Swift 可以让你清楚地知道值的类型。如果你的代码期望得到一个 `String` ,类型安全会阻止你不小心传入一个 `Int` 。同样的,如果你的代码期望得到一个 `String`,类型安全会阻止你意外传入一个可选的 `String` 。你可以在开发阶段尽早发现并修正错误。
<a name="constants_and_variables"></a>
## 常量和变量
常量和变量把一个名字(比如`maximumNumberOfLoginAttempts`或者`welcomeMessage`)和一个指定类型的值(比如数字`10`或者字符串`"Hello"`)关联起来。常量的值一旦设定就不能改变,而变量的值可以随意更改。
常量和变量把一个名字(比如 `maximumNumberOfLoginAttempts` 或者 `welcomeMessage` )和一个指定类型的值(比如数字 `10` 或者字符串 `"Hello"` )关联起来。常量的值一旦设定就不能改变,而变量的值可以随意更改。
<a name="declaring"></a>
### 声明常量和变量
常量和变量必须在使用前声明,用`let`来声明常量,用`var`来声明变量。下面的例子展示了如何用常量和变量来记录用户尝试登录的次数:
常量和变量必须在使用前声明,用 `let` 来声明常量,用 `var` 来声明变量。下面的例子展示了如何用常量和变量来记录用户尝试登录的次数:
```swift
let maximumNumberOfLoginAttempts = 10
@ -71,7 +79,7 @@ var currentLoginAttempt = 0
这两行代码可以被理解为:
“声明一个名字是`maximumNumberOfLoginAttempts`的新常量,并给它一个值`10`。然后,声明一个名字是`currentLoginAttempt`的变量并将它的值初始化为`0`。”
“声明一个名字是 `maximumNumberOfLoginAttempts` 的新常量,并给它一个值 `10` 。然后,声明一个名字是 `currentLoginAttempt` 的变量并将它的值初始化为 `0` 。”
在这个例子中,允许的最大尝试登录次数被声明为一个常量,因为这个值不会改变。当前尝试登录次数被声明为一个变量,因为每次尝试登录失败的时候都需要增加这个值。
@ -81,15 +89,15 @@ var currentLoginAttempt = 0
var x = 0.0, y = 0.0, z = 0.0
```
>注意:
如果你的代码中有不需要改变的值,请使用`let`关键字将它声明为常量。只将需要改变的值声明为变量。
> 注意:
> 如果你的代码中有不需要改变的值,请使用 `let` 关键字将它声明为常量。只将需要改变的值声明为变量。
<a name="type_annotations"></a>
### 类型标注
当你声明常量或者变量的时候可以加上类型标注type annotation说明常量或者变量中要存储的值的类型。如果要添加类型标注需要在常量或者变量名后面加上一个冒号和空格然后加上类型名称。
这个例子给`welcomeMessage`变量添加了类型标注,表示这个变量可以存储`String`类型的值:
这个例子给 `welcomeMessage` 变量添加了类型标注,表示这个变量可以存储 `String` 类型的值:
```swift
var welcomeMessage: String
@ -97,11 +105,11 @@ var welcomeMessage: String
声明中的冒号代表着“是...类型”,所以这行代码可以被理解为:
“声明一个类型为`String`,名字为`welcomeMessage`的变量。”
“声明一个类型为 `String` ,名字为 `welcomeMessage` 的变量。”
“类型为`String`”的意思是“可以存储任意`String`类型的值。”
“类型为 `String` ”的意思是“可以存储任意 `String` 类型的值。”
`welcomeMessage`变量现在可以被设置成任意字符串:
`welcomeMessage` 变量现在可以被设置成任意字符串:
```swift
welcomeMessage = "Hello"
@ -114,7 +122,7 @@ var red, green, blue: Double
```
> 注意:
一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值Swift可以推断出这个常量或者变量的类型请参考[类型安全和类型推断](#type_safety_and_type_inference)。在上面的例子中,没有给`welcomeMessage`赋初始值,所以变量`welcomeMessage`的类型是通过一个类型标注指定的,而不是通过初始值推断的。
> 一般来说你很少需要写类型标注。如果你在声明常量或者变量的时候赋了一个初始值Swift可以推断出这个常量或者变量的类型请参考[类型安全和类型推断](#type_safety_and_type_inference)。在上面的例子中,没有给 `welcomeMessage` 赋初始值,所以变量 `welcomeMessage` 的类型是通过一个类型标注指定的,而不是通过初始值推断的。
<a name="naming"></a>
### 常量和变量的命名
@ -132,7 +140,7 @@ let 🐶🐮 = "dogcow"
一旦你将常量或者变量声明为确定的类型,你就不能使用相同的名字再次进行声明,或者改变其存储的值的类型。同时,你也不能将常量与变量进行互转。
> 注意:
如果你需要使用与Swift保留关键字相同的名称作为常量或者变量名你可以使用反引号`)将关键字包围的方式将其作为名字使用。无论如何,你应当避免使用关键字作为常量或变量名,除非你别无选择。
> 如果你需要使用与Swift保留关键字相同的名称作为常量或者变量名你可以使用反引号`)将关键字包围的方式将其作为名字使用。无论如何,你应当避免使用关键字作为常量或变量名,除非你别无选择。
你可以更改现有的变量值为其他同类型的值,在下面的例子中,`friendlyWelcome`的值从`"Hello!"`改为了`"Bonjour!"`:
@ -160,7 +168,7 @@ print(friendlyWelcome)
// 输出 "Bonjour!"
```
`print(_:separator:terminator:)`是一个用来输出一个或多个值到适当输出区的全局函数。如果你用 Xcode`print(_:separator:terminator:)`将会输出内容到“console”面板上。`separator`和`terminator`参数具有默认值,因此你调用这个函数的时候可以忽略它们。默认情况下,该函数通过添加换行符来结束当前行。如果不想换行,可以传递一个空字符串给`terminator`参数--例如,`print(someValue, terminator:"")`。关于参数默认值的更多信息,请参考[默认参数值](./06_Functions.html#default_parameter_values)。
`print(_:separator:terminator:)` 是一个用来输出一个或多个值到适当输出区的全局函数。如果你用 Xcode`print(_:separator:terminator:)` 将会输出内容到“console”面板上。`separator``terminator` 参数具有默认值,因此你调用这个函数的时候可以忽略它们。默认情况下,该函数通过添加换行符来结束当前行。如果不想换行,可以传递一个空字符串给 `terminator` 参数--例如,`print(someValue, terminator:"")` 。关于参数默认值的更多信息,请参考[默认参数值](./06_Functions.html#default_parameter_values)。
Swift 用字符串插值string interpolation的方式把常量名或者变量名当做占位符加入到长字符串中Swift 会用当前常量或变量的值替换这些占位符。将常量或变量名放入圆括号中,并在开括号前使用反斜杠将其转义:
@ -211,38 +219,39 @@ let cat = "🐱"; print(cat)
<a name="integers"></a>
## 整数
整数就是没有小数部分的数字,比如`42`和`-23`。整数可以是`有符号`(正、负、零)或者`无符号`(正、零)。
整数就是没有小数部分的数字,比如 `42``-23` 。整数可以是 `有符号`(正、负、零)或者 `无符号`(正、零)。
Swift 提供了81632和64位的有符号和无符号整数类型。这些整数类型和 C 语言的命名方式很像比如8位无符号整数类型是`UInt8`32位有符号整数类型是`Int32`。就像 Swift 的其他类型一样,整数类型采用大写命名法。
Swift 提供了81632和64位的有符号和无符号整数类型。这些整数类型和 C 语言的命名方式很像比如8位无符号整数类型是`UInt8`32位有符号整数类型是 `Int32` 。就像 Swift 的其他类型一样,整数类型采用大写命名法。
<a name="integer_bounds"></a>
### 整数范围
你可以访问不同整数类型的`min`和`max`属性来获取对应类型的最小值和最大值:
你可以访问不同整数类型的 `min``max` 属性来获取对应类型的最小值和最大值:
```swift
let minValue = UInt8.min // minValue 为 0是 UInt8 类型
let maxValue = UInt8.max // maxValue 为 255是 UInt8 类型
```
`min`和`max`所传回值的类型,正是其所对的整数类型(如上例UInt8, 所传回的类型是UInt8),可用在表达式中相同类型值旁。
```
`min``max` 所传回值的类型,正是其所对的整数类型(如上例UInt8, 所传回的类型是UInt8),可用在表达式中相同类型值旁。
<a name="Int"></a>
### Int
一般来说你不需要专门指定整数的长度。Swift 提供了一个特殊的整数类型`Int`,长度与当前平台的原生字长相同:
* 在32位平台上`Int`和`Int32`长度相同。
* 在64位平台上`Int`和`Int64`长度相同。
* 在32位平台上`Int``Int32` 长度相同。
* 在64位平台上`Int``Int64` 长度相同。
除非你需要特定长度的整数,一般来说使用`Int`就够了。这可以提高代码一致性和可复用性。即使是在32位平台上`Int`可以存储的整数范围也可以达到`-2,147,483,648`~`2,147,483,647`,大多数时候这已经足够大了。
除非你需要特定长度的整数,一般来说使用 `Int` 就够了。这可以提高代码一致性和可复用性。即使是在32位平台上`Int` 可以存储的整数范围也可以达到 `-2,147,483,648` ~ `2,147,483,647` ,大多数时候这已经足够大了。
<a name="UInt"></a>
### UInt
Swift 也提供了一个特殊的无符号类型`UInt`,长度与当前平台的原生字长相同:
Swift 也提供了一个特殊的无符号类型 `UInt`,长度与当前平台的原生字长相同:
* 在32位平台上`UInt`和`UInt32`长度相同。
* 在64位平台上`UInt`和`UInt64`长度相同。
* 在32位平台上`UInt``UInt32` 长度相同。
* 在64位平台上`UInt``UInt64` 长度相同。
> 注意:
尽量不要使用`UInt`,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用`Int`,即使你要存储的值已知是非负的。统一使用`Int`可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断,请参考[类型安全和类型推断](#type_safety_and_type_inference)。
@ -250,15 +259,15 @@ Swift 也提供了一个特殊的无符号类型`UInt`,长度与当前平台
<a name="floating-point_numbers"></a>
## 浮点数
浮点数是有小数部分的数字,比如`3.14159``0.1`和`-273.15`。
浮点数是有小数部分的数字,比如 `3.14159` `0.1``-273.15`
浮点类型比整数类型表示的范围更大,可以存储比`Int`类型更大或者更小的数字。Swift 提供了两种有符号浮点数类型:
浮点类型比整数类型表示的范围更大,可以存储比 `Int` 类型更大或者更小的数字。Swift 提供了两种有符号浮点数类型:
* `Double`表示64位浮点数。当你需要存储很大或者很高精度的浮点数时请使用此类型。
* `Float`表示32位浮点数。精度要求不高的话可以使用此类型。
> 注意:
`Double`精确度很高至少有15位数字而`Float`只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围。
`Double`精确度很高至少有15位数字`Float`只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围,在两种类型都匹配的情况下,将优先选择 `Double`
<a name="type_safety_and_type_inference"></a>
## 类型安全和类型推断
@ -271,32 +280,32 @@ Swift 是一个*类型安全type safe*的语言。类型安全的语言可
因为有类型推断,和 C 或者 Objective-C 比起来 Swift 很少需要声明类型。常量和变量虽然需要明确类型,但是大部分工作并不需要你自己来完成。
当你声明常量或者变量并赋初值的时候类型推断非常有用。当你在声明常量或者变量的时候赋给它们一个字面量literal value 或 literal即可触发类型推断。字面量就是会直接出现在你代码中的值比如`42`和`3.14159`。)
当你声明常量或者变量并赋初值的时候类型推断非常有用。当你在声明常量或者变量的时候赋给它们一个字面量literal value 或 literal即可触发类型推断。字面量就是会直接出现在你代码中的值比如 `42``3.14159` 。)
例如,如果你给一个新常量赋值`42`并且没有标明类型Swift 可以推断出常量类型是`Int`,因为你给它赋的初始值看起来像一个整数:
例如,如果你给一个新常量赋值 `42` 并且没有标明类型Swift 可以推断出常量类型是 `Int` ,因为你给它赋的初始值看起来像一个整数:
```swift
let meaningOfLife = 42
// meaningOfLife 会被推测为 Int 类型
```
同理如果你没有给浮点字面量标明类型Swift 会推断你想要的是`Double`
同理如果你没有给浮点字面量标明类型Swift 会推断你想要的是 `Double`
```swift
let pi = 3.14159
// pi 会被推测为 Double 类型
```
当推断浮点数的类型时Swift 总是会选择`Double`而不是`Float`。
当推断浮点数的类型时Swift 总是会选择 `Double` 而不是`Float`
如果表达式中同时出现了整数和浮点数,会被推断为`Double`类型:
如果表达式中同时出现了整数和浮点数,会被推断为 `Double` 类型:
```swift
let anotherPi = 3 + 0.14159
// anotherPi 会被推测为 Double 类型
```
原始值`3`没有显式声明类型,而表达式中出现了一个浮点字面量,所以表达式会被推断为`Double`类型。
原始值 `3` 没有显式声明类型,而表达式中出现了一个浮点字面量,所以表达式会被推断为 `Double` 类型。
<a name="numeric_literals"></a>
## 数值型字面量
@ -317,13 +326,15 @@ let octalInteger = 0o21 // 八进制的17
let hexadecimalInteger = 0x11 // 十六进制的17
```
浮点字面量可以是十进制(没有前缀)或者是十六进制(前缀是`0x`。小数点两边必须有至少一个十进制数字或者是十六进制的数字。十进制浮点数也可以有一个可选的指数exponent),通过大写或者小写的 `e` 来指定;十六进制浮点数必须有一个指数,通过大写或者小写的 `p` 来指定。
浮点字面量可以是十进制(没有前缀)或者是十六进制(前缀是 `0x` 。小数点两边必须有至少一个十进制数字或者是十六进制的数字。十进制浮点数也可以有一个可选的指数exponent),通过大写或者小写的 `e` 来指定;十六进制浮点数必须有一个指数,通过大写或者小写的 `p` 来指定。
如果一个十进制数的指数为 `exp`那这个数相当于基数和10^exp的乘积
如果一个十进制数的指数为`exp`那这个数相当于基数和10^exp的乘积
* `1.25e2` 表示 1.25 × 10^2等于 `125.0`
* `1.25e-2` 表示 1.25 × 10^-2等于 `0.0125`
如果一个十六进制数的指数为`exp`那这个数相当于基数和2^exp的乘积
如果一个十六进制数的指数为`exp`那这个数相当于基数和2^exp的乘积
* `0xFp2` 表示 15 × 2^2等于 `60.0`
* `0xFp-2` 表示 15 × 2^-2等于 `3.75`
@ -372,9 +383,9 @@ let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
```
现在两个数字的类型都是`UInt16`,可以进行相加。目标常量`twoThousandAndOne`的类型被推断为`UInt16`,因为它是两个`UInt16`值的和。
现在两个数字的类型都是 `UInt16`,可以进行相加。目标常量 `twoThousandAndOne` 的类型被推断为 `UInt16`,因为它是两个 `UInt16` 值的和。
`SomeType(ofInitialValue)`是调用 Swift 构造器并传入一个初始值的默认方法。在语言内部,`UInt16`有一个构造器,可以接受一个`UInt8`类型的值,所以这个构造器可以用现有的`UInt8`来创建一个新的`UInt16`。注意,你并不能传入任意类型的值,只能传入`UInt16`内部有对应构造器的值。不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型),请参考[扩展](./20_Extensions.html)。
`SomeType(ofInitialValue)` 是调用 Swift 构造器并传入一个初始值的默认方法。在语言内部,`UInt16` 有一个构造器,可以接受一个`UInt8`类型的值,所以这个构造器可以用现有的 `UInt8` 来创建一个新的 `UInt16`。注意,你并不能传入任意类型的值,只能传入 `UInt16` 内部有对应构造器的值。不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型),请参考[扩展](./20_Extensions.html)。
<a name="integer_and_floating_point_conversion"></a>
### 整数和浮点数转换
@ -388,16 +399,16 @@ let pi = Double(three) + pointOneFourOneFiveNine
// pi 等于 3.14159,所以被推测为 Double 类型
```
这个例子中,常量`three`的值被用来创建一个`Double`类型的值,所以加号两边的数类型须相同。如果不进行转换,两者无法相加。
这个例子中,常量 `three` 的值被用来创建一个 `Double` 类型的值,所以加号两边的数类型须相同。如果不进行转换,两者无法相加。
浮点数到整数的反向转换同样行,整数类型可以用`Double`或者`Float`类型来初始化:
浮点数到整数的反向转换同样行,整数类型可以用 `Double` 或者 `Float` 类型来初始化:
```swift
let integerPi = Int(pi)
// integerPi 等于 3所以被推测为 Int 类型
```
当用这种方式来初始化一个新的整数值时,浮点值会被截断。也就是说`4.75`会变成`4``-3.9`会变成`-3`
当用这种方式来初始化一个新的整数值时,浮点值会被截断。也就是说 `4.75` 会变成 `4``-3.9` 会变成 `-3`
> 注意:
结合数字类常量和变量不同于结合数字类字面量。字面量`3`可以直接和字面量`0.14159`相加,因为数字字面量本身没有明确的类型。它们的类型只在编译器需要求值的时候被推测。
@ -425,16 +436,16 @@ var maxAmplitudeFound = AudioSample.min
<a name="booleans"></a>
## 布尔值
Swift 有一个基本的布尔Boolean类型叫做`Bool`。布尔值指逻辑上的值因为它们只能是真或者假。Swift 有两个布尔常量,`true`和`false`
Swift 有一个基本的布尔Boolean类型叫做`Bool`。布尔值指逻辑上的值因为它们只能是真或者假。Swift 有两个布尔常量,`true``false`
```swift
let orangesAreOrange = true
let turnipsAreDelicious = false
```
`orangesAreOrange`和`turnipsAreDelicious`的类型会被推断为`Bool`,因为它们的初值是布尔字面量。就像之前提到的`Int`和`Double`一样,如果你创建变量的时候给它们赋值`true`或者`false`,那你不需要将常量或者变量声明为`Bool`类型。初始化常量或者变量的时候如果所赋的值类型已知,就可以触发类型推断,这让 Swift 代码更加简洁并且可读性更高。
`orangesAreOrange``turnipsAreDelicious` 的类型会被推断为 `Bool`,因为它们的初值是布尔字面量。就像之前提到的 `Int``Double` 一样,如果你创建变量的时候给它们赋值 `true` 或者 `false`,那你不需要将常量或者变量声明为 `Bool` 类型。初始化常量或者变量的时候如果所赋的值类型已知,就可以触发类型推断,这让 Swift 代码更加简洁并且可读性更高。
当你编写条件语句比如`if`语句的时候,布尔值非常有用:
当你编写条件语句比如 `if` 语句的时候,布尔值非常有用:
```swift
if turnipsAreDelicious {
@ -447,7 +458,7 @@ if turnipsAreDelicious {
条件语句,例如`if`,请参考[控制流](./05_Control_Flow.html)。
如果你在需要使用`Bool`类型的地方使用了非布尔值Swift 的类型安全机制会报错。下面的例子会报告一个编译时错误:
如果你在需要使用 `Bool` 类型的地方使用了非布尔值Swift 的类型安全机制会报错。下面的例子会报告一个编译时错误:
```swift
let i = 1
@ -465,7 +476,7 @@ if i == 1 {
}
```
`i == 1`的比较结果是`Bool`类型,所以第二个例子可以通过类型检查。类似`i == 1`这样的比较,请参考[基本操作符](./05_Control_Flow.html)。
`i == 1` 的比较结果是 `Bool` 类型,所以第二个例子可以通过类型检查。类似 `i == 1` 这样的比较,请参考[基本操作符](./05_Control_Flow.html)。
和 Swift 中的其他类型安全的例子一样,这个方法可以避免错误并保证这块代码的意图总是清晰的。
@ -474,16 +485,16 @@ if i == 1 {
*元组tuples*把多个值组合成一个复合值。元组内的值可以是任意类型,并不要求是相同类型。
下面这个例子中,`(404, "Not Found")`是一个描述 *HTTP 状态码HTTP status code*的元组。HTTP 状态码是当你请求网页的时候 web 服务器返回的一个特殊值。如果你请求的网页不存在就会返回一个`404 Not Found`状态码。
下面这个例子中,`(404, "Not Found")` 是一个描述 *HTTP 状态码HTTP status code*的元组。HTTP 状态码是当你请求网页的时候 web 服务器返回的一个特殊值。如果你请求的网页不存在就会返回一个 `404 Not Found` 状态码。
```swift
let http404Error = (404, "Not Found")
// http404Error 的类型是 (Int, String),值是 (404, "Not Found")
```
`(404, "Not Found")`元组把一个`Int`值和一个`String`值组合起来表示 HTTP 状态码的两个部分:一个数字和一个人类可读的描述。这个元组可以被描述为“一个类型为`(Int, String)`的元组”。
`(404, "Not Found")` 元组把一个 `Int` 值和一个 `String` 值组合起来表示 HTTP 状态码的两个部分:一个数字和一个人类可读的描述。这个元组可以被描述为“一个类型为 `(Int, String)` 的元组”。
你可以把任意顺序的类型组合成一个元组,这个元组可以包含所有类型。只要你想,你可以创建一个类型为`(Int, Int, Int)`或者`(String, Bool)`或者其他任何你想要的组合的元组。
你可以把任意顺序的类型组合成一个元组,这个元组可以包含所有类型。只要你想,你可以创建一个类型为 `(Int, Int, Int)` 或者 `(String, Bool)` 或者其他任何你想要的组合的元组。
你可以将一个元组的内容分解decompose成单独的常量和变量然后你就可以正常使用它们了
@ -527,7 +538,7 @@ print("The status message is \(http200Status.description)")
// 输出 "The status message is OK"
```
作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个`(Int, String)`元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。请参考[函数参数与返回值](./06_Functions.html#Function_Parameters_and_Return_Values)。
作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个 `(Int, String)` 元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。请参考[函数参数与返回值](./06_Functions.html#Function_Parameters_and_Return_Values)。
> 注意:
元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。请参考[类和结构体](./09_Classes_and_Structures.html)。
@ -544,11 +555,11 @@ print("The status message is \(http200Status.description)")
* 没有值
> 注意:
C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回`nil``nil`表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型Objective-C 方法一般会返回一个特殊值(比如`NSNotFound`来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而Swift 的可选类型可以让你暗示_任意类型_的值缺失,并不需要一个特殊值。
C 和 Objective-C 中并没有可选类型这个概念。最接近的是 Objective-C 中的一个特性,一个方法要不返回一个对象要不返回`nil``nil`表示“缺少一个合法的对象”。然而,这只对对象起作用——对于结构体,基本的 C 类型或者枚举类型不起作用。对于这些类型Objective-C 方法一般会返回一个特殊值(比如`NSNotFound`来暗示值缺失。这种方法假设方法的调用者知道并记得对特殊值进行判断。然而Swift 的可选类型可以让你暗示*任意类型*的值缺失,并不需要一个特殊值。
来看一个例子。Swift 的`String`类型有一种构造器,作用是将一个`String`值转换成一个`Int`值。然而,并不是所有的字符串都可以转换成一个整数。字符串`"123"`可以被转换成数字`123`,但是字符串`"hello, world"`不行。
来看一个例子。Swift 的 `Int` 类型有一种构造器,作用是将一个 `String` 值转换成一个 `Int` 值。然而,并不是所有的字符串都可以转换成一个整数。字符串 `"123"` 可以被转换成数字 `123` ,但是字符串 `"hello, world"` 不行。
下面的例子使用这种构造器来尝试将一个`String`转换成`Int`
下面的例子使用这种构造器来尝试将一个 `String` 转换成 `Int`
```swift
let possibleNumber = "123"
@ -556,7 +567,7 @@ let convertedNumber = Int(possibleNumber)
// convertedNumber 被推测为类型 "Int?" 或者类型 "optional Int"
```
因为该构造器可能会失败,所以它返回一个_可选类型_optional`Int`,而不是一个`Int`。一个可选的`Int`被写作`Int?`而不是`Int`。问号暗示包含的值是可选类型,也就是说可能包含`Int`值也可能*不包含值*。(不能包含其他任何值比如`Bool`值或者`String`值。只能是`Int`或者什么都没有。)
因为该构造器可能会失败,所以它返回一个*可选类型*optional`Int`,而不是一个 `Int`。一个可选的 `Int` 被写作 `Int?` 而不是 `Int`。问号暗示包含的值是可选类型,也就是说可能包含 `Int` 值也可能*不包含值*。(不能包含其他任何值比如 `Bool` 值或者 `String` 值。只能是 `Int` 或者什么都没有。)
<a name="nil"></a>
### nil
@ -573,7 +584,7 @@ serverResponseCode = nil
> 注意:
`nil`不能用于非可选的常量和变量。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。
如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为`nil`
如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为 `nil`
```swift
var surveyAnswer: String?
@ -581,22 +592,23 @@ var surveyAnswer: String?
```
> 注意:
Swift 的`nil`和 Objective-C 中的`nil`并不一样。在 Objective-C 中,`nil`是一个指向不存在对象的指针。在 Swift 中,`nil`不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为`nil`,不只是对象类型。
Swift 的 `nil` 和 Objective-C 中的 `nil` 并不一样。在 Objective-C 中,`nil` 是一个指向不存在对象的指针。在 Swift 中,`nil` 不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为 `nil`,不只是对象类型。
<a name="if"></a>
### if 语句以及强制解析
你可以使用`if`语句和`nil`比较来判断一个可选值是否包含值。你可以使用“相等”(`==`)或“不等”(`!=`)来执行比较。
你可以使用 `if` 语句和 `nil` 比较来判断一个可选值是否包含值。你可以使用“相等”(`==`)或“不等”(`!=`)来执行比较。
如果可选类型有值,它将不等于`nil`:
如果可选类型有值,它将不等于 `nil`
```swift
if convertedNumber != nil {
print("convertedNumber contains some integer value.")
}
// 输出 "convertedNumber contains some integer value."
```
当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(`!`来获取值。这个惊叹号表示“我知道这个可选有值请使用它。”这被称为可选值的_强制解析forced unwrapping_
```
当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(`!`)来获取值。这个惊叹号表示“我知道这个可选有值,请使用它。”这被称为可选值的*强制解析forced unwrapping*
```swift
if convertedNumber != nil {
@ -605,17 +617,17 @@ if convertedNumber != nil {
// 输出 "convertedNumber has an integer value of 123."
```
更多关于`if`语句的内容,请参考[控制流](05_Control_Flow.html)。
更多关于 `if` 语句的内容,请参考[控制流](05_Control_Flow.html)。
> 注意:
使用`!`来获取一个不存在的可选值会导致运行时错误。使用`!`来强制解析值之前,一定要确定可选包含一个非`nil`的值。
使用 `!` 来获取一个不存在的可选值会导致运行时错误。使用 `!` 来强制解析值之前,一定要确定可选包含一个非 `nil` 的值。
<a name="optional_binding"></a>
### 可选绑定
使用*可选绑定optional binding*来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在`if`和`while`语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量。`if`和`while`语句,请参考[控制流](./05_Control_Flow.html)。
使用*可选绑定optional binding*来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在 `if``while` 语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量。`if``while` 语句,请参考[控制流](./05_Control_Flow.html)。
像下面这样在`if`语句中写一个可选绑定:
像下面这样在 `if` 语句中写一个可选绑定:
```swift
if let constantName = someOptional {
@ -623,7 +635,7 @@ if let constantName = someOptional {
}
```
你可以像上面这样使用可选绑定来重写`possibleNumber`这个[例子](./01_The_Basics.html#optionals)
你可以像上面这样使用可选绑定来重写 `possibleNumber` 这个[例子](./01_The_Basics.html#optionals)
```swift
if let actualNumber = Int(possibleNumber) {
@ -636,25 +648,37 @@ if let actualNumber = Int(possibleNumber) {
这段代码可以被理解为:
“如果`Int(possibleNumber)`返回的可选`Int`包含一个值,创建一个叫做`actualNumber`的新常量并将可选包含的值赋给它。”
“如果 `Int(possibleNumber)` 返回的可选 `Int` 包含一个值,创建一个叫做 `actualNumber` 的新常量并将可选包含的值赋给它。”
如果转换成功,`actualNumber`常量可以在`if`语句的第一个分支中使用。它已经被可选类型_包含的_值初始化过,所以不需要再使用`!`后缀来获取它的值。在这个例子中,`actualNumber`只被用来输出转换结果。
如果转换成功,`actualNumber` 常量可以在 `if` 语句的第一个分支中使用。它已经被可选类型 *包含的* 值初始化过,所以不需要再使用 `!` 后缀来获取它的值。在这个例子中,`actualNumber` 只被用来输出转换结果。
你可以在可选绑定中使用常量和变量。如果你想在`if`语句的第一个分支中操作`actualNumber`的值,你可以改成`if var actualNumber`,这样可选类型包含的值就会被赋给一个变量而非常量。
你可以在可选绑定中使用常量和变量。如果你想在`if`语句的第一个分支中操作 `actualNumber` 的值,你可以改成 `if var actualNumber`,这样可选类型包含的值就会被赋给一个变量而非常量。
你可以包含多个可选绑定在`if`语句中,并使用`where`子句做布尔值判断。
你可以包含多个可选绑定或多个布尔条件在一个 `if` 语句中,只要使用逗号分开就行。如果所有可选绑定的值为 `nil` 或者所有布尔条件语句都为 `false`,这样整个 `if` 条件判断都是为 `false`,这时你就需要使用嵌套 `if` 条件语句来处理,如下所示:
```swift
if let firstNumber = Int("4"), secondNumber = Int("42") where firstNumber < secondNumber {
print("\(firstNumber) < \(secondNumber)")
}
// prints "4 < 42"
```
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
// Prints "4 < 42 < 100"
if let firstNumber = Int("4") {
if let secondNumber = Int("42") {
if firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
}
}
// Prints "4 < 42 < 100"
```
> 注意:
> 在 `if` 条件语句中使用常量和变量来创建一个可选绑定,仅在 `if` 语句的句中(`body`)中才能获取到值。相反,在 `guard` 语句中使用常量和变量来创建一个可选绑定,仅在 `guard` 语句外且在语句后才能获取到值,请参考[控制流](./05_Control_Flow#early_exit.html)。
<a name="implicityly_unwrapped_optionals"></a>
### 隐式解析可选类型
如上所述,可选类型暗示了常量或者变量可以“没有值”。可选可以通过`if`语句来判断是否有值,如果有值的话可以通过可选绑定来解析值。
如上所述,可选类型暗示了常量或者变量可以“没有值”。可选可以通过 `if` 语句来判断是否有值,如果有值的话可以通过可选绑定来解析值。
有时候在程序架构中第一次被赋值之后可以确定一个可选类型_总会_有值。在这种情况下每次都要判断和解析可选值是非常低效的因为可以确定它总会有值。
@ -662,7 +686,7 @@ if let firstNumber = Int("4"), secondNumber = Int("42") where firstNumber < seco
当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考[无主引用以及隐式解析可选属性](./16_Automatic_Reference_Counting.html#unowned_references_and_implicitly_unwrapped_optional_properties)。
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型`String`和隐式解析可选类型`String`之间的区别:
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型 `String` 和隐式解析可选类型 `String` 之间的区别:
```swift
let possibleString: String? = "An optional string."
@ -675,7 +699,7 @@ let implicitString: String = assumedString // 不需要感叹号
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾。
> 注意:
如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。
> 如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。
你仍然可以把隐式解析可选类型当做普通可选类型来判断它是否包含值:
@ -696,11 +720,11 @@ if let definiteString = assumedString {
```
> 注意:
如果一个变量之后可能变成`nil`的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是`nil`的话,请使用普通可选类型。
> 如果一个变量之后可能变成`nil`的话请不要使用隐式解析可选类型。如果你需要在变量的生命周期中判断是否是`nil`的话,请使用普通可选类型。
<a name="error_handling"></a>
## 错误处理
你可以使用*错误处理error handling*来应对程序执行中可能会遇到的错误条件。
你可以使用 *错误处理error handling* 来应对程序执行中可能会遇到的错误条件。
相对于可选中运用值的存在与缺失来表达函数的成功与失败,错误处理可以推断失败的原因,并传播至程序的其他部分。
@ -728,38 +752,38 @@ do {
这里有一个错误处理如何用来应对不同错误条件的例子。
```swift
func makeASandwich() throws {
// ...
}
do {
try makeASandwich()
eatASandwich()
} catch Error.OutOfCleanDishes {
washDishes()
} catch Error.MissingIngredients(let ingredients) {
buyGroceries(ingredients)
func makeASandwich() throws {
// ...
}
do {
try makeASandwich()
eatASandwich()
} catch SandwichError.outOfCleanDishes {
washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
buyGroceries(ingredients)
}
```
在此例中,`makeASandwich()`(做一个三明治)函数会抛出一个错误消息如果没有干净的盘子或者某个原料缺失。因为`makeASandwich()`抛出错误,函数调用被包裹在`try`表达式中。将函数包裹在一个`do`语句中,任何被抛出的错误会被传播到提供的`catch`从句中。
在此例中,`makeASandwich()`(做一个三明治)函数会抛出一个错误消息如果没有干净的盘子或者某个原料缺失。因为 `makeASandwich()` 抛出错误,函数调用被包裹在 `try` 表达式中。将函数包裹在一个 `do` 语句中任何被抛出的错误会被传播到提供的 `catch` 从句中。
如果没有错误被抛出, `eatASandwich()`函数会被调用。如果一个匹配`Error.OutOfCleanDishes`的错误被抛出,`washDishes`函数会被调用。如果一个匹配`Error.MissingIngredients`的错误被抛出,`buyGroceries(_:)`函数会随着被`catch`所捕捉到的关联值`[String]`被调用
如果没有错误被抛出`eatASandwich()` 函数会被调用。如果一个匹配 `SandwichError.outOfCleanDishes` 的错误被抛出`washDishes()` 函数会被调用。如果一个匹配 `SandwichError.missingIngredients` 的错误被抛出,`buyGroceries(_:)` 函数会被调用,并且使用 `catch` 所捕捉到的关联值 `[String]` 作为参数
抛出,捕捉,以及传播错误会在[错误处理](./18_Error_Handling.html)章节详细说明。
<a name="assertions"></a>
## 断言
可选类型可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。然而,在某些情况下,如果值缺失或者值并不满足特定的条件,你的代码可能没办法继续执行。这时,你可以在你的代码中触发一个*断言assertion*来结束代码运行并通过调试来找到值缺失的原因。
可选类型可以让你判断值是否存在,你可以在代码中优雅地处理值缺失的情况。然而,在某些情况下,如果值缺失或者值并不满足特定的条件,你的代码可能没办法继续执行。这时,你可以在你的代码中触发一个 *断言assertion* 来结束代码运行并通过调试来找到值缺失的原因。
### 使用断言进行调试
断言会在运行时判断一个逻辑条件是否为`true`。从字面意思来说,断言“断言”一个条件是否为真。你可以使用断言来保证在运行其他代码之前,某些重要的条件已经被满足。如果条件判断为`true`,代码运行会继续进行;如果条件判断为`false`,代码执行结束,你的应用被终止。
断言会在运行时判断一个逻辑条件是否为 `true`。从字面意思来说,断言“断言”一个条件是否为真。你可以使用断言来保证在运行其他代码之前,某些重要的条件已经被满足。如果条件判断为 `true`,代码运行会继续进行;如果条件判断为 `false`,代码执行结束,你的应用被终止。
如果你的代码在调试环境下触发了一个断言,比如你在 Xcode 中构建并运行一个应用,你可以清楚地看到不合法的状态发生在哪里并检查断言被触发时你的应用的状态。此外,断言允许你附加一条调试信息。
你可以使用全局`assert(_:_file:line:)`函数来写一个断言。向这个函数传入一个结果为`true`或者`false`的表达式以及一条信息,当表达式的结果为`false`的时候这条信息会被显示:
你可以使用全局 `assert(_:_:file:line:)` 函数来写一个断言。向这个函数传入一个结果为 `true` 或者 `false` 的表达式以及一条信息,当表达式的结果为 `false` 的时候这条信息会被显示:
```swift
let age = -3
@ -767,7 +791,7 @@ assert(age >= 0, "A person's age cannot be less than zero")
// 因为 age < 0所以断言会触发
```
在这个例子中,只有`age >= 0`为`true`的时候,即`age`的值非负的时候,代码才会继续执行。如果`age`的值是负数,就像代码中那样,`age >= 0`为`false`,断言被触发,终止应用。
在这个例子中,只有 `age >= 0``true` 的时候,即 `age` 的值非负的时候,代码才会继续执行。如果 `age` 的值是负数,就像代码中那样,`age >= 0``false`,断言被触发,终止应用。
如果不需要断言信息,可以省略,就像这样:
@ -776,17 +800,17 @@ assert(age >= 0)
```
> 注意:
当代码使用优化编译的时候,断言将会被禁用,例如在 Xcode 中,使用默认的 target Release 配置选项来 build 时,断言会被禁用。
> 当代码使用优化编译的时候,断言将会被禁用,例如在 Xcode 中,使用默认的 target Release 配置选项来 build 时,断言会被禁用。
### 何时使用断言
当条件可能为假时使用断言但是最终一定要_保证_条件为真这样你的代码才能继续运行。断言的适用情景
* 整数类型的下标索引被传入一个自定义下标脚本实现,但是下标索引值可能太小或者太大。
* 整数类型的下标索引被传入一个自定义下标实现,但是下标索引值可能太小或者太大。
* 需要给函数传入一个值,但是非法的值可能导致函数不能正常执行。
* 一个可选值现在是`nil`,但是后面的代码运行需要一个非`nil`值。
* 一个可选值现在是 `nil`,但是后面的代码运行需要一个非 `nil` 值。
请参考[下标脚本](./12_Subscripts.html)和[函数](./06_Functions.html)。
请参考[下标](./12_Subscripts.html)和[函数](./06_Functions.html)。
> 注意:
断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。
> 断言可能导致你的应用终止运行,所以你应当仔细设计你的代码来让非法条件不会出现。然而,在你的应用发布之前,有时候非法条件可能出现,这时使用断言可以快速发现问题。

View File

@ -6,46 +6,49 @@
> 校对:[EvilCome](https://github.com/Evilcome)
> 2.0
> 翻译+校对:[JackAlan](https://github.com/AlanMelody)
> 翻译+校对:[JackAlan](https://github.com/AlanMelody)
> 2.1
> 校对:[shanks](http://codebuild.me)
> 2.2
> 翻译+校对:[Cee](https://github.com/Cee) 校对:[SketchK](https://github.com/SketchK)2016-05-11
本页包含内容:
- [术语](#terminology)
- [赋值运算符](#assignment_operator)
- [算术运算符](#arithmetic_operators)
- [组合赋值运算符Compound Assignment Operators](#compound_assignment_operators)
- [组合赋值运算符](#compound_assignment_operators)
- [比较运算符](#comparison_operators)
- [三目运算符Ternary Conditional Operator](#ternary_conditional_operator)
- [三目运算符](#ternary_conditional_operator)
- [空合运算符](#nil_coalescing_operator)
- [区间运算符](#range_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)。
区别于 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`)。一元运算符分前置运算符和后置运算符,前置运算符需紧跟在操作对象之前(如`!b`),后置运算符需紧跟在操作对象之后(如`i++`)。
- 二元运算符操作两个操作对象(如`2 + 3`),是中置的,因为它们出现在两个操作对象之间。
- 一元运算符对单一操作对象操作(如 `-a`)。一元运算符分前置运算符和后置运算符,前置运算符需紧跟在操作对象之前(如 `!b`),后置运算符需紧跟在操作对象之后(如 `c!`)。
- 二元运算符操作两个操作对象(如 `2 + 3`),是中置的,因为它们出现在两个操作对象之间。
- 三元运算符操作三个操作对象,和 C 语言一样Swift 只有一个三元运算符,就是三目运算符(`a ? b : c`)。
受运算符影响的值叫操作数,在表达式`1 + 2`中,加号`+`是二元运算符,它的两个操作数是值`1``2`
受运算符影响的值叫操作数,在表达式 `1 + 2` 中,加号 `+` 是二元运算符,它的两个操作数是值 `1``2`
<a name="assignment_operator"></a>
## 赋值运算符
赋值运算(`a = b`),表示用`b`的值来初始化或更新`a`的值:
赋值运算(`a = b`),表示用 `b` 的值来初始化或更新 `a` 的值:
```swift
let b = 10
@ -58,7 +61,7 @@ a = b
```swift
let (x, y) = (1, 2)
// 现在 x 等于 1, y 等于 2
// 现在 x 等于 1y 等于 2
```
与 C 语言和 Objective-C 不同Swift 的赋值操作并不返回任何值。所以以下代码是错误的:
@ -69,7 +72,7 @@ if x = y {
}
```
这个特性使你无法把(`==`)错写成(`=`),由于`if x = y`是错误代码Swift帮你避免此类错误的的发生。
这个特性使你无法把(`==`)错写成(`=`),由于 `if x = y` 是错误代码Swift帮你避免此类错误发生。
<a name="arithmetic_operators"></a>
## 算术运算符
@ -88,9 +91,9 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
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
"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")
2倍非常好那余数是1用橙色标出
你可以在 `9` 中放入两个 `4`,那余数是 1用橙色标出
在 Swift 中可以表达为:
@ -115,13 +118,13 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
9 % 4 // 等于 1
```
为了得到`a % b`的结果,`%`计算了以下等式,并输出`余数`作为结果:
为了得到 `a % b` 的结果,`%` 计算了以下等式,并输出`余数`作为结果:
a = (b × 倍数) + 余数
`倍数`取最大值的时候,就会刚好可以容入`a`中。
`倍数`取最大值的时候,就会刚好可以容入 `a` 中。
`9``4`代入等式中,我们得`1`
`9``4` 代入等式中,我们得 `1`
9 = (4 × 2) + 1
@ -131,13 +134,13 @@ Swift 中所有数值类型都支持了基本的四则算术运算:
-9 % 4 // 等于 -1
```
`-9``4`代入等式,`-2`是取到的最大整数:
`-9``4` 代入等式,`-2` 是取到的最大整数:
-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`等于`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")
### 自增和自减运算
和 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
let three = 3
@ -205,28 +176,29 @@ let minusSix = -6
let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6
```
虽然一元`+`什么都不会改变,但当你在使用一元负号来表达负数时,你可以使用一元正号来表达正数,如此你的代码会具有对称美。
虽然一元 `+` 什么都不会改变,但当你在使用一元负号来表达负数时,你可以使用一元正号来表达正数,如此你的代码会具有对称美。
<a name="compound_assignment_operators"></a>
## 组合赋值运算符Compound Assignment Operators
## 组合赋值运算符
如同 C 语言Swift 也提供把其他运算符和赋值运算(`=`)组合的组合赋值运算符,组合加运算(`+=`)是其中一个例子:
```swift
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`这类代码是错误。这不同于上面提到的自增和自减运算符。
在[表达式](../chapter3/04_Expressions.html)章节里有复合运算符的完整列表。
<a name="comparison_operators"></a>
## 比较运算符
## 比较运算符Comparison Operators
所有标准 C 语言中的比较运算都可以在 Swift 中使用:
@ -238,7 +210,7 @@ a += 2 // a 现在是 3
- 小于等于(`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"
```
关于`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>
## 三目运算符(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
let contentHeight = 40
@ -303,28 +288,25 @@ if hasHeader {
// rowHeight 现在是 90
```
第一段代码例子使用了三目运算,所以一行代码就能让我们得到正确答案。这比第二段代码简洁得多,无需将`rowHeight`定义成变量,因为它的值无需在`if`语句中改变。
第一段代码例子使用了三目运算,所以一行代码就能让我们得到正确答案。这比第二段代码简洁得多,无需将 `rowHeight` 定义成变量,因为它的值无需在 `if` 语句中改变。
三目运算提供有效率且便捷的方式来表达二选一的选择。需要注意的事,过度使用三目运算符会使简洁的代码变的难懂。我们应避免在一个组合语句中使用多个三目运算符。
<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
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`变量被定义为一个可选`String`类型,默认值为`nil`。由于`userDefinedColorName`是一个可选类型,我们可以使用空合运算符去判断其值。在上一个例子中,通过空合运算符为一个名为`colorNameToUse`的变量赋予一个字符串类型初始值。
由于`userDefinedColorName`值为空,因此表达式`userDefinedColorName ?? defaultColorName`返回`defaultColorName`的值,即`red`
`userDefinedColorName` 变量被定义为一个可选`String` 类型,默认值为 `nil`。由于 `userDefinedColorName` 是一个可选类型,我们可以使用空合运算符去判断其值。在上一个例子中,通过空合运算符为一个名为 `colorNameToUse` 的变量赋予一个字符串类型初始值。
由于 `userDefinedColorName` 值为空,因此表达式 `userDefinedColorName ?? defaultColorName` 返回 `defaultColorName` 的值,即 `red`
另一种情况,分配一个非空值(`non-nil`)给`userDefinedColorName`,再次执行空合运算,运算结果为封包在`userDefaultColorName`中的值,而非默认值。
另一种情况,分配一个非空值`non-nil`)给 `userDefinedColorName`,再次执行空合运算,运算结果为封包在 `userDefaultColorName` 中的值,而非默认值。
```swift
userDefinedColorName = "green"
@ -348,14 +330,14 @@ colorNameToUse = userDefinedColorName ?? defaultColorName
```
<a name="range_operators"></a>
## 区间运算符
## 区间运算符Range Operators
Swift 提供了两个方便表达一个区间的值的运算符。
### 闭区间运算符
闭区间运算符(`a...b`)定义一个包含从`a``b`(包括`a``b`)的所有值的区间`b`必须大于等于`a`
闭区间运算符(`a...b`)定义一个包含从 `a``b`包括 `a``b`的所有值的区间`a` 的值不能超过 `b`
闭区间运算符在迭代一个区间的所有值时是非常有用的,如在`for-in`循环中:
闭区间运算符在迭代一个区间的所有值时是非常有用的,如在 `for-in` 循环中:
```swift
for index in 1...5 {
@ -368,14 +350,14 @@ for index in 1...5 {
// 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
let names = ["Anna", "Alex", "Brian", "Jack"]
@ -389,10 +371,10 @@ for i in 0..<count {
// 第 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>
## 逻辑运算
## 逻辑运算Logical Operators
逻辑运算的操作对象是逻辑布尔值。Swift 支持基于 C 语言的三个标准逻辑运算。
@ -402,9 +384,9 @@ for i in 0..<count {
### 逻辑非
逻辑非运算(`!a`)对一个布尔值取反,使得`true``false``false``true`
逻辑非运算(`!a`)对一个布尔值取反,使得 `true``false``false``true`
它是一个前置运算符,需紧跟在操作数之前,且不加空格。读作`非 a`,例子如下:
它是一个前置运算符,需紧跟在操作数之前,且不加空格。读作 `非 a` ,例子如下:
```swift
let allowedEntry = false
@ -414,17 +396,17 @@ if !allowedEntry {
// 输出 "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
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
let hasDoorKey = false
@ -469,14 +451,14 @@ if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
// 输出 "Welcome!"
```
这个例子使用了含多个`&&``||`的复合逻辑。但无论怎样,`&&``||`始终只能操作两个值。所以这实际是三个简单逻辑连续操作的结果。我们来解读一下:
这个例子使用了含多个 `&&``||` 的复合逻辑。但无论怎样,`&&``||` 始终只能操作两个值。所以这实际是三个简单逻辑连续操作的结果。我们来解读一下:
如果我们输入了正确的密码并通过了视网膜扫描,或者我们有一把有效的钥匙,又或者我们知道紧急情况下重置的密码,我们就能把门打开进入。
前两种情况,我们都不满足,所以前两个简单逻辑的结果是`false`,但是我们是知道紧急情况下重置的密码的,所以整个复杂表达式的值还是`true`
前两种情况,我们都不满足,所以前两个简单逻辑的结果是 `false`,但是我们是知道紧急情况下重置的密码的,所以整个复杂表达式的值还是 `true`
>注意:
Swift 逻辑操作符`&&``||`是左结合的,这意味着拥有多元逻辑操作符的复合表达式优先计算最左边的子表达式。
> 注意:
Swift 逻辑操作符 `&&``||` 是左结合的,这意味着拥有多元逻辑操作符的复合表达式优先计算最左边的子表达式。
### 使用括号来明确优先级

View File

@ -10,7 +10,15 @@
> 2.1
> 翻译:[DianQK](https://github.com/DianQK)
> 校对:[shanks](http://codebuild.me)
> 校对:[shanks](http://codebuild.me), [Realank](https://github.com/Realank),
> 2.2
> 校对:[SketchK](https://github.com/SketchK)
> 3.0
> 校对:[CMB](https://github.com/chenmingbiao)
>
> 版本日期2016-09-13
本页包含内容:
@ -27,11 +35,12 @@
- [比较字符串](#comparing_strings)
- [字符串的 Unicode 表示形式](#unicode_representations_of_strings)
`String`是例如"hello, world""albatross"这样的有序的`Character`(字符)类型的值的集合。通过`String`类型来表示。
一个`String`的内容可以用变量的方式读取,它包括一个`Character`值的集合。
一个`String`的内容可以用许多方式读取,它包括一个`Character`值的集合。
创建和操作字符串的语法与 C 语言中字符串操作相似,轻量并且易读。
字符串连接操作只需要简单地通过`+`符号将两个字符串相连即可。与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。你也可以在字符串内插过程中使用字符串插入常量、变量、字面量表达成更长的字符串,这样可以很容易的创建自定义的字符串值,进行展示、存储以及打印。
字符串连接操作只需要简单地通过`+`符号将两个字符串相连即可。与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。你也可以在字符串内插过程中使用字符串插入常量、变量、字面量表达成更长的字符串,这样可以很容易的创建自定义的字符串值,进行展示、存储以及打印。
尽管语法简易,但`String`类型是一种快速、现代化的字符串实现。
每一个字符串都是由编码无关的 Unicode 字符组成,并支持访问字符的多种 Unicode 表示形式representations
@ -40,7 +49,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)*。
<a name="string_literals"></a>
## 字符串字面量String Literals
@ -68,7 +76,7 @@ var anotherEmptyString = String() // 初始化方法
// 两个字符串均为空并等价。
```
您可以通过检查其`Boolean`类型的`isEmpty`属性来判断该字符串是否为空:
您可以通过检查其`Bool`类型的`isEmpty`属性来判断该字符串是否为空:
```swift
if emptyString.isEmpty {
@ -77,6 +85,7 @@ if emptyString.isEmpty {
// 打印输出:"Nothing to see here"
```
<a name="string_mutability"></a>
## 字符串可变性 (String Mutability)
@ -95,6 +104,7 @@ constantString += " and another Highlander"
> 注意:
在 Objective-C 和 Cocoa 中,您需要通过选择两个不同的类(`NSString``NSMutableString`)来指定字符串是否可以被修改。
<a name="strings_are_value_types"></a>
## 字符串是值类型Strings Are Value Types
@ -109,6 +119,7 @@ Swift 默认字符串拷贝的方式保证了在函数/方法中传递的是字
在实际编译时Swift 编译器会优化字符串的使用,使实际的复制只发生在绝对必要的情况下,这意味着您将字符串作为值类型的同时可以获得极高的性能。
<a name="working_with_characters"></a>
## 使用字符Working with Characters
@ -141,6 +152,7 @@ print(catString)
// 打印输出:"Cat!🐱"
```
<a name="concatenating_strings_and_characters"></a>
## 连接字符串和字符 (Concatenating Strings and Characters)
@ -203,6 +215,7 @@ Unicode 是一个国际标准,用于文本的编码和表示。
它使您可以用标准格式表示来自任意语言几乎所有的字符,并能够对文本文件或网页这样的外部资源中的字符进行读写操作。
Swift 的`String``Character`类型是完全兼容 Unicode 标准的。
<a name="unicode_scalars"></a>
### Unicode 标量Unicode Scalars
@ -248,7 +261,7 @@ let sparklingHeart = "\u{1F496}" // 💖, Unicode 标量 U+1F496
```swift
let eAcute: Character = "\u{E9}" // é
let combinedEAcute: Character = "\u{65}\u{301}" // e 后面加上 ́
// eAcute 是 é, combinedEAcute 是
// eAcute 是 é, combinedEAcute 是 é
```
可扩展的字符群集是一个灵活的方法,用许多复杂的脚本字符表示单一的`Character`值。
@ -259,7 +272,7 @@ let combinedEAcute: Character = "\u{65}\u{301}" // e 后面加上 ́
```swift
let precomposed: Character = "\u{D55C}" // 한
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ, ᅡ, ᆫ
// precomposed 是 한, decomposed 是 한
// precomposed 是 한, decomposed 是
```
可拓展的字符群集可以使包围记号(例如`COMBINING ENCLOSING CIRCLE`或者`U+20DD`)的标量包围其他 Unicode 标量,作为一个单一的`Character`值:
@ -269,7 +282,7 @@ let enclosedEAcute: Character = "\u{E9}\u{20DD}"
// enclosedEAcute 是 é⃝
```
局部的指示符号的 Unicode 标量可以组合成一个单一的`Character`值,例如`REGIONAL INDICATOR SYMBOL LETTER U`(`U+1F1FA`)和`REGIONAL INDICATOR SYMBOL LETTER S`(`U+1F1F8`)
地域性指示符号的 Unicode 标量可以组合成一个单一的`Character`值,例如`REGIONAL INDICATOR SYMBOL LETTER U`(`U+1F1FA`)和`REGIONAL INDICATOR SYMBOL LETTER S`(`U+1F1F8`)
```swift
@ -290,7 +303,7 @@ print("unusualMenagerie has \(unusualMenagerie.characters.count) characters")
注意在 Swift 中,使用可拓展的字符群集作为`Character`值来连接或改变字符串时,并不一定会更改字符串的字符数量。
例如,如果你用四个字符的单词`cafe`初始化一个新的字符串,然后添加一个`COMBINING ACTUE ACCENT`(`U+0301`)作为字符串的结尾。最终这个字符串的字符数量仍然是`4`,因为第四个字符是``,而不是`e`
例如,如果你用四个字符的单词`cafe`初始化一个新的字符串,然后添加一个`COMBINING ACTUE ACCENT`(`U+0301`)作为字符串的结尾。最终这个字符串的字符数量仍然是`4`,因为第四个字符是`é`,而不是`e`
```swift
var word = "cafe"
@ -312,7 +325,7 @@ print("the number of characters in \(word) is \(word.characters.count)")
<a name="accessing_and_modifying_a_string"></a>
## 访问和修改字符串 (Accessing and Modifying a String)
你可以通字符串的属性和方法来访问和读取它,当然也可以用下标语法完成。
你可以通字符串的属性和方法来访问和修改它,当然也可以用下标语法完成。
<a name="string_indices"></a>
### 字符串索引 (String Indices)
@ -323,72 +336,70 @@ print("the number of characters in \(word) is \(word.characters.count)")
使用`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
let greeting = "Guten Tag!"
greeting[greeting.startIndex]
// G
greeting[greeting.endIndex.predecessor()]
greeting[greeting.index(before: greeting.endIndex)]
// !
greeting[greeting.startIndex.successor()]
greeting[greeting.index(after: greeting.startIndex)]
// u
let index = greeting.startIndex.advancedBy(7)
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index]
// a
```
试图获取越界索引对应的`Character`,将引发一个运行时错误。
试图获取越界索引对应的 `Character`,将引发一个运行时错误。
```swift
greeting[greeting.endIndex] // error
greeting.endIndex.successor() // error
greeting.index(after: endIndex) // error
```
使用`characters`属性的`indices`属性会创建一个包含全部索引的范围(`Range`),用来在一个字符串中访问单个字符。
使用 `characters` 属性的 `indices` 属性会创建一个包含全部索引的范围(`Range`),用来在一个字符串中访问单个字符。
```swift
for index in greeting.characters.indices {
print("\(greeting[index]) ", terminator: " ")
print("\(greeting[index]) ", terminator: "")
}
// 打印输出 "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>
### 插入和删除 (Inserting and Removing)
调用`insert(_:atIndex:)`方法可以在一个字符串的指定索引插入一个字符。
调用 `insert(_:atIndex:)` 方法可以在一个字符串的指定索引插入一个字符,调用 `insert(contentsOf:at:)` 方法可以在一个字符串的指定索引插入一个段字符串
```swift
var welcome = "hello"
welcome.insert("!", atIndex: welcome.endIndex)
// welcome now 现在等于 "hello!"
welcome.insert("!", at: welcome.endIndex)
// welcome 变量现在等于 "hello!"
welcome.insert(contentsOf:" there".characters, at: welcome.index(before: welcome.endIndex))
// welcome 变量现在等于 "hello there!"
```
调用`insertContentsOf(_:at:)`方法可以在一个字符串的指定索引插入一个字符串。
调用 `remove(at:)` 方法可以在一个字符串的指定索引删除一个字符,调用 `removeSubrange(_:)` 方法可以在一个字符串的指定索引删除一个字符串。
```swift
welcome.insertContentsOf(" there".characters, at: welcome.endIndex.predecessor())
// welcome 现在等于 "hello there!"
```
调用`removeAtIndex(_:)`方法可以在一个字符串的指定索引删除一个字符。
```swift
welcome.removeAtIndex(welcome.endIndex.predecessor())
welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome 现在等于 "hello there"
```
调用`removeRange(_:)`方法可以在一个字符串的指定索引删除一个子字符串。
```swift
let range = welcome.endIndex.advancedBy(-6)..<welcome.endIndex
welcome.removeRange(range)
let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
welcome.removeSubrange(range)
// welcome 现在等于 "hello"
```
> 注意:
> 您可以使用 `insert(_:at:)`、`insert(contentsOf:at:)`、`remove(at:)` 和 `removeSubrange(_:)` 方法在任意一个确认的并遵循 `RangeReplaceableCollection` 协议的类型里面,如上文所示是使用在 `String` 中,您也可以使用在 `Array`、`Dictionary` 和 `Set` 中。
<a name="comparing_strings"></a>
## 比较字符串 (Comparing Strings)
@ -417,7 +428,7 @@ if quotation == sameQuotation {
// "Voulez-vous un café?" 使用 LATIN SMALL LETTER E WITH ACUTE
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}?"
if eAcuteQuestion == combinedEAcuteQuestion {
@ -440,7 +451,7 @@ if latinCapitalLetterA != cyrillicCapitalLetterA {
```
> 注意:
> 在 Swift 中,字符串和字符并不区分区域
> 在 Swift 中,字符串和字符并不区分地域(not locale-sensitive)
<a name="prefix_and_suffix_equality"></a>
@ -472,7 +483,7 @@ let romeoAndJuliet = [
var act1SceneCount = 0
for scene in romeoAndJuliet {
if scene.hasPrefix("Act 1 ") {
++act1SceneCount
act1SceneCount += 1
}
}
print("There are \(act1SceneCount) scenes in Act 1")
@ -486,9 +497,9 @@ var mansionCount = 0
var cellCount = 0
for scene in romeoAndJuliet {
if scene.hasSuffix("Capulet's mansion") {
++mansionCount
mansionCount += 1
} else if scene.hasSuffix("Friar Lawrence's cell") {
++cellCount
cellCount += 1
}
}
print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
@ -502,7 +513,7 @@ print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
<a name="unicode_representations_of_strings"></a>
## 字符串的 Unicode 表示形式Unicode Representations of Strings
当一个 Unicode 字符串被写进文本文件或者其他储存时,字符串中的 Unicode 标量会用 Unicode 定义的几种编码格式编码。每一个字符串中的小块编码都被称代码单元。这些包括 UTF-8 编码格式编码字符串为8位的代码单元 UTF-16 编码格式编码字符串位16位的代码单元以及 UTF-32 编码格式编码字符串32位的代码单元
当一个 Unicode 字符串被写进文本文件或者其他储存时,字符串中的 Unicode 标量会用 Unicode 定义的几种`编码格式`encoding forms编码。每一个字符串中的小块编码都被称`代码单元`code units。这些包括 UTF-8 编码格式编码字符串为8位的代码单元 UTF-16 编码格式编码字符串位16位的代码单元以及 UTF-32 编码格式编码字符串32位的代码单元
Swift 提供了几种不同的方式来访问字符串的 Unicode 表示形式。
您可以利用`for-in`来对字符串进行遍历,从而以 Unicode 可扩展的字符群集的方式访问每一个`Character`值。
@ -514,13 +525,12 @@ Swift 提供了几种不同的方式来访问字符串的 Unicode 表示形式
* UTF-16 代码单元集合 (利用字符串的`utf16`属性进行访问)
* 21位的 Unicode 标量值集合,也就是字符串的 UTF-32 编码格式 (利用字符串的`unicodeScalars`属性进行访问)
下面由`D``o``g``‼`(`DOUBLE EXCLAMATION MARK`, Unicode 标量 `U+203C`)和`<EFBFBD>`(`DOG FACE`Unicode 标量为`U+1F436`)组成的字符串中的每一个字符代表着一种不同的表示:
下面由`D`,`o`,`g`,`‼`(`DOUBLE EXCLAMATION MARK`, Unicode 标量 `U+203C`)和`🐶`(`DOG FACE`Unicode 标量为`U+1F436`)组成的字符串中的每一个字符代表着一种不同的表示:
```swift
let dogString = "Dog‼🐶"
```
<a name="UTF-8_representation"></a>
### UTF-8 表示
@ -633,7 +643,7 @@ print("")
### Unicode 标量表示 (Unicode Scalars Representation)
您可以通过遍历`String`值的`unicodeScalars`属性来访问它的 Unicode 标量表示。
其为`UnicodeScalarView`类型的属性,`UnicodeScalarView``UnicodeScalar`的集合。
其为`UnicodeScalarView`类型的属性,`UnicodeScalarView``UnicodeScalar`类型的值的集合。
`UnicodeScalar`是21位的 Unicode 代码点。
每一个`UnicodeScalar`拥有一个`value`属性可以返回对应的21位数值`UInt32`来表示:
@ -649,7 +659,7 @@ print("")
<td>🐶<br>U+1F436</td>
</tr>
<tr height="77">
<td height="77">UTF-16<br>Code Unit</td>
<td height="77">Unicode Scalar<br>Code Unit</td>
<td>68</td>
<td>111</td>
<td>103</td>

View File

@ -9,7 +9,11 @@
> 翻译+校对:[JackAlan](https://github.com/AlanMelody)
> 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
var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5)
var anotherThreeDoubles = [Double](count: 3, repeatedValue: 2.5)
// anotherThreeDoubles 被推断为 [Double],等价于 [2.5, 2.5, 2.5]
var sixDoubles = threeDoubles + anotherThreeDoubles
@ -223,7 +227,7 @@ firstItem = shoppingList[0]
```swift
let apples = shoppingList.removeLast()
// 数组的最后一项被移除了
// shoppingList 现在只有5项不包括 cheese
// shoppingList 现在只有5项不包括 Apples
// apples 常量的值现在等于 "Apples" 字符串
```
@ -406,7 +410,7 @@ for genre in favoriteGenres {
更多关于`for-in`循环的信息,参见[For 循环](./05_Control_Flow.html#for_loops)。
Swift 的`Set`类型没有确定的顺序,为了按照特定顺序来遍历一个`Set`中的值可以使用`sort()`方法,它将根据提供的序列返回一个有序集合.
Swift 的`Set`类型没有确定的顺序,为了按照特定顺序来遍历一个`Set`中的值可以使用`sort()`方法,它将返回一个有序数组,这个数组的元素排列顺序由操作符'<'对元素进行比较的结果来确定.
```swift
for genre in favoriteGenres.sort() {
@ -460,7 +464,7 @@ oddDigits.exclusiveOr(singleDigitPrimeNumbers).sort()
* 使用`isSubsetOf(_:)`方法来判断一个集合中的值是否也被包含在另外一个集合中。
* 使用`isSupersetOf(_:)`方法来判断一个集合中包含另一个集合中所有的值。
* 使用`isStrictSubsetOf(_:)`或者`isStrictSupersetOf(_:)`方法来判断一个集合是否是另外一个集合的子集合或者父集合并且两个集合并不相等。
* 使用`isDisjointWith(_:)`方法来判断两个集合是否不含有相同的值。
* 使用`isDisjointWith(_:)`方法来判断两个集合是否不含有相同的值(是否没有交集)
```swift
let houseAnimals: Set = ["🐶", "🐱"]

View File

@ -12,33 +12,33 @@
> 翻译:[Prayer](https://github.com/futantan)
> 校对:[shanks](http://codebuild.me)
> 2.2
> 翻译:[LinusLing](https://github.com/linusling)
> 校对:[SketchK](https://github.com/SketchK)
> 3.0
> 翻译:[Realank](https://github.com/realank) 2016-09-13
本页包含内容:
- [For 循环](#for_loops)
- [For-In 循环](#for_in_loops)
- [While 循环](#while_loops)
- [条件语句](#conditional_statement)
- [控制转移语句Control Transfer Statements](#control_transfer_statements)
- [提前退出](#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 还可以匹配很多不同的模式,包括间匹配(interval match元组tuple转换到特定类型。`switch`语句的 case 中匹配的值可以绑定成临时常量或变量在case体内使用,也可以`where`描述更复杂的匹配条件。
<a name="for_loops"></a>
## For 循环
<a name="for_in_loops"></a>
## For-In 循环
Swift 提供两种`for`循环形式以来按照指定的次数多次执行一系列语句:
* `for-in`循环对一个集合里面的每个元素执行一系列语句。
* for 循环,用来重复执行一系列语句直到达成特定条件达成,一般通过在每次循环完成后增加计数器的值来实现。
<a name="for_in"></a>
### For-In
你可以使用`for-in`循环来遍历一个集合里面的所有元素,例如由数字表示的区间、数组中的元素、字符串中的字符。
你可以使用`for-in`循环来遍历一个集合中的所有元素,例如数字范围、数组中的元素或者字符串中的字符。
下面的例子用来输出乘 5 乘法表前面一部分内容:
@ -53,11 +53,11 @@ for index in 1...5 {
// 5 times 5 is 25
```
例子中用来进行遍历的元素是一组使用闭区间操作符(`...`)表示的从`1``5`的数字。`index`被赋值为闭区间中的第一个数字(`1`),然后循环中的语句被执行一次。在本例中,这个循环只包含一个语句,用来输出当前`index`值所对应的乘 5 乘法表结果。该语句执行后,`index`的值被更新为闭区间中的第二个数字(`2`),之后`print(_:separator:terminator:)`函数会再执行一次。整个过程会进行到闭区间结尾为止。
例子中用来进行遍历的元素是使用闭区间操作符(`...`)表示的从`1``5`的数字区间`index`被赋值为闭区间中的第一个数字(`1`),然后循环中的语句被执行一次。在本例中,这个循环只包含一个语句,用来输出当前`index`值所对应的乘 5 乘法表结果。该语句执行后,`index`的值被更新为闭区间中的第二个数字(`2`),之后`print(_:separator:terminator:)`函数会再执行一次。整个过程会进行到闭区间结尾为止。
上面的例子中,`index`是一个每次循环遍历开始时被自动赋值的常量。这种情况下,`index`在使用前不需要声明,只需要将它包含在循环的声明中,就可以对其进行隐式声明,而无需使用`let`关键字声明。
如果你不需要知道区间序列内每一项的值,你可以使用下划线(`_`)替代变量名来忽略对值的访问
如果你不需要区间序列内每一项的值,你可以使用下划线(`_`)替代变量名来忽略这个值
```swift
let base = 3
@ -70,7 +70,7 @@ print("\(base) to the power of \(power) is \(answer)")
// 输出 "3 to the power of 10 is 59049"
```
这个例子计算 base 这个数的 power 次幂(本例中,是`3``10`次幂),从`1``3``0`次幂)开始做`3`的乘法, 进行`10`次,使用`1``10`的闭区间循环。这个计算并不需要知道每一次循环中计数器具体的值,只需要执行了正确的循环次数即可。下划线符号`_`(替代循环中的变量)能够忽略具体的值,并且不提供循环遍历时对值的访问。
这个例子计算 base 这个数的 power 次幂(本例中,是`3``10`次幂),从`1``3``0`次幂)开始做`3`的乘法, 进行`10`次,使用`1``10`的闭区间循环。这个计算并不需要知道每一次循环中计数器具体的值,只需要执行了正确的循环次数即可。下划线符号`_`(替代循环中的变量)能够忽略当前值,并且不提供循环遍历时对值的访问。
使用`for-in`遍历一个数组所有元素:
@ -99,57 +99,10 @@ for (animalName, legCount) in numberOfLegs {
字典元素的遍历顺序和插入顺序可能不同,字典的内容在内部是无序的,所以遍历元素时不能保证顺序。关于数组和字典,详情参见[集合类型](./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>
## While 循环
`while`循环运行一系列语句直到条件变成`false`。这类循环适合使用在第一次迭代前迭代次数未知的情况下。Swift 提供两种`while`循环形式:
`while`循环会一直运行一语句直到条件变成`false`。这类循环适合使用在第一次迭代前迭代次数未知的情况下。Swift 提供两种`while`循环形式:
* `while`循环,每次在循环开始时计算条件是否符合;
* `repeat-while`循环,每次在循环结束时计算条件是否符合。
@ -157,11 +110,11 @@ print("The loop statements were executed \(index) times")
<a name="while"></a>
###While
`while`循环从计算一条件开始。如果条件为`true`,会重复运行一系列语句,直到条件变为`false`
`while`循环从计算一条件开始。如果条件为`true`,会重复运行一语句,直到条件变为`false`
下面是一般情况下 `while` 循环格式:
下面是 `while` 循环的一般格式:
```swift
```
while condition {
statements
}
@ -169,12 +122,12 @@ 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)
游戏的规则如下:
* 游戏盘面包括 25 个方格,游戏目标是达到或者超过第 25 个方格;
* 每一轮,你通过掷一个 6 边的骰子来确定你移动方块的步数,移动的路线由上图中横向的虚线所示;
* 每一轮,你通过掷一个六面体骰子来确定你移动方块的步数,移动的路线由上图中横向的虚线所示;
* 如果在某轮结束,你移动到了梯子的底部,可以顺着梯子爬上去;
* 如果在某轮结束,你移动到了蛇的头部,你会顺着蛇的身体滑下去。
@ -182,26 +135,27 @@ while condition {
```swift
let finalSquare = 25
var board = [Int](count: finalSquare + 1, repeatedValue: 0)
var board = [Int](repeating: 0, count: finalSquare + 1)
```
一些方被设置成有蛇或者梯子的指定值。梯子底部的方是一个正值,使你可以向上移动,蛇头处的方是一个负值,会让你向下移动:
一些方被设置成特定的值来表示有蛇或者梯子。梯子底部的方是一个正值,使你可以向上移动,蛇头处的方是一个负值,会让你向下移动:
```swift
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
```
3 号方是梯子的底部,会让你向上移动到 11 号方格,我们使用`board[03]`等于`+08`(来表示`11``3`之间的差值)。使用一元运算符(`+i`)是为了和一元运算符(`-i`)对称,为了让盘面代码整齐,小于 10 的数字都使用 0 补齐(这些风格上的调整都不是必须的,只是为了让代码看起来更加整洁)。
3 号方是梯子的底部,会让你向上移动到 11 号方格,我们使用`board[03]`等于`+08`(来表示`11``3`之间的差值)。使用一元运算符(`+i`)是为了和一元运算符(`-i`)对称,为了让盘面代码整齐,小于 10 的数字都使用 0 补齐(这些风格上的调整都不是必须的,只是为了让代码看起来更加整洁)。
玩家由左下角编号为 0 的方格开始游戏。一般来说玩家第一次掷骰子后才会进入游戏盘面:
玩家由左下角空白处编号为 0 的方格开始游戏。玩家第一次掷骰子后才会进入游戏盘面:
```swift
var square = 0
var diceRoll = 0
while square < finalSquare {
// 掷骰子
if ++diceRoll == 7 { diceRoll = 1 }
diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
// 根据点数移动
square += diceRoll
if square < board.count {
@ -212,26 +166,27 @@ while square < finalSquare {
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` `2``3``4``5``6``1``2`
掷完骰子后,玩家向前移动`diceRoll`个方格,如果玩家移动超过了第 25 个方格,这个时候游戏结束,相应地,代码会在`square`增加`board[square]`的值向前或向后移动(遇到了梯子或者蛇)之前,检测`square`的值是否小于`board``count`属性
掷完骰子后,玩家向前移动`diceRoll`个方格,如果玩家移动超过了第 25 个方格,这个时候游戏将会结束,为了应对这种情况,代码会首先判断`square`的值是否小于`board``count`属性,只有小于才会在`board[square]`上增加`square`,来向前或向后移动(遇到了梯子或者蛇)。
如果没有这个检测(`square < board.count``board[square]`可能会越界访问`board`数组,导致错误。例如如果`square`等于`26` 代码会去尝试访问`board[26]`,超过数组的长度。
> 注意:
> 如果没有这个检测(`square < board.count``board[square]`可能会越界访问`board`数组,导致错误。如果`square`等于`26` 代码会去尝试访问`board[26]`,超过数组的长度。
当本轮`while`循环运行完毕,会再检测循环条件是否需要再运行一次循环。如果玩家移动到或者超过第 25 个方格,循环条件结果为`false`,此时游戏结束。
`while` 循环比较适合本例中的这种情况,因为在 `while` 循环开始时,我们并不知道游戏的长度或者循环的次数,只有在达成指定条件时循环才会结束。
`while` 循环比较适合本例中的这种情况,因为在 `while` 循环开始时,我们并不知道游戏要跑多久,只有在达成指定条件时循环才会结束。
<a name="repeat_while"></a>
###Repeat-While
`while`循环的另外一种形式是`repeat-while`,它和`while`的区别是在判断循环条件之前,先执行一次循环的代码块然后重复循环直到条件为`false`
`while`循环的另外一种形式是`repeat-while`,它和`while`的区别是在判断循环条件之前,先执行一次循环的代码块然后重复循环直到条件为`false`
> 注意:
> Swift语言的`repeat-while`循环其他语言中的`do-while`循环是类似的。
> Swift语言的`repeat-while`循环其他语言中的`do-while`循环是类似的。
下面是一般情况下 `repeat-while`循环的格式:
下面是 `repeat-while`循环的一般格式:
```swift
repeat {
@ -239,11 +194,11 @@ repeat {
} while condition
```
还是蛇和梯子的游戏,使用`repeat-while`循环来替代`while`循环。`finalSquare``board``square``diceRoll`的值初始化同`while`循环一样:
还是蛇和梯子的游戏,使用`repeat-while`循环来替代`while`循环。`finalSquare``board``square``diceRoll`的值初始化同`while`循环一样:
``` swift
let finalSquare = 25
var board = [Int](count: finalSquare + 1, repeatedValue: 0)
var board = [Int](repeating: 0, count: finalSquare + 1)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
var square = 0
@ -259,7 +214,8 @@ repeat {
// 顺着梯子爬上去或者顺着蛇滑下去
square += board[square]
// 掷骰子
if ++diceRoll == 7 { diceRoll = 1 }
diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
// 根据点数移动
square += diceRoll
} while square < finalSquare
@ -273,14 +229,14 @@ print("Game over!")
<a name="conditional_statement"></a>
## 条件语句
根据特定的条件执行特定的代码通常是十分有用的,例如:当错误发生时,你可能想运行额外的代码;或者,当输入的值太大或太小时,向用户显示一条消息。要实现这些功能,你就需要使用*条件语句*。
根据特定的条件执行特定的代码通常是十分有用的当错误发生时,你可能想运行额外的代码;或者,当值太大或太小时,向用户显示一条消息。要实现这些功能,你就需要使用*条件语句*。
Swift 提供两种类型的条件语句:`if`语句和`switch`语句。通常,当条件较为简单且可能的情况很少时,使用`if`语句。而`switch`语句更适用于条件较复杂、可能情况较多且需要用到模式匹配pattern-matching的情
Swift 提供两种类型的条件语句:`if`语句和`switch`语句。通常,当条件较为简单且可能的情况很少时,使用`if`语句。而`switch`语句更适用于条件较复杂、有更多排列组合的时候。并且`switch`在需要用到模式匹配pattern-matching的情况下会更有用
<a name="if"></a>
### If
`if`语句最简单的形式就是只包含一个条件,当且仅当该条件为`true`时,才执行相关代码:
`if`语句最简单的形式就是只包含一个条件,只有该条件为`true`时,才执行相关代码:
```swift
var temperatureInFahrenheit = 30
@ -292,7 +248,7 @@ if temperatureInFahrenheit <= 32 {
上面的例子会判断温度是否小于等于 32 华氏度(水的冰点)。如果是,则打印一条消息;否则,不打印任何消息,继续执行`if`块后面的代码。
当然,`if`语句允许二选一也就是当条件为`false`时,执行 *else 语句*
当然,`if`语句允许二选一执行,叫做`else`从句。也就是当条件为`false`时,执行 *else 语句*
```swift
temperatureInFahrenheit = 40
@ -304,9 +260,9 @@ if temperatureInFahrenheit <= 32 {
// 输出 "It's not that cold. Wear a t-shirt."
```
显然,这两条分支中总有一条会被执行。由于温度已升至 40 华氏度,不算太冷,没必要再围围巾——因此,`else`分支就被触发了。
显然,这两条分支中总有一条会被执行。由于温度已升至 40 华氏度,不算太冷,没必要再围围巾因此,`else`分支就被触发了。
你可以把多个`if`语句链接在一起,像下面这样
你可以把多个`if`语句链接在一起,来实现更多分支
```swift
temperatureInFahrenheit = 90
@ -322,7 +278,7 @@ if temperatureInFahrenheit <= 32 {
在上面的例子中,额外的`if`语句用于判断是不是特别热。而最后的`else`语句被保留了下来,用于打印既不冷也不热时的消息。
实际上,最后的`else`语句是可选的:
实际上,当不需要完整判断情况的时候,最后的`else`语句是可选的:
```swift
temperatureInFahrenheit = 72
@ -346,43 +302,45 @@ if temperatureInFahrenheit <= 32 {
switch some value to consider {
case value 1:
respond to value 1
case value 2, value 3:
case value 2,
value 3:
respond to value 2 or 3
default:
otherwise, do something else
}
```
`switch`语句都由*多个 case* 构成。为了匹配某些更特定的值Swift 提供了几种更复杂的匹配模式,这些模式将在本节的稍后部分提到。
每一个 case 都是代码执行的一条分支,这与`if`语句类似。与之不同的是,`switch`语句会决定哪一条分支应该被执行
`switch`语句由*多个 case* 构成,每个由`case`关键字开始。为了匹配某些更特定的值Swift 提供了几种方法来进行更复杂的模式匹配,这些模式将在本节的稍后部分提到
`switch`语句必须是完备的。这就是说,每一个可能的值都必须至少有一个 case 分支与之对应。在某些不可能涵盖所有值的情况下,你可以使用默认(`default`)分支满足该要求,这个默认分支必须在`switch`语句的最后面
`if`语句类似,每一个 case 都是代码执行的一条分支。`switch`语句会决定哪一条分支应该被执行,这个流程被称作根据给定的值*切换(switching)*
`switch`语句必须是完备的。这就是说,每一个可能的值都必须至少有一个 case 分支与之对应。在某些不可能涵盖所有值的情况下,你可以使用默认(`default`)分支来涵盖其它所有没有对应的值,这个默认分支必须在`switch`语句的最后面。
下面的例子使用`switch`语句来匹配一个名为`someCharacter`的小写字符:
```swift
let someCharacter: Character = "e"
let someCharacter: Character = "z"
switch someCharacter {
case "a", "e", "i", "o", "u":
print("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
print("\(someCharacter) is a consonant")
case "a":
print("The first letter of the alphabet")
case "z":
print("The last letter of the alphabet")
default:
print("\(someCharacter) is not a vowel or a consonant")
print("Some other character")
}
// 输出 "e is a vowel"
// Prints "The last letter of the alphabet"
```
在这个例子中,第一个 case 分支用于匹配五个元音,第二个 case 分支用于匹配所有的辅音
在这个例子中,第一个 case 分支用于匹配第一个英文字母`a`,第二个 case 分支用于匹配最后一个字母`z`
因为`switch`语句必须有一个case分支用于覆盖所有可能的字符而不仅仅是所有的英文字母所以switch语句使用`default`分支来匹配除了`a`和`z`外的所有值这个分支保证了swith语句的完备性。
由于为其它可能的字符写 case 分支没有实际的意义,因此在这个例子中使用了默认分支来处理剩下的既不是元音也不是辅音的字符——这就保证了`switch`语句的完备性。
<a name="no_implicit_fallthrough"></a>
#### 不存在隐式的贯穿No Implicit Fallthrough
与 C 语言和 Objective-C 中的`switch`语句不同,在 Swift 中,当匹配的 case 分支中的代码执行完毕后,程序会终止`switch`语句,而不会继续执行下一个 case 分支。这也就是说,不需要在 case 分支中显式地使用`break`语句。这使得`switch`语句更安全、更易用,也避免了因忘记写`break`语句而产生的错误。
与 C 和 Objective-C 中的`switch`语句不同,在 Swift 中,当匹配的 case 分支中的代码执行完毕后,程序会终止`switch`语句,而不会继续执行下一个 case 分支。这也就是说,不需要在 case 分支中显式地使用`break`语句。这使得`switch`语句更安全、更易用,也避免了因忘记写`break`语句而产生的错误。
> 注意:
虽然在Swift中`break`不是必须的,但你依然可以在 case 分支中的代码执行完毕前使用`break`跳出,详情请参见[Switch 语句中的 break](#break_in_a_switch_statement)。
@ -392,28 +350,32 @@ default:
```swift
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a":
case "a": // Invalid, the case has an empty body
case "A":
print("The letter A")
default:
print("Not the letter A")
}
// this will report a compile-time error
// This will report a compile-time error.
```
不像 C 语言里的`switch`语句,在 Swift 中,`switch`语句不会同时匹配`"a"`和`"A"`。相反的,上面的代码会引起编译期错误:`case "a": does not contain any executable statements`——这就避免了意外地从一个 case 分支贯穿到另外一个,使得代码更安全、也更直观。
一个 case 也可以包含多个模式,用逗号把它们分开(如果太长了也可以分行写):
不像 C 语言里的`switch`语句,在 Swift 中,`switch`语句不会一起匹配`"a"`和`"A"`。相反的,上面的代码会引起编译期错误:`case "a": 不包含任何可执行语句`——这就避免了意外地从一个 case 分支贯穿到另外一个,使得代码更安全、也更直观。
为了让单个case同时匹配`a`和`A`,可以将这个两个值组合成一个复合匹配,并且用逗号分开:
```swift
switch some value to consider {
case value 1, value 2:
statements
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
print("The letter A")
default:
print("Not the letter A")
}
// Prints "The letter A
```
为了可读性,符合匹配可以写成多行形式,详情请参考[复合匹配Compound Cases](#compound_cases)
> 注意:
如果想要贯穿至特定的 case 分支,请使用`fallthrough`语句,详情请参考[贯穿Fallthrough](#fallthrough)。
如果想要显式贯穿case分支请使用`fallthrough`语句,详情请参考[贯穿Fallthrough](#fallthrough)。
<a name="interval_matching"></a>
#### 区间匹配
@ -442,10 +404,9 @@ print("There are \(naturalCount) \(countedThings).")
// 输出 "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`语句遍历它。
<a name="tuples"></a>
#### 元组Tuple
@ -471,17 +432,17 @@ default:
// 输出 "(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的蓝色矩形里,或者在这个矩形外面。
不像 C 语言Swift 允许多个 case 匹配同一个值。实际上,在这个例子中,点(0, 0)可以匹配所有_四个 case_。但是如果存在多个匹配那么只会执行第一个被匹配到的 case 分支。考虑点(0, 0)会首先匹配`case (0, 0)`,因此剩下的能够匹配(0, 0)的 case 分支都会被忽视掉。
不像 C 语言Swift 允许多个 case 匹配同一个值。实际上,在这个例子中,点(0, 0)可以匹配所有_四个 case_。但是如果存在多个匹配那么只会执行第一个被匹配到的 case 分支。考虑点(0, 0)会首先匹配`case (0, 0)`,因此剩下的能够匹配分支都会被忽视掉。
<a name="value_bindings"></a>
#### 值绑定Value Bindings
case 分支的模式允许将匹配的值绑定到一个临时的常量或变量,这些常量或变量在该 case 分支里就可以被引用了——这种行为被称为*值绑定*value binding
case 分支允许将匹配的值绑定到一个临时的常量或变量,并且在case分支体内使用 —— 这种行为被称为*值绑定*value binding因为匹配的值在case分支体内与临时的常量或变量绑定
下面的例子展示了如何在一个`(Int, Int)`类型的元组中使用值绑定来分类下图中的点(x, y)
@ -498,18 +459,16 @@ case let (x, y):
// 输出 "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轴上或者不在坐标轴上。
这三个 case 都声明了常量`x`和`y`的占位符,用于临时获取元组`anotherPoint`的一个或两个值。第一个 case ——`case (let x, 0)`将匹配一个纵坐标为`0`的点,并把这个点的横坐标赋给临时的常量`x`。类似的,第二个 case ——`case (0, let y)`将匹配一个横坐标为`0`的点,并把这个点的纵坐标赋给临时的常量`y`。
一旦声明了这些临时的常量,它们就可以在其对应的 case 分支里用。在这个例子中,它们用于简化`print(_:separator:terminator:)`的书写
一旦声明了这些临时的常量,它们就可以在其对应的 case 分支里使用。在这个例子中,它们用于打印给定点的类型
请注意,这个`switch`语句不包含默认分支。这是因为最后一个 case ——`case let(x, y)`声明了一个可以匹配余下所有值的元组。这使得`switch`语句已经完备了,因此不需要再书写默认分支。
在上面的例子中,`x`和`y`是常量,这是因为没有必要在其对应的 case 分支中修改它们的值。然而,它们也可以是变量——程序将会创建临时变量,并用相应的值初始化它。修改这些变量只会影响其对应的 case 分支。
<a name="where"></a>
#### Where
@ -530,18 +489,52 @@ case let (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`上,或者不在对角线上。
这三个 case 都声明了常量`x`和`y`的占位符,用于临时获取元组`yetAnotherPoint`的两个值。这常量被用作`where`语句的一部分,从而创建一个动态的过滤器(filter)。当且仅当`where`语句的条件为`true`时,匹配到的 case 分支才会被执行。
这三个 case 都声明了常量`x`和`y`的占位符,用于临时获取元组`yetAnotherPoint`的两个值。这两个常量被用作`where`语句的一部分,从而创建一个动态的过滤器(filter)。当且仅当`where`语句的条件为`true`时,匹配到的 case 分支才会被执行。
就像是值绑定中的例子,由于最后一个 case 分支匹配了余下所有可能的值,`switch`语句就已经完备了,因此不需要再书写默认分支。
<a name="compound_cases"></a>
#### 复合匹配Compound Cases
当多个条件可以使用同一种方法来处理时可以将这几种可能放在同一个case后面并且用逗号隔开。当case后面的任意一种模式匹配的时候这条分支就会被匹配。并且如果匹配列表过长还可以分行书写
```swift
let someCharacter: Character = "e"
switch someCharacter {
case "a", "e", "i", "o", "u":
print("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
print("\(someCharacter) is a consonant")
default:
print("\(someCharacter) is not a vowel or a consonant")
}
```
这个switch语句中的第一个case匹配了英语中的五个小写原因字母。相似的第二个case匹配了英语中所有的小写辅音字母。最终default分支匹配了其它所有字符。
复合匹配同样可以包含值绑定。复合匹配里所有的匹配模式,都必须包含相同的值绑定。并且每一个绑定都必须获取到相同类型的值。这保证了,无论复合匹配中的哪个模式发生了匹配,分支体内的代码,都能获取到绑定的值,并且绑定的值都有一样的类型。
```swift
let stillAnotherPoint = (9, 0)
switch stillAnotherPoint {
case (let distance, 0), (0, let distance):
print("On an axis, \(distance) from the origin")
default:
print("Not on an axis")
}
```
上面的case有两个模式`(let distance, 0)`匹配了在x轴上的值`(0, let distance)`匹配了在y轴上的值。两个模式都绑定了`distance`,并且`distance`在两种模式下都是整型——这意味着分支体内的代码只要case匹配都可以获取到`distance`值
<a name="control_transfer_statements"></a>
## 控制转移语句Control Transfer Statements
控制转移语句改变你代码的执行顺序,通过它可以实现代码的跳转。Swift 有五种控制转移语句:
控制转移语句改变你代码的执行顺序通过它可以实现代码的跳转。Swift 有五种控制转移语句:
- `continue`
- `break`
@ -554,10 +547,7 @@ case let (x, y):
<a name="continue"></a>
### Continue
`continue`语句告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。就好像在说“本次循环迭代我已经执行完了”,但是并不会离开整个循环体。
> 注意:
> 在一个带有条件和递增的for循环体中调用`continue`语句后,迭代增量仍然会被计算求值。循环体继续像往常一样工作,仅仅只是循环体中的执行代码会被跳过。
`continue`语句告诉一个循环体立刻停止本次循环,重新开始下次循环。就好像在说“本次循环我已经执行完了”,但是并不会离开整个循环体。
下面的例子把一个小写字符串中的元音字母和空格字符移除,生成了一个含义模糊的短句:
@ -573,10 +563,10 @@ for character in puzzleInput.characters {
}
}
print(puzzleOutput)
// 输出 "grtmndsthnklk"
// 输出 "grtmndsthnklk"
```
在上面的代码中,只要匹配到元音字母或者空格字符,就调用`continue`语句,使本次循环迭代结束,新开始下次循环迭代。这种行为使`switch`匹配到元音字母和空格字符时不做处理,而不是让每一个匹配到的字符都被打印。
在上面的代码中,只要匹配到元音字母或者空格字符,就调用`continue`语句,使本次循环结束,新开始下次循环。这种行为使`switch`匹配到元音字母和空格字符时不做处理,而不是让每一个匹配到的字符都被打印。
<a name="break"></a>
### Break
@ -586,7 +576,7 @@ print(puzzleOutput)
<a name="break_in_a_loop_statement"></a>
#### 循环语句中的 break
当在一个循环体中使用`break`时,会立刻中断该循环体的执行,然后跳转到表示循环体结束的大括号(`}`)后的第一行代码。不会再有本次循环迭代的代码被执行,也不会再有下次的循环迭代产生。
当在一个循环体中使用`break`时,会立刻中断该循环体的执行,然后跳转到表示循环体结束的大括号(`}`)后的第一行代码。不会再有本次循环的代码被执行,也不会再有下次的循环产生。
<a name="break_in_a_switch_statement"></a>
#### Switch 语句中的 break
@ -596,7 +586,7 @@ print(puzzleOutput)
这种特性可以被用来匹配或者忽略一个或多个分支。因为 Swift 的`switch`需要包含所有的分支而且不允许有为空的分支,有时为了使你的意图更明显,需要特意匹配或者忽略某个分支。那么当你想忽略某个分支时,可以在该分支内写上`break`语句。当那个分支被匹配到时,分支内的`break`语句立即结束`switch`代码块。
>注意:
当一个`switch`分支仅仅包含注释时,会被报编译时错误。注释不是代码语句而且也不能让`switch`分支达到被忽略的效果。你总是可以使用`break`来忽略某个分支。
当一个`switch`分支仅仅包含注释时,会被报编译时错误。注释不是代码语句而且也不能让`switch`分支达到被忽略的效果。你应该使用`break`来忽略某个分支。
下面的例子通过`switch`来判断一个`Character`值是否代表下面四种语言之一。为了简洁,多个值被包含在了同一个分支情况中。
@ -625,14 +615,14 @@ if let integerValue = possibleIntegerValue {
这个例子检查`numberSymbol`是否是拉丁,阿拉伯,中文或者泰语中的`1`到`4`之一。如果被匹配到,该`switch`分支语句给`Int?`类型变量`possibleIntegerValue`设置一个整数值。
当`switch`代码块执行完后,接下来的代码通过使用可选绑定来判断`possibleIntegerValue`是否曾经被设置过值。因为是可选类型的缘故,`possibleIntegerValue`有一个隐式的初始值`nil`,所以仅仅当`possibleIntegerValue`曾被`switch`代码块的前四个分支中的某个设置过一个值时,可选的绑定会被判定为成功。
当`switch`代码块执行完后,接下来的代码通过使用可选绑定来判断`possibleIntegerValue`是否曾经被设置过值。因为是可选类型的缘故,`possibleIntegerValue`有一个隐式的初始值`nil`,所以仅仅当`possibleIntegerValue`曾被`switch`代码块的前四个分支中的某个设置过一个值时,可选的绑定会被判定为成功。
在上面的例子中,想要把`Character`所有的的可能性都枚举出来是不现实的,所以使用`default`分支来包含所有上面没有匹配到字符的情况。由于这个`default`分支不需要执行任何动作,所以它只写了一条`break`语句。一旦落入到`default`分支中后,`break`语句就完成了该分支的所有代码操作,代码继续向下,开始执行`if let`语句。
<a name="fallthrough"></a>
### 贯穿Fallthrough
Swift 中的`switch`不会从上一个 case 分支落入到下一个 case 分支中。相反,只要第一个匹配到的 case 分支完成了它需要执行的语句,整个`switch`代码块完成了它的执行。相比之下C 语言要求你显式地插入`break`语句到每个`switch`分支的末尾来阻止自动落入到下一个 case 分支中。Swift 的这种避免默认落入到下一个分支中的特性意味着它的`switch` 功能要比 C 语言的更加清晰和可预测,可以避免无意识地执行多个 case 分支从而引发的错误。
Swift 中的`switch`不会从上一个 case 分支落入到下一个 case 分支中。相反,只要第一个匹配到的 case 分支完成了它需要执行的语句,整个`switch`代码块完成了它的执行。相比之下C 语言要求你显式地插入`break`语句到每个 case 分支的末尾来阻止自动落入到下一个 case 分支中。Swift 的这种避免默认落入到下一个分支中的特性意味着它的`switch` 功能要比 C 语言的更加清晰和可预测,可以避免无意识地执行多个 case 分支从而引发的错误。
如果你确实需要 C 风格的贯穿的特性,你可以在每个需要该特性的 case 分支中使用`fallthrough`关键字。下面的例子使用`fallthrough`来创建一个数字的描述语句。
@ -650,31 +640,29 @@ print(description)
// 输出 "The number 5 is a prime number, and also an integer."
```
这个例子定义了一个`String`类型的变量`description`并且给它设置了一个初始值。函数使用`switch`逻辑来判断`integerToDescribe`变量的值。当`integerToDescribe`的值属于列表中的质数之一时,该函数添加一段文字在`description`后,来表明这个数字是一个质数。然后它使用`fallthrough`关键字来“贯穿”到`default`分支中。`default`分支添加一段额外的文字在`description`的最后,至此`switch`代码块执行完了。
这个例子定义了一个`String`类型的变量`description`并且给它设置了一个初始值。函数使用`switch`逻辑来判断`integerToDescribe`变量的值。当`integerToDescribe`的值属于列表中的质数之一时,该函数在`description`后添加一段文字,来表明这个数字是一个质数。然后它使用`fallthrough`关键字来“贯穿”到`default`分支中。`default`分支在`description`的最后添加一段额外的文字,至此`switch`代码块执行完了。
如果`integerToDescribe`的值不属于列表中的任何质数,那么它不会匹配到第一个`switch`分支。而这里没有其他特别的分支情况,所以`integerToDescribe`匹配到包含所有的`default`分支中。
如果`integerToDescribe`的值不属于列表中的任何质数,那么它不会匹配到第一个`switch`分支。而这里没有其他特别的分支情况,所以`integerToDescribe`匹配到`default`分支中。
当`switch`代码块执行完后,使用`print(_:separator:terminator:)`函数打印该数字的描述。在这个例子中,数字`5`被准确的识别为了一个质数。
> 注意:
> `fallthrough`关键字不会检查它下一个将会落入执行的 case 中的匹配条件。`fallthrough`简单地使代码执行继续连接到下一个 case 中的执行代码,这和 C 语言标准中的`switch`语句特性是一样的。
> `fallthrough`关键字不会检查它下一个将会落入执行的 case 中的匹配条件。`fallthrough`简单地使代码继续连接到下一个 case 中的代码,这和 C 语言标准中的`switch`语句特性是一样的。
<a name="labeled_statements"></a>
### 带标签的语句
在 Swift 中,你可以在循环体和`switch`代码块中嵌套循环体和`switch`代码块来创造复杂的控制流结构。然而,循环体和`switch`代码块两者都可以使用`break`语句来提前结束整个方法体。因此,显式地指明`break`语句想要终止的是哪个循环体或者`switch`代码块,会很有用。类似地,如果你有许多嵌套的循环体,显式指明`continue`语句想要影响哪一个循环体也会非常有用。
在 Swift 中,你可以在循环体和条件语句中嵌套循环体和条件语句来创造复杂的控制流结构。并且,循环体和条件语句都可以使用`break`语句来提前结束整个代码块。因此,显式地指明`break`语句想要终止的是哪个循环体或者条件语句,会很有用。类似地,如果你有许多嵌套的循环体,显式指明`continue`语句想要影响哪一个循环体也会非常有用。
为了实现这个目的,你可以使用标签来标记一个循环体或者`switch`代码块,当使用`break`或者`continue`时,带上这个标签,可以控制该标签代表对象的中断或者执行。
为了实现这个目的,你可以使用标签*statement label*)来标记一个循环体或者条件语句,对于一个条件语句,你可以使用`break`加标签的方式,来结束这个被标记的语句。对于一个循环语句,你可以使用`break`或者`continue`加标签,来结束或者继续这条被标记语句的执行。
产生一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签,并且该标签后面还需带着一个冒号。下面是一个`while`循环体的语法,同样的规则适用于所有的循环体和`switch`代码块
声明一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签,作为这个语句的前导关键字(introducor keyword)并且该标签后面跟随一个冒号。下面是一个针对`while`循环体的标签语法,同样的规则适用于所有的循环体和条件语句
```swift
label name: while condition {
statements
}
```
> `label name`: while `condition` {
> `statements`
> }
下面的例子是一个带有标签的`while`循环体中调用`break`和`continue`语句,该循环体是前面章节中*蛇和梯子*的改编版本。这次,游戏增加了一条额外的规则:
下面的例子是前面章节中*蛇和梯子*的适配版本,在此版本中,我们将使用一个带有标签的`while`循环体中调用`break`和`continue`语句。这次,游戏增加了一条额外的规则:
- 为了获胜,你必须*刚好*落在第 25 个方块中。
@ -682,35 +670,36 @@ 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`值被和之前一样的方式初始化:
```swift
let finalSquare = 25
var board = [Int](count: finalSquare + 1, repeatedValue: 0)
var board = [Int](repeating: 0, count: finalSquare + 1)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
var square = 0
var diceRoll = 0
```
这个版本的游戏使用`while`循环和`switch`方法块来实现游戏的逻辑。`while`循环有一个标签名`gameLoop`,来表明它是蛇与梯子的主循环。
这个版本的游戏使用`while`循环和`switch`语句来实现游戏的逻辑。`while`循环有一个标签名`gameLoop`,来表明它是游戏的主循环。
该`while`循环体的条件判断语句是`while square !=finalSquare`这表明你必须刚好落在方格25中。
```swift
gameLoop: while square != finalSquare {
if ++diceRoll == 7 { diceRoll = 1 }
diceRoll += 1
if diceRoll == 7 { diceRoll = 1 }
switch square + diceRoll {
case finalSquare:
// 到达最后一个方块,游戏结束
// diceRoll will move us to the final square, so the game is over
break gameLoop
case let newSquare where newSquare > finalSquare:
// 超出最后一个方块,再掷一次骰子
// diceRoll will move us beyond the final square, so roll again
continue gameLoop
default:
// 本次移动有效
// this is a valid move, so find out its effect
square += diceRoll
square += board[square]
}
@ -718,77 +707,74 @@ gameLoop: while square != finalSquare {
print("Game over!")
```
每次循环迭代开始时掷骰子。与之前玩家掷完骰子就立即移动不同,这里使用了`switch`来考虑每次移动可能产生的结果,从而决定玩家本次是否能够移动。
每次循环迭代开始时掷骰子。与之前玩家掷完骰子就立即移动不同,这里使用了`switch`语句来考虑每次移动可能产生的结果,从而决定玩家本次是否能够移动。
- 如果骰子数刚好使玩家移动到最终的方格里,游戏结束。`break gameLoop`语句跳转控制去执行`while`循环体后的第一行代码,游戏结束。
- 如果骰子数将会使玩家的移动超出最后的方格,那么这种移动是不合法的,玩家需要重新掷骰子。`continue gameLoop`语句结束本次`while`循环的迭代,开始下一次循环迭代
- 在剩余的所有情况中,骰子数产生的都是合法的移动。玩家向前移动骰子数个方格,然后游戏逻辑再处理玩家当前是否处于蛇头或者梯子的底部。本次循环迭代结束,控制跳转到`while`循环体的条件判断语句处,再决定是否能够继续执行下次循环迭代
- 如果骰子数刚好使玩家移动到最终的方格里,游戏结束。`break gameLoop`语句跳转控制去执行`while`循环体后的第一行代码,意味着游戏结束。
- 如果骰子数将会使玩家的移动超出最后的方格,那么这种移动是不合法的,玩家需要重新掷骰子。`continue gameLoop`语句结束本次`while`循环,开始下一次循环。
- 在剩余的所有情况中,骰子数产生的都是合法的移动。玩家向前移动 `diceRoll` 个方格,然后游戏逻辑再处理玩家当前是否处于蛇头或者梯子的底部。接着本次循环结束,控制跳转到`while`循环体的条件判断语句处,再决定是否需要继续执行下次循环。
> 注意:
> 如果上述的`break`语句没有使用`gameLoop`标签,那么它将会中断`switch`代码块而不是`while`循环。使用`gameLoop`标签清晰的表明了`break`想要中断的是哪个代码块。
>注意:
如果上述的`break`语句没有使用`gameLoop`标签,那么它将会中断`switch`语句而不是`while`循环。使用`gameLoop`标签清晰的表明了`break`想要中断的是哪个代码块。
同时请注意,当调用`continue gameLoop`去跳转到下一次循环迭代时,这里使用`gameLoop`标签并不是严格必须的。因为在这个游戏中,只有一个循环体,所以`continue`语句会影响到哪个循环体是没有歧义的。然而,`continue`语句使用`gameLoop`标签也是没有危害的。这样做符合标签的使用规则,同时参照旁边的`break gameLoop`,能够使游戏的逻辑更加清晰和易于理解。
<a name="early_exit"></a>
## 提前退出
像`if`语句一样,`guard`的执行取决于一个表达式的布尔值。我们可以使用`guard`语句来要求条件必须为真时,以执行`guard`语句后的代码。不同于`if`语句,一个`guard`语句总是有一个`else`句,如果条件不为真则执行`else`句中的代码。
像`if`语句一样,`guard`的执行取决于一个表达式的布尔值。我们可以使用`guard`语句来要求条件必须为真时,以执行`guard`语句后的代码。不同于`if`语句,一个`guard`语句总是有一个`else`句,如果条件不为真则执行`else`句中的代码。
```swift
func greet(person: [String: String]) {
guard let name = person["name"] else {
return
}
print("Hello \(name)")
guard let location = person["location"] else {
print("I hope the weather is nice near you.")
return
}
print("I hope the weather is nice in \(location).")
}
greet(["name": "John"])
// prints "Hello John!"
// prints "I hope the weather is nice near you."
// 输出 "Hello John!"
// 输出 "I hope the weather is nice near you."
greet(["name": "Jane", "location": "Cupertino"])
// prints "Hello Jane!"
// prints "I hope the weather is nice in Cupertino."
// 输出 "Hello Jane!"
// 输出 "I hope the weather is nice in Cupertino."
```
如果`guard`语句的条件被满足,则在保护语句的封闭大括号结束后继续执行代码。任何使用了可选绑定作为条件的一部分并被分配了值的变量或常量对于剩下的保护语句出现的代码段是可用的
如果`guard`语句的条件被满足,则继续执行`guard`语句大括号后的代码。将变量或者常量的可选绑定作为`guard`语句的条件,都可以保护`guard`语句后面的代码
如果条件不被满足,在`else`分支上的代码就会被执行。这个分支必须转移控制以退出`guard`语句出现的代码段。它可以用控制转移语句如`return`,`break`,`continue`或者`throw`做这件事,或者调用一个不返回的方法或函数,例如`fatalError()`。
相比于可以实现同样功能的`if`语句,按需使用`guard`语句会提升我们代码的可性。
它可以使你的代码连贯的被执行而不需要将它包在`else`块中,它可以使你处理违反要求的代码使其接近要求。
相比于可以实现同样功能的`if`语句,按需使用`guard`语句会提升我们代码的可性。它可以使你的代码连贯的被执行而不需要将它包在`else`块中,它可以使你在紧邻条件判断的地方,处理违规的情况。
<a name="checking_api_availability"></a>
## 检测 API 可用性
Swift检查 API 可用性的内置支持,这可以确保我们不会不小心地使用对于当前部署目标不可用的 API。
Swift内置支持检查 API 可用性,这可以确保我们不会在当前部署机器上,不小心地使用了不可用的API。
编译器使用 SDK 中的可用信息来验证我们的代码中使用的所有 API 在项目指定的部署目标上是否可用。如果我们尝试使用一个不可用的 APISwift 会在编译报错。
编译器使用 SDK 中的可用信息来验证我们的代码中使用的所有 API 在项目指定的部署目标上是否可用。如果我们尝试使用一个不可用的 APISwift 会在编译报错。
我们使用一个可用性条件在一个`if`或`guard`语句中去有条件的执行一段代码,这取决于我们想要使用的 API 是否在运行时是可用。编译器使用从可用性条件语句中获取的信息去验证代码块中调用的 API 是否可用。
我们`if`或`guard`语句中使用`可用性条件availability condition)`去有条件的执行一段代码,来在运行时判断调用的API是否可用。编译器使用从可用性条件语句中获取的信息去验证,在这个代码块中调用的 API 是否可用。
```swift
if #available(iOS 9, OSX 10.10, *) {
// 在 iOS 使用 iOS 9 的 API, 在 OS X 使用 OS X v10.10 的 API
if #available(iOS 10, macOS 10.12, *) {
// 在 iOS 使用 iOS 10 的 API, 在 macOS 使用 macOS 10.12 的 API
} else {
// 使用先前版本的 iOS 和 OS X 的 API
// 使用先前版本的 iOS 和 macOS 的 API
}
```
以上可用性条件指定了在 iOS 系统上`if`的代码仅在 iOS 9 及更高版本的系统上执行;在 OS X,仅在 OS X v10.10 及更高版本的系统上执行。最后一个参数,`*`,是必须的,用于处理未来潜在的平台
以上可用性条件指定在iOS中`if`语句的代码块仅仅在 iOS 10 及更高的系统下运行;在 macOS中,仅在 macOS 10.12 及更高才会运行。最后一个参数,`*`,是必须的,用于指定在所有其它平台中如果版本号高于你的设备指定的最低版本if语句的代码块将会运行
在它一般形式中,可用性条件获取了一系列平台名字和版本。平台名字可以是`iOS``OSX``watchOS`。除了特定的主板本号像 iOS 8我们可以指定较小的版本号像 iOS 8.3 以及 OS X v10.10.3。
在它一般形式中,可用性条件使用了一个平台名字和版本的列表。平台名字可以是`iOS``macOS``watchOS`和`tvOS`——请访问[声明属性](../chapter3/06_Attributes.html)来获取完整列表。除了指定像 iOS 8的主板本号,我们可以指定iOS 8.3 以及 macOS 10.10.3的子版本号
```swift
if #available(platform name version, ..., *) {
statements to execute if the APIs are available
statements to execute if the APIs are available
} else {
fallback statements to execute if the APIs are unavailable
fallback statements to execute if the APIs are unavailable
}
```

View File

@ -12,150 +12,163 @@
> 翻译:[DianQK](https://github.com/DianQK)
> 定稿:[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)
- [函数参数与返回值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)
- [嵌套函数Nested Functions](#Nested_Functions)
*函数*是用来完成特定任务的独立的代码块。你给一个函数起一个合适的名字,用来标识函数做什么,并且当函数需要执行的时候,这个名字会被用于“调用”函数。
Swift 统一的函数语法足够灵活,可以用来表示任何函数,包括从最简单的没有参数名字的 C 风格函数,到复杂的带局部和外部参数名的 Objective-C 风格函数。参数可以提供默认值,以简化函数调用。参数也可以既当做传入参数,也当做传出参数,也就是说,一旦函数执行结束,传入的参数值可以被修改
`函数` 是一段完成特定任务的独立代码片段。你可以通过给函数命名来标识某个函数的功能,这个名字可以被用来在需要的时候"调用"这个函数来完成它的任务
Swift 中,每个函数都有一种类型,包括函数的参数值类型和返回值类型。你可以把函数类型当做任何其他普通变量类型一样处理,这样就可以更简单地把函数当做别的函数的参数,也可以从其他函数中返回函数。函数的定义可以写在在其他函数定义中,这样可以在嵌套函数范围内实现功能封装
Swift 统一的函数语法非常的灵活,可以用来表示任何函数,包括从最简单的没有参数名字的 C 风格函数,到复杂的带局部和外部参数名的 Objective-C 风格函数。参数可以提供默认值,以简化函数调用。参数也可以既当做传入参数,也当做传出参数,也就是说,一旦函数执行结束,传入的参数值将被修改
在 Swift 中,每个函数都有一个由函数的参数值类型和返回值类型组成的类型。你可以把函数类型当做任何其他普通变量类型一样处理,这样就可以更简单地把函数当做别的函数的参数,也可以从其他函数中返回函数。函数的定义可以写在其他函数定义中,这样可以在嵌套函数范围内实现功能封装。
<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
func sayHello(personName: String) -> String {
let greeting = "Hello, " + personName + "!"
func greet(person: String) -> String {
let greeting = "Hello, " + person + "!"
return greeting
}
```
所有的这些信息汇总起来成为函数的*定义*,并以 `func` 作为前缀。指定函数返回类型时,用返回箭头 `->`(一个连字符后跟一个右尖括号)后跟返回类型的名称的方式来表示。
该定义描述了函数做什么,它期望接收什么和执行结束时它返回的结果是什么类型。这样的定义使得函数可以在别的地方以一种清晰的方式被调用:
该定义描述了函数的功能,它期望接收什么作为参数和执行结束时它返回的结果是什么类型。这样的定义使得函数可以在别的地方以一种清晰的方式被调用:
```swift
print(sayHello("Anna"))
// prints "Hello, Anna!"
print(sayHello("Brian"))
// prints "Hello, Brian!"
print(greet(person: "Anna"))
// 打印 "Hello, Anna!"
print(greet(person: "Brian"))
// 打印 "Hello, Brian!"
```
调用 `sayHello(_:)` 函数时,在圆括号中传给它一个 `String` 类型的实参,例如 `sayHello("Anna")`。因为这个函数返回一个 `String` 类型的值,`sayHello` 可以被包含在 `print(_:separator:terminator:)` 的调用中,用来输出这个函数的返回值,正如上面所示
调用 `sayHello(_:)` 函数时,在圆括号中传给它一个 `String` 类型的实参,例如 `sayHello("Anna")`正如上面所示,因为这个函数返回一个 `String` 类型的值,所以`sayHello` 可以被包含在 `print(_:separator:terminator:)` 的调用中,用来输出这个函数的返回值。
`sayHello(_:)` 的函数体中,先定义了一个新的名为 `greeting``String` 常量,同时赋值了给 `personName` 的一个简单问候消息。然后用 `return` 关键字把这个问候返回出去。一旦 `return greeting` 被调用,该函数结束它的执行并返回 `greeting` 的当前值。
>注意
`print(_:separator:terminator:)` 函数的第一个参数并没有设置一个标签,而其他的参数因为已经有了默认值,因此是可选的。关于这些函数语法上的变化详见下方关于 函数参数标签和参数名 以及 默认参数值。
`sayHello(_:)` 的函数体中,先定义了一个新的名为 `greeting``String` 常量,同时,把对 `personName` 的问候消息赋值给了 `greeting` 。然后用 `return` 关键字把这个问候返回出去。一旦 `return greeting` 被调用,该函数结束它的执行并返回 `greeting` 的当前值。
你可以用不同的输入值多次调用 `sayHello(_:)`。上面的例子展示的是用`"Anna"``"Brian"`调用的结果,该函数分别返回了不同的结果。
为了简化这个函数的定义,可以将问候消息的创建和返回写成一句:
```swift
func sayHelloAgain(personName: String) -> String {
return "Hello again, " + personName + "!"
func greetAgain(person: String) -> String {
return "Hello again, " + person + "!"
}
print(sayHelloAgain("Anna"))
// prints "Hello again, Anna!"
print(greetAgain(person: "Anna"))
// 打印 "Hello again, Anna!"
```
<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
func sayHelloWorld() -> String {
return "hello, world"
}
print(sayHelloWorld())
// prints "hello, world"
// 打印 "hello, world"
```
尽管这个函数没有参数,但是定义中在函数名后还是需要一对圆括号。当被调用时,也需要在函数名后写一对圆括号。
<a name="functions_with_multiple_parameters"></a>
### 多参数函数 (Functions With Multiple Parameters)
函数可以有多种输入参数,这些参数被包含在函数的括号之中,以逗号分隔。
这个函数取得一个人的名字和是否被招呼作为输入,并对那个人返回适当问候语:
下面这个函数一个人名和是否已经打过招呼作为输入,并返回对这个人的适当问候语:
```swift
func sayHello(personName: String, alreadyGreeted: Bool) -> String {
func greet(person: String, alreadyGreeted: Bool) -> String {
if alreadyGreeted {
return sayHelloAgain(personName)
return greetAgain(person: person)
} else {
return sayHello(personName)
return greet(person: person)
}
}
print(sayHello("Tim", alreadyGreeted: true))
// prints "Hello again, Tim!"
print(greet(person: "Tim", alreadyGreeted: true))
// 打印 "Hello again, Tim!"
```
你通过在括号内传递一个`String`参数值和一个标识为`alreadyGreeted``Bool`值,使用逗号分隔来调用`sayHello(_:alreadyGreeted:)`函数。
当调用超过一个参数的函数时,第一个参数后的参数根据其对应的参数名称标记,函数参数命名在[函数参数名称Function Parameter Names](#Function_Parameter_Names)有更详细的描述。
可以通过在括号内使用逗号分隔来传递一个`String`参数值和一个标识为`alreadyGreeted``Bool`值,来调用`sayHello(_:alreadyGreeted:)`函数。注意这个函数和上面`greet(person:)`是不同的。虽然它们都有着同样的名字`greet`,但是`greet(person:alreadyGreeted:)`函数需要两个参数,而`greet(person:)`只需要一个参数。
<a name="functions_without_return_values"></a>
### 无返回值函数Functions Without Return Values
### 无返回值函数 (Functions Without Return Values)
函数可以没有返回值。下面是 `sayHello(_:)` 函数的另一个版本,叫 `sayGoodbye(_:)`,这个函数直接输出 `String` 值,而不是返回它:
函数可以没有返回值。下面是 `sayHello(_:)` 函数的另一个版本,叫 `sayGoodbye(_:)`,这个函数直接打印一个`String`值,而不是返回它:
```swift
func sayGoodbye(personName: String) {
print("Goodbye, \(personName)!")
func greet(person: String) {
print("Hello, \(person)!")
}
sayGoodbye("Dave")
// prints "Goodbye, Dave!"
greet(person: "Dave")
// 打印 "Hello, Dave!"
```
因为这个函数不需要返回值,所以这个函数的定义中没有返回箭头(->)和返回类型。
> 注意
> 严格上来说,虽然没有返回值被定义,`sayGoodbye(_:)` 函数依然返回了值。没有定义返回类型的函数会返回特殊的值,叫 `Void`。它其实是一个空的元组tuple没有任何元素可以写成`()`
>注意
严格上来说,虽然没有返回值被定义,`sayGoodbye(_:)` 函数依然返回了值。没有定义返回类型的函数会返回一个特殊的`Void`。它其实是一个空的元组tuple没有任何元素可以写成()。
被调用时,一个函数的返回值可以被忽略:
```swift
func printAndCount(stringToPrint: String) -> Int {
print(stringToPrint)
return stringToPrint.characters.count
func printAndCount(string: String) -> Int {
print(string)
return string.characters.count
}
func printWithoutCounting(stringToPrint: String) {
printAndCount(stringToPrint)
func printWithoutCounting(string: String) {
let _ = printAndCount(string: string)
}
printAndCount("hello, world")
// prints "hello, world" and returns a value of 12
printWithoutCounting("hello, world")
// prints "hello, world" but does not return a value
printAndCount(string: "hello, world")
// 打印 "hello, world" 并且返回值 12
printWithoutCounting(string: "hello, world")
// 打印 "hello, world" 但是没有返回任何值
```
第一个函数 `printAndCount(_:)`,输出一个字符串并返回 `Int` 类型的字符数。第二个函数 `printWithoutCounting`调用了第一个函数,但是忽略了它的返回值。当第二个函数被调用时,消息依然会由第一个函数输出,但是返回值不会被用到。
> 注意
> 返回值可以被忽略但定义了有返回值的函数必须返回一个值如果在函数定义底部没有返回任何值将导致编译错误compile-time error
>注意
返回值可以被忽略,但定义了有返回值的函数必须返回一个值,如果在函数定义底部没有返回任何值,将导致编译错误compile-time error
<a name="functions_with_multiple_return_values"></a>
### 多重返回值函数Functions with Multiple Return Values
### 多重返回值函数 (Functions with Multiple Return Values)
你可以用元组tuple类型让多个值作为一个复合值从函数中返回。
面的这个例子中,定义了一个名为`minMax(_:)`的函数,作用是在一个`Int`数组中找出最小值与最大值。
例中定义了一个名为 `minMax(_:)` 的函数,作用是在一个 `Int` 类型的数组中找出最小值与最大值。
```swift
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
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)")
// prints "min is -6 and max is 109"
// 打印 "min is -6 and max is 109"
```
需要注意的是,元组的成员不需要在元组从函数中返回时命名,因为它们的名字已经在函数返回类型中指定了。
<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
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
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)")
}
// 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
func someFunction(firstParameterName: Int, secondParameterName: Int) {
// function body goes here
// firstParameterName and secondParameterName refer to
// the argument values for the first and second parameters
// In the function body, firstParameterName and secondParameterName
// refer to 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)
```
一般情况下,第一个参数省略其外部参数名,第二个以及随后的参数使用其局部参数名作为外部参数名。所有参数必须有独一无二的局部参数名。尽管多个参数可以有相同的外部参数名,但不同的外部参数名能让你的代码更有可读性
<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>
### 默认参数值Default Parameter Values
### 默认参数值 (Default Parameter Values)
你可以在函数体中为每个参数定义`默认值Deafult Values`。当默认值被定义后,调用这个函数时可以忽略这个参数。
你可以在函数体中通过给参数赋值来为任意一个参数定义默认值Deafult Values。当默认值被定义后调用这个函数时可以忽略这个参数。
```swift
func someFunction(parameterWithDefault: Int = 12) {
// function body goes here
// if no arguments are passed to the function call,
// value of parameterWithDefault is 12
func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
// If you omit the second argument when calling this function, then
// the value of parameterWithDefault is 12 inside the function body.
}
someFunction(6) // parameterWithDefault is 6
someFunction() // parameterWithDefault is 12
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault is 6
someFunction(parameterWithoutDefault: 4) // parameterWithDefault is 12
```
> 注意
> 将带有默认值的参数放在函数参数列表的最后。这样可以保证在函数调用时,非默认参数的顺序是一致的,同时使得相同的函数在不同情况下调用时显得更为清晰。
将不带有默认值的参数放在函数参数列表的最前。一般来说,没有默认值的参数更加的重要,将不带默认值的参数放在最前保证在函数调用时,非默认参数的顺序是一致的,同时也使得相同的函数在不同情况下调用时显得更为清晰。
<a name="variadic_parameters"></a>
### 可变参数Variadic Parameters
### 可变参数 (Variadic Parameters)
一个`可变参数variadic parameter`可以接受零个或多个值。函数调用时,你可以用可变参数来指定函数参数可以被传入不确定数量的输入值。通过在变量类型名后面加入`...`的方式来定义可变参数。
一个可变参数variadic parameter可以接受零个或多个值。函数调用时你可以用可变参数来指定函数参数可以被传入不确定数量的输入值。通过在变量类型名后面加入`...`的方式来定义可变参数。
可变参数的传入值在函数体中变为此类型的一个数组。例如,一个叫做 `numbers``Double...` 型可变参数,在函数体内可以当做一个叫 `numbers``[Double]` 型的数组常量。
下面的这个函数用来计算一组任意长度数字的`算术平均数arithmetic mean`
下面的这个函数用来计算一组任意长度数字的 *算术平均数arithmetic mean)*
```swift
func arithmeticMean(numbers: Double...) -> Double {
func arithmeticMean(_ numbers: Double...) -> Double {
var total: Double = 0
for number in numbers {
total += number
@ -325,74 +332,33 @@ arithmeticMean(3, 8.25, 18.75)
// 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>
### 输入输出参数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
func swapTwoInts(inout a: Int, inout _ b: Int) {
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
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(_:_:)` 函数前,都加了 `&` 的前缀:
@ -401,31 +367,32 @@ var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &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(_:_:)` 函数中被修改,尽管它们的定义在函数体外。
> 注意
> 输入输出参数和返回值是不一样的。上面的 `swapTwoInts` 函数并没有定义任何返回值,但仍然修改了 `someInt` 和 `anotherInt` 的值。输入输出参数是函数对函数体外产生影响的另一种方式。
>注意
输入输出参数和返回值是不一样的。上面的 `swapTwoInts` 函数并没有定义任何返回值,但仍然修改了 `someInt``anotherInt` 的值。输入输出参数是函数对函数体外产生影响的另一种方式。
<a name="Function_Types"></a>
## 函数类型Function Types
## 函数类型 (Function Types)
每个函数都有种特定的函数类型由函数的参数类型和返回类型组成。
每个函数都有种特定的`函数类型`,函数的类型由函数的参数类型和返回类型组成。
例如:
```swift
func addTwoInts(a: Int, _ b: Int) -> Int {
func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
func multiplyTwoInts(a: Int, _ b: Int) -> Int {
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
return a * b
}
```
这个例子中定义了两个简单的数学函数:`addTwoInts``multiplyTwoInts`。这两个函数都接受两个 `Int` 值, 返回一个`Int`值。
这个例子中定义了两个简单的数学函数:`addTwoInts``multiplyTwoInts`。这两个函数都接受两个 `Int` 值, 返回一个 `Int` 值。
这两个函数的类型是 `(Int, Int) -> Int`,可以解读为“这个函数类型有两个 `Int` 型的参数并返回一个 `Int` 型的值。”。
@ -440,7 +407,7 @@ func printHelloWorld() {
这个函数的类型是:`() -> Void`,或者叫“没有参数,并返回 `Void` 类型的函数”。
<a name="using_function_types"></a>
### 使用函数类型Using Function Types
### 使用函数类型 (Using Function Types)
在 Swift 中,使用函数类型就像使用其他类型一样。例如,你可以定义一个类型为函数的常量或变量,并将适当的函数赋值给它:
@ -448,76 +415,77 @@ func printHelloWorld() {
var mathFunction: (Int, Int) -> Int = addTwoInts
```
可以解读为:
段代码可以解读为:
定义一个叫做 `mathFunction` 的变量,类型是‘一个有两个 `Int` 型的参数并返回一个 `Int` 型的值的函数’,并让这个新变量指向 `addTwoInts` 函数”。
定义一个叫做 `mathFunction` 的变量,类型是‘一个有两个 `Int` 型的参数并返回一个 `Int` 型的值的函数’,并让这个新变量指向 `addTwoInts` 函数”。
`addTwoInts``mathFunction` 有同样的类型,所以这个赋值过程在 Swift 类型检查中是允许的。
`addTwoInts``mathFunction` 有同样的类型,所以这个赋值过程在 Swift 类型检查(type-check)中是允许的。
现在,你可以用 `mathFunction` 来调用被赋值的函数了:
```swift
print("Result: \(mathFunction(2, 3))")
// prints "Result: 5"
// Prints "Result: 5"
```
有相同匹配类型的不同函数可以被赋值给同一个变量,就像非函数类型的变量一样:
```swift
mathFunction = multiplyTwoInts
print("Result: \(mathFunction(2, 3))")
// prints "Result: 6"
// Prints "Result: 6"
```
就像其他类型一样,当赋值一个函数给常量或变量时,你可以让 Swift 来推断其函数类型:
```swift
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>
### 函数类型作为参数类型Function Types as Parameter Types
### 函数类型作为参数类型 (Function Types as Parameter Types)
你可以用`(Int, Int) -> Int`这样的函数类型作为另一个函数的参数类型。这样你可以将函数的一部分实现留给函数的调用者来提供。
你可以用 `(Int, Int) -> Int` 这样的函数类型作为另一个函数的参数类型。这样你可以将函数的一部分实现留给函数的调用者来提供。
下面是另一个例子,正如上面的函数一样,同样是输出某种数学运算结果:
```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))")
}
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>
### 函数类型作为返回类型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
func stepForward(input: Int) -> Int {
func stepForward(_ input: Int) -> Int {
return input + 1
}
func stepBackward(input: Int) -> Int {
func stepBackward(_ input: Int) -> Int {
return input - 1
}
```
下面这个叫做 `chooseStepFunction(_:)` 的函数,它的返回类型是 `(Int) -> Int` 类型的函数。`chooseStepFunction(_:)` 根据布尔值 `backwards` 来返回 `stepForward(_:)` 函数或 `stepBackward(_:)` 函数:
如下名为 `chooseStepFunction(_:)` 的函数,它的返回类型是 `(Int) -> Int` 类型的函数。`chooseStepFunction(_:)` 根据布尔值 `backwards` 来返回 `stepForward(_:)` 函数或 `stepBackward(_:)` 函数:
```swift
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
return backwards ? stepBackward : stepForward
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
return backward ? stepBackward : stepForward
}
```
@ -525,13 +493,14 @@ func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
```swift
var currentValue = 3
let moveNearerToZero = chooseStepFunction(currentValue > 0)
// moveNearerToZero now refers to the stepBackward() function
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// 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
print("Counting to zero:")
@ -548,22 +517,22 @@ print("zero!")
```
<a name="Nested_Functions"></a>
## 嵌套函数Nested Functions
## 嵌套函数 (Nested Functions)
章中你所见到的所有函数都叫全局函数global functions它们定义在全局域中。你也可以把函数定义在别的函数体中称作嵌套函数nested functions
到目前为止本章中你所见到的所有函数都叫`全局`函数global functions它们定义在`全局域`中。你也可以把函数定义在别的函数体中,称作 `嵌套函数`nested functions
默认情况下嵌套函数是对外界不可见的但是可以被它们的外围函数enclosing function调用。一个外围函数也可以返回它的某一个嵌套函数使得这个函数可以在其他域中被使用。
你可以用返回嵌套函数的方式重写 `chooseStepFunction(_:)` 函数:
```swift
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
func stepForward(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
let moveNearerToZero = chooseStepFunction(currentValue > 0)
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
print("\(currentValue)... ")

View File

@ -11,6 +11,12 @@
> 2.1
> 翻译:[100mango](https://github.com/100mango), [magicdict](https://github.com/magicdict)
> 校对:[shanks](http://codebuild.me)
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-12
>
> 3.0
> 翻译:[Lanford](https://github.com/LanfordCai) 2016-09-19
本页包含内容:
@ -18,15 +24,15 @@
- [尾随闭包Trailing Closures](#trailing_closures)
- [值捕获Capturing Values](#capturing_values)
- [闭包是引用类型Closures Are Reference Types](#closures_are_reference_types)
- [逃逸闭包(Nonescaping Closures) ](#nonescaping_closures)
- [逃逸闭包(Escaping Closures) ](#escaping_closures)
- [自动闭包Autoclosures](#autoclosures)
闭包是自包含的函数代码块可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块blocks以及其他一些编程语言中的匿名函数比较相似。
*闭包*是自包含的函数代码块可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块blocks以及其他一些编程语言中的匿名函数比较相似。
闭包可以捕获和存储其所在上下文中任意常量和变量的引用。这就是所谓的闭合包裹着这些常量和变量,俗称闭包。Swift 会为管理在捕获过程中涉及到的所有内存操作。
闭包可以捕获和存储其所在上下文中任意常量和变量的引用。*闭合包裹*常量和变量,所谓闭包。Swift 会为管理在捕获过程中涉及到的所有内存操作。
> 注意
> 如果不熟悉捕获capturing这个概念也不用担心可以在[值捕获](#capturing_values)章节对其进行详细了解。
> 注意
> 如果不熟悉捕获capturing这个概念也不用担心可以在[值捕获](#capturing_values)章节对其进行详细了解。
在[函数](./06_Functions.html)章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:
@ -37,7 +43,7 @@
Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进行语法优化,主要优化如下:
* 利用上下文推断参数和返回值类型
* 隐式返回单表达式闭包,即单表达式闭包可以省略`return`关键字
* 隐式返回单表达式闭包,即单表达式闭包可以省略 `return` 关键字
* 参数名称缩写
* 尾随Trailing闭包语法
@ -45,43 +51,43 @@ Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进
## 闭包表达式Closure Expressions
[嵌套函数](./06_Functions.html#nested_function)是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。当然,有时候写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在处理一些函数并需要将另外一些函数作为该函数的参数时。
[嵌套函数](./06_Functions.html#nested_function)是一个在较复杂函数中方便进行命名和定义自包含代码模块的方式。当然,有时候写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在处理一些函数并需要将另外一些函数作为该函数的参数时。
闭包表达式是一种利用简洁语法构建内联闭包的方式。闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。下面闭包表达式的例子通过使用几次迭代展示了`sort(_:)`方法定义和语法优化的方式。每一次迭代都用更简洁的方式描述了相同的功能。
*闭包表达式*是一种利用简洁语法构建内联闭包的方式。闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。下面闭包表达式的例子通过使用几次迭代展示了 `sorted(by:)` 方法定义和语法优化的方式。每一次迭代都用更简洁的方式描述了相同的功能。
<a name="the_sorted_function"></a>
### sort 方法The Sort Method
### sorted 方法The Sorted Method
Swift 标准库提供了名为`sort`的方法,会根据提供的用于排序的闭包函数将已知类型数组中的值进行排序。一旦排序完成,`sort(_:)`方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被`sort(_:)`方法修改。
Swift 标准库提供了名为 `sorted(by:)` 的方法,会根据你所提供的用于排序的闭包函数将已知类型数组中的值进行排序。一旦排序完成,`sorted(by:)` 方法会返回一个与原数组大小相同包含同类型元素且元素已正确排序的新数组。原数组不会被 `sorted(by:)` 方法修改。
下面的闭包表达式示例使用`sort(_:)`方法对一个`String`类型的数组进行字母逆序排序.以下是初始数组
下面的闭包表达式示例使用 `sorted(by:)` 方法对一个 `String` 类型的数组进行字母逆序排序以下是初始数组:
```swift
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
```
`sort(_:)`方法接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回`true`,反之返回`false`
`sorted(by:)` 方法接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值*前面*,排序闭包函数需要返回`true`,反之返回`false`
该例子对一个`String`类型的数组进行排序,因此排序闭包函数类型需为`(String, String) -> Bool`
该例子对一个 `String` 类型的数组进行排序,因此排序闭包函数类型需为 `(String, String) -> Bool`
提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为`sort(_:)`方法的参数传入:
提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为 `sorted(by:)` 方法的参数传入:
```swift
func backwards(s1: String, s2: String) -> Bool {
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reversed = names.sort(backwards)
// reversed 为 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
var reversedNames = names.sorted(by: backward)
// reversedNames 为 ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
```
如果第一个字符串(`s1`)大于第二个字符串(`s2``backwards(_:_:)`函数返回`true`,表示在新的数组中`s1`应该出现在`s2`前。对于字符串中的字符来说,“大于”表示“按照字母顺序较晚出现”。这意味着字母`"B"`大于字母`"A"`,字符串`"Tom"`大于字符串`"Tim"`。该闭包将进行字母逆序排序,`"Barry"`将会排在`"Alex"`之前。
如果第一个字符串(`s1`)大于第二个字符串(`s2``backward(_:_:)` 函数返回 `true`,表示在新的数组中 `s1` 应该出现在 `s2` 前。对于字符串中的字符来说,“大于”表示“按照字母顺序较晚出现”。这意味着字母 `"B"` 大于字母 `"A"` ,字符串 `"Tom"` 大于字符串 `"Tim"`。该闭包将进行字母逆序排序,`"Barry"` 将会排在 `"Alex"` 之前。
然而,这是一个相当冗长的方式,本质上只是写了一个单表达式函数 (`a > b`)。在下面的例子中,利用闭表达式语法可以更好地构造一个内联排序闭包。
然而,以这种方式来编写一个实际上很简单的表达式(`a > b`),确实太过繁琐了。对于这个例子来说,利用闭表达式语法可以更好地构造一个内联排序闭包。
<a name="closure_expression_syntax"></a>
### 闭包表达式语法Closure Expression Syntax
闭包表达式语法有如下一般形式:
闭包表达式语法有如下一般形式:
```swift
{ (parameters) -> returnType in
@ -89,81 +95,80 @@ var reversed = names.sort(backwards)
}
```
闭包表达式语法可以使用常量、变量和`inout`类型作为参数,不能提供默认值。也可以在参数列表的最后使用可变参数。元组也可以作为参数和返回值。
闭包表达式的参数可以是inout参数,不能设定默认值。也可以使用具名的可变参数(译者注:但是如果可变参数不放在参数列表的最后一位的话,调用闭包的时时编译器将报错。可参考[这里](http://stackoverflow.com/questions/39548852/swift-3-0-closure-expression-what-if-the-variadic-parameters-not-at-the-last-pl)。元组也可以作为参数和返回值。
下面的例子展示了之前`backwards(_:_:)`函数对应的闭包表达式版本的代码:
下面的例子展示了之前 `backward(_:_:)` 函数对应的闭包表达式版本的代码:
```swift
reversed = names.sort({ (s1: String, s2: String) -> Bool in
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})
```
需要注意的是内联闭包参数和返回值类型声明与`backwards(_:_:)`函数类型声明相同。在这两种方式中,都写成了`(s1: String, s2: String) -> Bool`。然而在内联闭包表达式中,函数和返回值类型都写在大括号内,而不是大括号外。
需要注意的是内联闭包参数和返回值类型声明与 `backward(_:_:)` 函数类型声明相同。在这两种方式中,都写成了 `(s1: String, s2: String) -> Bool`。然而在内联闭包表达式中,函数和返回值类型都写在*大括号内*,而不是大括号外。
闭包的函数体部分由关键字`in`引入。该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。
由于这个闭包的函数体部分如此短,以至于可以将其改写成一行代码:
```swift
reversed = names.sort( { (s1: String, s2: String) -> Bool in return s1 > s2 } )
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )
```
该例中`sort(_:)`方法的整体调用保持不变,一对圆括号仍然包裹住了方法的整个参数。然而,参数现在变成了内联闭包。
该例中 `sorted(by:)` 方法的整体调用保持不变,一对圆括号仍然包裹住了方法的整个参数。然而,参数现在变成了内联闭包。
<a name="inferring_type_from_context"></a>
### 根据上下文推断类型Inferring Type From Context
因为排序闭包函数是作为`sort(_:)`方法的参数传入的Swift 可以推断其参数和返回值的类型。`sort(_:)`方法被一个字符串数组调用,因此其参数必须是`(String, String) -> Bool`类型的函数。这意味着`(String, String)``Bool`类型并不需要作为闭包表达式定义的一部分。因为所有的类型都可以被正确推断,返回箭头(`->`)和围绕在参数周围的括号也可以被省略:
因为排序闭包函数是作为 `sorted(by:)` 方法的参数传入的Swift 可以推断其参数和返回值的类型。`sorted(by:)` 方法被一个字符串数组调用,因此其参数必须是 `(String, String) -> Bool` 类型的函数。这意味着 `(String, String)``Bool` 类型并不需要作为闭包表达式定义的一部分。因为所有的类型都可以被正确推断,返回箭头(`->`)和围绕在参数周围的括号也可以被省略:
```swift
reversed = names.sort( { s1, s2 in return s1 > s2 } )
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
```
实际上任何情况下,通过内联闭包表达式构造的闭包作为参数传递给函数或方法时,都可以推断出闭包的参数和返回值类型。
这意味着闭包作为函数或者方法的参数时,您几乎不需要利用完整格式构造内联闭包。
实际上,通过内联闭包表达式构造的闭包作为参数传递给函数或方法时,总是能够推断出闭包的参数和返回值类型。这意味着闭包作为函数或者方法的参数时,你几乎不需要利用完整格式构造内联闭包。
尽管如此,仍然可以明确写出有着完整格式的闭包。如果完整格式的闭包能够提高代码的可读性,则可以采用完整格式的闭包。而在`sort(_:)`方法这个例子里,闭包的目的就是排序。由于这个闭包是为了处理字符串数组的排序,因此读者能够推测出这个闭包是用于字符串处理的。
尽管如此,仍然可以明确写出有着完整格式的闭包。如果完整格式的闭包能够提高代码的可读性,则我们更鼓励采用完整格式的闭包。而在 `sorted(by:)` 方法这个例子里,显然闭包的目的就是排序。由于这个闭包是为了处理字符串数组的排序,因此读者能够推测出这个闭包是用于字符串处理的。
<a name="implicit_returns_from_single_expression_closures"></a>
### 单表达式闭包隐式返回Implicit Return From Single-Expression Clossures
### 单表达式闭包隐式返回Implicit Returns From Single-Expression Closures
单行表达式闭包可以通过省略`return`关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:
单行表达式闭包可以通过省略 `return` 关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:
```swift
reversed = names.sort( { s1, s2 in s1 > s2 } )
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
```
在这个例子中,`sort(_:)`方法的第二个参数函数类型明确了闭包必须返回一个`Bool`类型值。因为闭包函数体只包含了一个单一表达式(`s1 > s2`),该表达式返回`Bool`类型值,因此这里没有歧义,`return`关键字可以省略。
在这个例子中,`sorted(by:)` 方法的数类型明确了闭包必须返回一个 `Bool` 类型值。因为闭包函数体只包含了一个单一表达式(`s1 > s2`),该表达式返回 `Bool` 类型值,因此这里没有歧义,`return` 关键字可以省略。
<a name="shorthand_argument_names"></a>
### 参数名称缩写Shorthand Argument Names
Swift 自动为内联闭包提供了参数名称缩写功能,可以直接通过`$0``$1``$2`来顺序调用闭包的参数,以此类推。
Swift 自动为内联闭包提供了参数名称缩写功能,可以直接通过 `$0``$1``$2` 来顺序调用闭包的参数,以此类推。
如果在闭包表达式中使用参数名称缩写,可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。`in`关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
如果在闭包表达式中使用参数名称缩写,可以在闭包定义中省略参数列表,并且对应参数名称缩写的类型会通过函数类型进行推断。`in`关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
```swift
reversed = names.sort( { $0 > $1 } )
reversedNames = names.sorted(by: { $0 > $1 } )
```
在这个例子中,`$0``$1`表示闭包中第一个和第二个`String`类型的参数。
在这个例子中,`$0``$1`表示闭包中第一个和第二个 `String` 类型的参数。
<a name="operator_functions"></a>
### 运算符函数Operator Functions
<a name="operator_methods"></a>
### 运算符方法Operator Methods
实际上还有一种更简短的方式来写上面例子中的闭包表达式。Swift 的`String`类型定义了关于大于号(`>`)的字符串实现,其作为一个函数接受两个`String`类型的参数并返回`Bool`类型的值。而这正好与`sort(_:)`方法的第二个参数需要的函数类型相符合。因此,可以简单地传递一个大于号Swift 可以自动推断出想使用大于号的字符串函数实现:
实际上还有一种更简短的方式来写上面例子中的闭包表达式。Swift 的 `String` 类型定义了关于大于号(`>`)的字符串实现,其作为一个函数接受两个 `String` 类型的参数并返回 `Bool` 类型的值。而这正好与 `sorted(by:)` 方法的参数需要的函数类型相符合。因此,可以简单地传递一个大于号Swift 可以自动推断出想使用大于号的字符串函数实现:
```swift
reversed = names.sort(>)
reversedNames = names.sorted(by: >)
```
更多关于运算符表达式的内容请查看[运算符函数](./25_Advanced_Operators.html#operator_functions)。
更多关于运算符方法的内容请查看[运算符方法](./25_Advanced_Operators.html#operator_methods)。
<a name="trailing_closures"></a>
## 尾随闭包Trailing Closures
如果需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用*尾随闭包*来增强函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用:
如果需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用*尾随闭包*来增强函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。在使用尾随闭包时,你不用写出它的参数标签
```swift
func someFunctionThatTakesAClosure(closure: () -> Void) {
@ -171,7 +176,7 @@ func someFunctionThatTakesAClosure(closure: () -> Void) {
}
// 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure({
someFunctionThatTakesAClosure(closure: {
// 闭包主体部分
})
@ -181,23 +186,23 @@ someFunctionThatTakesAClosure() {
}
```
在[闭包表达式语法](#closure_expression_syntax)一节中作为`sort(_:)`方法参数的字符串排序闭包可以改写为:
在[闭包表达式语法](#closure_expression_syntax)一节中作为 `sorted(by:)` 方法参数的字符串排序闭包可以改写为:
```swift
reversed = names.sort() { $0 > $1 }
reversedNames = names.sorted() { $0 > $1 }
```
如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,甚至可以把`()`省略掉:
如果闭包表达式是函数或方法的唯一参数,则当你使用尾随闭包时,甚至可以把 `()` 省略掉:
```swift
reversed = names.sort { $0 > $1 }
reversedNames = names.sorted { $0 > $1 }
```
当闭包非常长以至于不能在一行中进行书写时尾随闭包变得非常有用。举例来说Swift 的`Array`类型有一个`map(_:)`方法,获取一个闭包表达式作为其唯一参数。该闭包函数会为数组中的每一个元素调用一次,并返回该元素所映射的值。具体的映射方式和返回值类型由闭包来指定。
当闭包非常长以至于不能在一行中进行书写时尾随闭包变得非常有用。举例来说Swift 的 `Array` 类型有一个 `map(_:)` 方法,这个方法获取一个闭包表达式作为其唯一参数。该闭包函数会为数组中的每一个元素调用一次,并返回该元素所映射的值。具体的映射方式和返回值类型由闭包来指定。
当提供给数组的闭包应用于每个数组元素后,`map(_:)`方法将返回一个新的数组,数组中包含了与原数组中的元素一一对应的映射后的值。
当提供给数组的闭包应用于每个数组元素后,`map(_:)` 方法将返回一个新的数组,数组中包含了与原数组中的元素一一对应的映射后的值。
下例介绍了如何在`map(_:)`方法中使用尾随闭包将`Int`类型数组`[16, 58, 510]`转换为包含对应`String`类型的值的数组`["OneSix", "FiveEight", "FiveOneZero"]`
下例介绍了如何在 `map(_:)` 方法中使用尾随闭包将 `Int` 类型数组 `[16, 58, 510]` 转换为包含对应 `String` 类型的值的数组`["OneSix", "FiveEight", "FiveOneZero"]`
```swift
let digitNames = [
@ -207,91 +212,90 @@ let digitNames = [
let numbers = [16, 58, 510]
```
如上代码创建了一个数位和它们英文版本名字相映射的字典。同时还定义了一个准备转换为字符串数组的整型数组。
如上代码创建了一个整型数位和它们英文版本名字相映射的字典。同时还定义了一个准备转换为字符串数组的整型数组。
现在可以通过传递一个尾随闭包给`numbers``map(_:)`方法来创建对应的字符串版本数组:
现在可以通过传递一个尾随闭包给 `numbers` 数组的 `map(_:)` 方法来创建对应的字符串版本数组:
```swift
let strings = numbers.map {
(var number) -> String in
(number) -> String in
var number = number
var output = ""
while number > 0 {
repeat {
output = digitNames[number % 10]! + output
number /= 10
}
} while number > 0
return output
}
// strings 常量被推断为字符串类型数组,即 [String]
// 其值为 ["OneSix", "FiveEight", "FiveOneZero"]
```
`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` 字典获取所映射的字符串。这个闭包能够用于创建任意正整数的字符串表示。
> 注意
> 字典`digitNames`下标后跟着一个叹号(`!`因为字典下标返回一个可选值optional value表明该键不存在时会查找失败。在上例中由于可以确定`number % 10`总是`digitNames`字典的有效下标,因此叹号可以用于强制解包 (force-unwrap) 存储在下标的可选类型的返回值中的`String`类型的值。
> 注意
> 字典 `digitNames` 下标后跟着一个叹号(`!`因为字典下标返回一个可选值optional value表明该键不存在时会查找失败。在上例中由于可以确定 `number % 10` 总是 `digitNames` 字典的有效下标,因此叹号可以用于强制解包 (force-unwrap) 存储在下标的可选类型的返回值中的`String`类型的值。
`digitNames`字典中获取的字符串被添加到`output`前部,逆序建立了一个字符串版本的数字。(在表达式`number % 10`中,如果`number``16`,则返回`6``58`返回`8``510`返回`0`。)
`digitNames` 字典中获取的字符串被添加到 `output` 的*前部*,逆序建立了一个字符串版本的数字。(在表达式 `number % 10` 中,如果 `number``16`,则返回 `6``58` 返回 `8``510` 返回 `0`。)
`number`变量之后除以`10`。因为其是整数,在计算过程中未除尽部分被忽略。因此`16`变成了`1``58`变成了`5``510`变成了`51`
`number` 变量之后除以 `10`。因为其是整数,在计算过程中未除尽部分被忽略。因此 `16` 变成了 `1``58` 变成了 `5``510` 变成了 `51`
整个过程重复进行,直到`number /= 10``0`,这时闭包会将字符串`output`返回,而`map(_:)`方法则会将字符串添加到映射数组中。
整个过程重复进行,直到 `number /= 10``0`,这时闭包会将字符串 `output` 返回,而 `map(_:)` 方法则会将字符串添加到映射数组中。
在上面的例子中,通过尾随闭包语法,优雅地在函数后封装了闭包的具体功能,而不再需要将整个闭包包裹在`map(_:)`方法的括号内。
在上面的例子中,通过尾随闭包语法,优雅地在函数后封装了闭包的具体功能,而不再需要将整个闭包包裹在 `map(_:)` 方法的括号内。
<a name="capturing_values"></a>
## 捕获Capturing Values
## 捕获Capturing Values
闭包可以在其被定义的上下文中*捕获*常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
Swift 中,可以捕获值的闭包的最简单形式是嵌套函数,也就是定义在其他函数的函数体内的函数。嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。
举个例子,这有一个叫做`makeIncrementor`的函数,其包含了一个叫做`incrementor`的嵌套函数。嵌套函数`incrementor()`从上下文中捕获了两个值,`runningTotal``amount`。捕获这些值之后,`makeIncrementor``incrementor`作为闭包返回。每次调用`incrementor`时,其会以`amount`作为增量增加`runningTotal`的值。
举个例子,这有一个叫做 `makeIncrementor` 的函数,其包含了一个叫做 `incrementor` 的嵌套函数。嵌套函数 `incrementor()` 从上下文中捕获了两个值,`runningTotal``amount`。捕获这些值之后,`makeIncrementor``incrementor` 作为闭包返回。每次调用 `incrementor` 时,其会以 `amount` 作为增量增加 `runningTotal` 的值。
```swift
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() -> Int {
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
return incrementer
}
```
`makeIncrementor`返回类型为`() -> Int`。这意味着其返回的是一个函数,而不是一个简单类型的值。该函数在每次调用时不接受参数,只返回一个`Int`类型的值。关于函数返回其他函数的内容,请查看[函数类型作为返回类型](./06_Functions.html#function_types_as_return_types)。
`makeIncrementor` 返回类型为 `() -> Int`。这意味着其返回的是一个*函数*,而一个简单类型的值。该函数在每次调用时不接受参数,只返回一个 `Int` 类型的值。关于函数返回其他函数的内容,请查看[函数类型作为返回类型](./06_Functions.html#function_types_as_return_types)。
`makeIncrementer(forIncrement:)`函数定义了一个初始值为`0`的整型变量`runningTotal`,用来存储当前跑步总数。该值通过`incrementor`返回。
`makeIncrementer(forIncrement:)` 函数定义了一个初始值为 `0` 的整型变量 `runningTotal`,用来存储当前总计数值。该值`incrementor`返回
`makeIncrementer(forIncrement:)`有一个`Int`类型的参数,其外部参数名为`forIncrement`,内部参数名为`amount`,该参数表示每次`incrementor`被调用时`runningTotal`将要增加的量。
`makeIncrementer(forIncrement:)` 有一个 `Int` 类型的参数,其外部参数名为 `forIncrement`,内部参数名为 `amount`,该参数表示每次 `incrementor` 被调用时 `runningTotal` 将要增加的量。`makeIncrementer` 函数还定义了一个嵌套函数 `incrementor`,用来执行实际的增加操作。该函数简单地使 `runningTotal` 增加 `amount`,并将其返回。
嵌套函数`incrementor`用来执行实际的增加操作。该函数简单地使`runningTotal`增加`amount`,并将其返回。
如果我们单独看这个函数,会发现看上去不同寻常:
如果我们单独考虑嵌套函数 `incrementer()`,会发现它有些不同寻常:
```swift
func incrementor() -> Int {
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
```
`incrementer()`函数并没有任何参数,但是在函数体内访问了`runningTotal``amount`变量。这是因为它从外围函数捕获了`runningTotal``amount`变量的引用。捕获引用保证了`runningTotal``amount`变量在调用完`makeIncrementer`后不会消失,并且保证了在下一次执行`incrementer`函数时,`runningTotal`依旧存在。
`incrementer()` 函数并没有任何参数,但是在函数体内访问了 `runningTotal``amount` 变量。这是因为它从外围函数捕获了 `runningTotal``amount` 变量的*引用*。捕获引用保证了 `runningTotal``amount` 变量在调用完 `makeIncrementer` 后不会消失,并且保证了在下一次执行 `incrementer` 函数时,`runningTotal` 依旧存在。
> 注意
> 为了优化,如果一个值是不可变的Swift 可能会改为捕获并保存一份对值的拷贝。
> 注意
> 为了优化,如果一个值不会被闭包改变,或者在闭包创建后不会改变Swift 可能会改为捕获并保存一份对值的拷贝。
> Swift 也会负责被捕获变量的所有内存管理工作,包括释放不再需要的变量。
下面是一个使用`makeIncrementor`的例子:
下面是一个使用 `makeIncrementor` 的例子:
```swift
let incrementByTen = makeIncrementor(forIncrement: 10)
```
该例子定义了一个叫做`incrementByTen`的常量,该常量指向一个每次调用会将`runningTotal`变量增加`10``incrementor`函数。调用这个函数多次可以得到以下结果:
该例子定义了一个叫做 `incrementByTen` 的常量,该常量指向一个每次调用会将`runningTotal` 变量增加 `10``incrementor` 函数。调用这个函数多次可以得到以下结果:
```swift
incrementByTen()
@ -302,7 +306,7 @@ incrementByTen()
// 返回的值为30
```
如果创建了另一个`incrementor`,它会有属于自己的一个全新、独立的`runningTotal`变量的引用
如果创建了另一个 `incrementor`,它会有属于自己的引用,指向一个全新、独立的 `runningTotal` 变量:
```swift
let incrementBySeven = makeIncrementor(forIncrement: 7)
@ -310,7 +314,7 @@ incrementBySeven()
// 返回的值为7
```
再次调用原来的`incrementByTen`会在原来的变量`runningTotal`上继续增加值,该变量和`incrementBySeven`中捕获的变量没有任何联系:
再次调用原来的 `incrementByTen` 会继续增加它自己的 `runningTotal` 变量,该变量和 `incrementBySeven` 中捕获的变量没有任何联系:
```swift
incrementByTen()
@ -318,16 +322,16 @@ incrementByTen()
```
> 注意
> 如果将闭包赋值给一个类实例的属性,并且该闭包通过访问该实例或其成员而捕获了该实例,您将创建一个在闭包和该实例间循环强引用。Swift 使用捕获列表来打破这种循环强引用。更多信息,请参考[闭包引起的循环强引用](./16_Automatic_Reference_Counting.html#strong_reference_cycles_for_closures)。
> 如果将闭包赋值给一个类实例的属性,并且该闭包通过访问该实例或其成员而捕获了该实例,你将在闭包和该实例间创建一个循环强引用。Swift 使用捕获列表来打破这种循环强引用。更多信息,请参考[闭包引起的循环强引用](./16_Automatic_Reference_Counting.html#strong_reference_cycles_for_closures)。
<a name="closures_are_reference_types"></a>
## 闭包是引用类型Closures Are Reference Types
上面的例子中,`incrementBySeven``incrementByTen`是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量的值。这是因为函数和闭包都是*引用类型*。
上面的例子中,`incrementBySeven``incrementByTen`是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量的值。这是因为函数和闭包都是*引用类型*。
无论将函数或闭包赋值给一个常量还是变量,实际上都是将常量或变量的值设置为对应函数或闭包的引用。上面的例子中,指向闭包的引用`incrementByTen`是一个常量,而并非闭包内容本身。
无论将函数或闭包赋值给一个常量还是变量,实际上都是将常量或变量的值设置为对应函数或闭包的*引用*。上面的例子中,指向闭包的引用 `incrementByTen` 是一个常量,而并非闭包内容本身。
这也意味着如果将闭包赋值给了两个不同的常量或变量,两个值都会指向同一个闭包:
这也意味着如果将闭包赋值给了两个不同的常量或变量,两个值都会指向同一个闭包:
```swift
let alsoIncrementByTen = incrementByTen
@ -335,121 +339,118 @@ alsoIncrementByTen()
// 返回的值为50
```
<a name="nonescaping_closures"></a>
## 逃逸闭包(Nonescaping Closures)
<a name="escaping_closures"></a>
## 逃逸闭包(Escaping Closures)
当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中*逃逸*。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注`@noescape`,用来指明这个闭包是允许“逃逸”出这个函数的。将闭包标注`@noescape`能使编译器知道这个闭包的生命周期(译者注:闭包只能在函数体中被执行,不能脱离函数体执行,所以编译器明确知道运行时的上下文),从而可以进行一些比较激进的优化。
当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中*逃逸*。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注 `@escaping`,用来指明这个闭包是允许“逃逸”出这个函数的。
```swift
func someFunctionWithNoescapeClosure(@noescape closure: () -> Void) {
closure()
}
```
一种能使闭包“逃逸”出函数的方法是,将这个闭包保存在一个函数外部定义的变量中。举个例子,很多启动异步操作的函数接受一个闭包参数作为 completion handler。这类函数会在异步操作开始之后立刻返回但是闭包直到异步操作结束后才会被调用。在这种情况下闭包需要“逃逸”出函数因为闭包需要在函数返回之后被调用。例如
举个例子,`sort(_:)`方法接受一个用来进行元素比较的闭包作为参数。这个参数被标注了`@noescape`,因为它确保自己在排序结束之后就没用了。
一种能使闭包“逃逸”出函数的方法是,将这个闭包保存在一个函数外部定义的变量中。举个例子,很多启动异步操作的函数接受一个闭包参数作为 completion handler。这类函数会在异步操作开始之后立刻返回但是闭包直到异步操作结束后才会被调用。在这种情况下闭包需要“逃逸”出函数因为闭包需要在函数返回之后被调用。例如
```swift
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: () -> Void) {
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
```
`someFunctionWithEscapingClosure(_:)`函数接受一个闭包作为参数,该闭包被添加到一个函数外定义的数组中。如果你试图将这个参数标注为`@noescape`,你将会获得一个编译错误。
`someFunctionWithEscapingClosure(_:)` 函数接受一个闭包作为参数,该闭包被添加到一个函数外定义的数组中。如果你将这个参数标记为 `@escaping`,就会得到一个编译错误。
将一个闭包标记为 `@escaping` 意味着你必须在闭包中显式地引用 `self`。比如说,在下面的代码中,传递到 `someFunctionWithEscapingClosure(_:)` 中的闭包是一个逃逸闭包,这意味着它需要显式地引用 `self`。相对的,传递到 `someFunctionWithNonescapingClosure(_:)` 中的闭包是一个非逃逸闭包,这意味着它可以隐式引用 `self`
将闭包标注为`@noescape`使你能在闭包中隐式地引用`self`
```swift
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNoescapeClosure { x = 200 }
someFunctionWithNonescapingClosure { x = 200 }
}
}
let instance = SomeClass()
instance.doSomething()
print(instance.x)
// prints "200"
// 打印出 "200"
completionHandlers.first?()
print(instance.x)
// prints "100"
// 打印出 "100"
```
<a name="autoclosures"></a>
## 自动闭包Autoclosures
*自动闭包*是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够用一个普通的表达式来代替显式的闭包,从而省略闭包的花括号
*自动闭包*是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够省略闭包的花括号,用一个普通的表达式来代替显式的闭包。
我们经常会调用一个接受闭包作为参数的函数,但是很少实现那样的函数。举个例子来说,`assert(condition:message:file:line:)`函数接受闭包作为它的`condition`参数和`message`参数;它的`condition`参数仅会在 debug 模式下被求值,它的`message`参数仅当`condition`参数为`false`时被计算求值。
我们经常会*调用*采用自动闭包的函数,但是很少去*实现*这样的函数。举个例子来说,`assert(condition:message:file:line:)` 函数接受自动闭包作为它的 `condition` 参数和 `message` 参数;它的 `condition` 参数仅会在 debug 模式下被求值,它的 `message` 参数仅当 `condition` 参数为 `false` 时被计算求值。
自动闭包让你能够延迟求值,因为代码段不会被执行直到你调用这个闭包。延迟求值对于那些有副作用Side Effect代价昂贵的代码来说是很有益处的,因为你能控制代码什么时候执行。下面的代码展示了闭包如何延时求值。
自动闭包让你能够延迟求值,因为直到你调用这个闭包,代码段才会被执行。延迟求值对于那些有副作用Side Effect高计算成本的代码来说是很有益处的,因为它使得你能控制代码的执行时机。下面的代码展示了闭包如何延时求值。
```swift
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// prints "5"
let customerProvider = { customersInLine.removeAtIndex(0) }
// 打印出 "5"
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// prints "5"
// 打印出 "5"
print("Now serving \(customerProvider())!")
// prints "Now serving Chris!"
// Prints "Now serving Chris!"
print(customersInLine.count)
// prints "4"
// 打印出 "4"
```
尽管在闭包的代码中,`customersInLine`的第一个元素被移除了,不过在闭包被调用之前,这个元素是不会被移除的。如果这个闭包永远不被调用,那么在闭包里面的表达式将永远不会执行,那意味着列表中的元素永远不会被移除。请注意,`customerProvider`的类型不是`String`,而是`() -> String`,一个没有参数且返回值为`String`的函数。
尽管在闭包的代码中,`customersInLine` 的第一个元素被移除了,不过在闭包被调用之前,这个元素是不会被移除的。如果这个闭包永远不被调用,那么在闭包里面的表达式将永远不会执行,那意味着列表中的元素永远不会被移除。请注意,`customerProvider` 的类型不是 `String`,而是 `() -> String`,一个没有参数且返回值为 `String` 的函数。
将闭包作为参数传递给函数时,你能获得同样的延时求值行为。
将闭包作为参数传递给函数时,你能获得同样的延时求值行为。
```swift
// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serveCustomer(customerProvider: () -> String) {
func serve(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serveCustomer( { customersInLine.removeAtIndex(0) } )
// prints "Now serving Alex!"
serve(customer: { customersInLine.remove(at: 0) } )
// 打印出 "Now serving Alex!"
```
`serveCustomer(_:)`接受一个返回顾客名字的显式的闭包。下面这个版本的`serveCustomer(_:)`完成了相同的操作,不过它并没有接受一个显式的闭包,而是通过将参数标记为`@autoclosure`来接收一个自动闭包。现在你可以将该函数当接受`String`类型参数的函数来调用。`customerProvider`参数将自动转化为一个闭包,因为该参数被标记了`@autoclosure`特性。
上面的 `serve(customer:)` 函数接受一个返回顾客名字的显式的闭包。下面这个版本的 `serve(customer:)` 完成了相同的操作,不过它并没有接受一个显式的闭包,而是通过将参数标记为 `@autoclosure` 来接收一个自动闭包。现在你可以将该函数当接受 `String` 类型参数(而非闭包)的函数来调用。`customerProvider` 参数将自动转化为一个闭包,因为该参数被标记了 `@autoclosure` 特性。
```swift
// customersInLine is ["Ewa", "Barry", "Daniella"]
func serveCustomer(@autoclosure customerProvider: () -> String) {
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serveCustomer(customersInLine.removeAtIndex(0))
// prints "Now serving Ewa!"
serve(customer: customersInLine.remove(at: 0))
// 打印出 "Now serving Ewa!"
```
> 注意
> 过度使用`autoclosures`会让你的代码变得难以理解。上下文和函数名应该能够清晰地表明求值是被延迟执行的。
> 注意
> 过度使用 `autoclosures` 会让你的代码变得难以理解。上下文和函数名应该能够清晰地表明求值是被延迟执行的。
`@autoclosure`特性暗含了`@noescape`特性,这个特性在[逃逸闭包](#nonescaping_closures)一节中有描述。如果你想让这个闭包可以“逃逸”,则应该使用`@autoclosure(escaping)`特性.
如果你想让一个自动闭包可以“逃逸”,则应该同时使用 `@autoclosure``@escaping` 属性。`@escaping` 属性的讲解见上面的[逃逸闭包](#escaping_closures)
```swift
// customersInLine is ["Barry", "Daniella"]
var customerProviders: [() -> String] = []
func collectCustomerProviders(@autoclosure(escaping) customerProvider: () -> String) {
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.removeAtIndex(0))
collectCustomerProviders(customersInLine.removeAtIndex(0))
collectCustomerProviders(customersInLine.remove(at: 0))
collectCustomerProviders(customersInLine.remove(at: 0))
print("Collected \(customerProviders.count) closures.")
// prints "Collected 2 closures."
// 打印出 "Collected 2 closures."
for customerProvider in customerProviders {
print("Now serving \(customerProvider())!")
}
// prints "Now serving Barry!"
// prints "Now serving Daniella!"
// 打印出 "Now serving Barry!"
// 打印出 "Now serving Daniella!"
```
在上面的代码中,`collectCustomerProviders(_:)`函数并没有调用传入的`customerProvider`闭包,而是将闭包追加到了`customerProviders`数组中。这个数组定义在函数作用域范围外,这意味着数组内的闭包将会在函数返回之后被调用。因此,`customerProvider`参数必须允许“逃逸”出函数作用域。
在上面的代码中,`collectCustomerProviders(_:)` 函数并没有调用传入的 `customerProvider` 闭包,而是将闭包追加到了 `customerProviders` 数组中。这个数组定义在函数作用域范围外,这意味着数组内的闭包能够在函数返回之后被调用。因此,`customerProvider` 参数必须允许“逃逸”出函数作用域。

View File

@ -10,7 +10,10 @@
> 2.1
> 翻译:[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>
## 递归枚举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)
}
}
// 计算 (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))
// 输出 "18"
```

View File

@ -10,6 +10,9 @@
> 2.1
> 校对:[shanks](http://codebuild.me)2015-10-29
> 2.2
> 校对:[SketchK](https://github.com/SketchK) 2016-05-13
本页包含内容:
- [类和结构体对比](#comparing_classes_and_structures)
@ -33,18 +36,18 @@ Swift 中类和结构体有很多共同点。共同处在于:
* 定义属性用于存储值
* 定义方法用于提供功能
* 定义附属脚本用于访问
* 定义下标操作使得可以通过下标语法来访问实例所包含的
* 定义构造器用于生成初始化值
* 通过扩展以增加默认实现的功能
* 实现协议以提供某种标准功能
更多信息请参见[属性](./10_Properties.html)[方法](./11_Methods.html)[下标脚本](./12_Subscripts.html)[构造过程](./14_Initialization.html)[扩展](./21_Extensions.html),和[协议](./22_Protocols.html)。
更多信息请参见[属性](./10_Properties.html)[方法](./11_Methods.html)[下标](./12_Subscripts.html)[构造过程](./14_Initialization.html)[扩展](./21_Extensions.html),和[协议](./22_Protocols.html)。
与结构体相比,类还有如下的附加功能:
* 继承允许一个类继承另一个类的特征
* 类型转换允许在运行时检查和解释一个类实例的类型
* 构器允许一个类实例释放任何其所被分配的资源
* 构器允许一个类实例释放任何其所被分配的资源
* 引用计数允许对一个类的多次引用
更多信息请参见[继承](./13_Inheritance.html)[类型转换](./19_Type_Casting.html)[析构过程](./15_Deinitialization.html),和[自动引用计数](./16_Automatic_Reference_Counting.html)。

View File

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

View File

@ -1,27 +1,34 @@
# 下标脚本Subscripts
# 下标Subscripts
-----------------
> 1.0
> 翻译:[siemenliu](https://github.com/siemenliu)
> 校对:[zq54zquan](https://github.com/zq54zquan)
> 2.0,2.1
> 翻译+校对:[shanks](http://codebuild.me)2015-10-29
> 2.0
> 翻译+校对:[shanks](http://codebuild.me)
> 2.1
> 翻译+校对:[shanks](http://codebuild.me)[Realank](https://github.com/Realank)
> 2.2
> 校对:[SketchK](https://github.com/SketchK) 2016-05-13
本页包含内容:
- [下标脚本语法](#subscript_syntax)
- [下标脚本用法](#subscript_usage)
- [下标脚本选项](#subscript_options)
- [下标语法](#subscript_syntax)
- [下标用法](#subscript_usage)
- [下标选项](#subscript_options)
*下标脚本* 可以定义在类(Class、结构体structure和枚举enumeration是访问集合collection列表list或序列sequence中元素的快捷方式。可以使用下标脚本的索引设置和获取值,不需要再调用对应的存取方法。举例来说,用下标脚本访问一个`Array`实例中的元素可以写作`someArray[index]`,访问`Dictionary`实例中的元素可以写作`someDictionary[key]`
*下标* subscripts可以定义在类(class、结构体structure和枚举enumeration是访问集合collection列表list或序列sequence中元素的快捷方式。可以使用下标的索引设置和获取值,不需要再调用对应的存取方法。举例来说,用下标访问一个`Array`实例中的元素可以写作`someArray[index]`,访问`Dictionary`实例中的元素可以写作`someDictionary[key]`
一个类型可以定义多个下标脚本,通过不同索引类型进行重载。下标脚本不限于一维,你可以定义具有多个入参的下标脚本满足自定义类型的需求。
一个类型可以定义多个下标,通过不同索引类型进行重载。下标不限于一维,你可以定义具有多个入参的下标满足自定义类型的需求。
<a name="subscript_syntax"></a>
## 下标脚本语法
## 下标语法
下标脚本允许你通过在实例名称后面的方括号中传入一个或者多个索引值来对实例进行存取。语法类似于实例方法语法和计算型属性语法的混合。与定义实例方法类似,定义下标脚本使用`subscript`关键字,指定一个或多个入参和返回类型与实例方法不同的是,下标脚本可以设定为读写或只读。这种行为由 getter 和 setter 实现,有点类似计算型属性:
下标允许你通过在实例名称后面的方括号中传入一个或者多个索引值来对实例进行存取。语法类似于实例方法语法和计算型属性语法的混合。与定义实例方法类似,定义下标使用`subscript`关键字,指定一个或多个入参和返回类型与实例方法不同的是,下标可以设定为读写或只读。这种行为由 getter 和 setter 实现,有点类似计算型属性:
```swift
subscript(index: Int) -> Int {
@ -35,9 +42,9 @@ subscript(index: Int) -> Int {
}
```
`newValue`的类型和下标脚本的返回类型相同。如同计算型属性,可以不指定 setter 的参数(`newValue`。如果不指定参数setter 会提供一个名为`newValue`的默认参数。
`newValue`的类型和下标的返回类型相同。如同计算型属性,可以不指定 setter 的参数(`newValue`。如果不指定参数setter 会提供一个名为`newValue`的默认参数。
如同只读计算型属性,可以省略只读下标脚本`get`关键字:
如同只读计算型属性,可以省略只读下标的`get`关键字:
```swift
subscript(index: Int) -> Int {
@ -45,7 +52,7 @@ subscript(index: Int) -> Int {
}
```
下面代码演示了只读下标脚本的实现,这里定义了一个`TimesTable`结构体,用来表示传入整数的乘法表:
下面代码演示了只读下标的实现,这里定义了一个`TimesTable`结构体,用来表示传入整数的乘法表:
```swift
struct TimesTable {
@ -61,38 +68,38 @@ print("six times three is \(threeTimesTable[6])")
在上例中,创建了一个`TimesTable`实例,用来表示整数`3`的乘法表。数值`3`被传递给结构体的构造函数,作为实例成员`multiplier`的值。
你可以通过下标脚本访问`threeTimesTable`实例,例如上面演示的`threeTimesTable[6]`。这条语句查询了`3`的乘法表中的第六个元素,返回`3``6`倍即`18`
你可以通过下标访问`threeTimesTable`实例,例如上面演示的`threeTimesTable[6]`。这条语句查询了`3`的乘法表中的第六个元素,返回`3``6`倍即`18`
> 注意
> `TimesTable`例子基于一个固定的数学公式,对`threeTimesTable[someIndex]`进行赋值操作并不合适,因此下标脚本定义为只读的。
> `TimesTable`例子基于一个固定的数学公式,对`threeTimesTable[someIndex]`进行赋值操作并不合适,因此下标定义为只读的。
<a name="subscript_usage"></a>
## 下标脚本用法
## 下标用法
下标脚本的确切含义取决于使用场景。下标脚本通常作为访问集合collection列表list或序列sequence中元素的快捷方式。你可以针对自己特定的类或结构体的功能来自由地以最恰当的方式实现下标脚本
下标的确切含义取决于使用场景。下标通常作为访问集合collection列表list或序列sequence中元素的快捷方式。你可以针对自己特定的类或结构体的功能来自由地以最恰当的方式实现下标。
例如Swift 的`Dictionary`类型实现下标脚本用于对其实例中储存的值进行存取操作。为字典设值时,在下标脚本中使用和字典的键类型相同的键,并把一个和字典的值类型相同的值赋给这个下标脚本
例如Swift 的`Dictionary`类型实现下标用于对其实例中储存的值进行存取操作。为字典设值时,在下标中使用和字典的键类型相同的键,并把一个和字典的值类型相同的值赋给这个下标:
```swift
var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
numberOfLegs["bird"] = 2
```
上例定义一个名为`numberOfLegs`的变量,并用一个包含三对键值的字典字面量初始化它。`numberOfLegs`字典的类型被推断为`[String: Int]`。字典创建完成后,该例子通过下标脚本`String`类型的键`bird``Int`类型的值`2`添加到字典中。
上例定义一个名为`numberOfLegs`的变量,并用一个包含三对键值的字典字面量初始化它。`numberOfLegs`字典的类型被推断为`[String: Int]`。字典创建完成后,该例子通过下标将`String`类型的键`bird``Int`类型的值`2`添加到字典中。
更多关于`Dictionary`下标脚本的信息请参考[读取和修改字典](./04_Collection_Types.html#accessing_and_modifying_a_dictionary)
更多关于`Dictionary`下标的信息请参考[读取和修改字典](./04_Collection_Types.html#accessing_and_modifying_a_dictionary)
> 注意
> Swift 的`Dictionary`类型的下标脚本接受并返回可选类型的值。上例中的`numberOfLegs`字典通过下标脚本返回的是一个`Int?`或者说“可选的int”。`Dictionary`类型之所以如此实现下标脚本,是因为不是每个键都有个对应的值,同时这也提供了一种通过键删除对应值的方式,只需将键对应的值赋值为`nil`即可。
> Swift 的`Dictionary`类型的下标接受并返回可选类型的值。上例中的`numberOfLegs`字典通过下标返回的是一个`Int?`或者说“可选的int”。`Dictionary`类型之所以如此实现下标,是因为不是每个键都有个对应的值,同时这也提供了一种通过键删除对应值的方式,只需将键对应的值赋值为`nil`即可。
<a name="subscript_options"></a>
## 下标脚本选项
## 下标选项
下标脚本可以接受任意数量的入参,并且这些入参可以是任意类型。下标脚本的返回值也可以是任意类型。下标脚本可以使用变量参数和可变参数,但不能使用输入输出参数,也不能给参数设置默认值。
下标可以接受任意数量的入参,并且这些入参可以是任意类型。下标的返回值也可以是任意类型。下标可以使用变量参数和可变参数,但不能使用输入输出参数,也不能给参数设置默认值。
一个类或结构体可以根据自身需要提供多个下标脚本实现,使用下标脚本时将通过入参的数量和类型进行区分,自动匹配合适的下标脚本,这就是*下标脚本的重载*。
一个类或结构体可以根据自身需要提供多个下标实现,使用下标时将通过入参的数量和类型进行区分,自动匹配合适的下标,这就是*下标的重载*。
虽然接受单一入参的下标脚本是最常见的,但也可以根据情况定义接受多个入参的下标脚本。例如下例定义了一个`Matrix`结构体,用于表示一个`Double`类型的二维矩阵。`Matrix`结构体的下标脚本接受两个整型参数:
虽然接受单一入参的下标是最常见的,但也可以根据情况定义接受多个入参的下标。例如下例定义了一个`Matrix`结构体,用于表示一个`Double`类型的二维矩阵。`Matrix`结构体的下标接受两个整型参数:
```swift
struct Matrix {
@ -131,18 +138,18 @@ var matrix = Matrix(rows: 2, columns: 2)
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/subscriptMatrix01_2x.png)
`row``column`的值传入下标脚本来为矩阵设值,下标脚本的入参使用逗号分隔:
`row``column`的值传入下标来为矩阵设值,下标的入参使用逗号分隔:
```swift
matrix[0, 1] = 1.5
matrix[1, 0] = 3.2
```
上面两条语句分别调用下标脚本的 setter 将矩阵右上角位置(即`row``0``column``1`的位置)的值设置为`1.5`,将矩阵左下角位置(即`row``1``column``0`的位置)的值设置为`3.2`
上面两条语句分别调用下标的 setter 将矩阵右上角位置(即`row``0``column``1`的位置)的值设置为`1.5`,将矩阵左下角位置(即`row``1``column``0`的位置)的值设置为`3.2`
![](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/subscriptMatrix02_2x.png)
`Matrix`下标脚本的 getter 和 setter 中都含有断言,用来检查下标脚本入参`row``column`的值是否有效。为了方便进行断言,`Matrix`包含了一个名为`indexIsValidForRow(_:column:)`的便利方法,用来检查入参`row``column`的值是否在矩阵范围内:
`Matrix`下标的 getter 和 setter 中都含有断言,用来检查下标入参`row``column`的值是否有效。为了方便进行断言,`Matrix`包含了一个名为`indexIsValidForRow(_:column:)`的便利方法,用来检查入参`row``column`的值是否在矩阵范围内:
```swift
func indexIsValidForRow(row: Int, column: Int) -> Bool {
@ -150,7 +157,7 @@ func indexIsValidForRow(row: Int, column: Int) -> Bool {
}
```
断言在下标脚本越界时触发:
断言在下标越界时触发:
```swift
let someValue = matrix[2, 2]

View File

@ -6,25 +6,28 @@
> 校对:[menlongsheng](https://github.com/menlongsheng)
> 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)
- [重写Overriding](#overriding)
- [防止重写Preventing Overrides](#preventing_overrides)
一个类可以*继承inherit*另一个类的方法methods属性properties和其它特性。当一个类继承其它类时继承类叫*子类subclass*,被继承类叫*超类或父类superclass*。在 Swift 中,继承是区分「类」与其它类型的一个基本特征。
在 Swift 中,类可以调用和访问超类的方法,属性和下标脚本subscripts并且可以重写override这些方法属性和下标脚本来优化或修改它们的行为。Swift 会检查你的重写定义在超类中是否有匹配的定义,以此确保你的重写行为是正确的。
在 Swift 中类可以调用和访问超类的方法属性和下标subscripts并且可以重写override这些方法属性和下标来优化或修改它们的行为。Swift 会检查你的重写定义在超类中是否有匹配的定义,以此确保你的重写行为是正确的。
可以为类中继承来的属性添加属性观察器property observers这样一来当属性值改变时类就会被通知到。可以为任何属性添加属性观察器无论它原本被定义为存储型属性stored property还是计算型属性computed property
<a name="defining_a_base_class"></a>
## 定义一个基类Base class
## 定义一个基类(Defining a Base Class
不继承于其它类的类,称之为*基类base calss*。
不继承于其它类的类,称之为*基类base class*。
> 注意
Swift 中的类并不是从一个通用的基类继承而来。如果你不为你定义的类指定一个超类的话,这个类就自动成为基类。
@ -124,21 +127,21 @@ print("Tandem: \(tandem.description)")
<a name="overriding"></a>
## 重写Overriding
子类可以为继承来的实例方法instance method类方法class method实例属性instance property或下标脚本subscript提供自己定制的实现implementation。我们把这种行为叫*重写overriding*。
子类可以为继承来的实例方法instance method类方法class method实例属性instance property或下标subscript提供自己定制的实现implementation。我们把这种行为叫*重写overriding*。
如果要重写某个特性,你需要在重写定义的前面加上`override`关键字。这么做,你就表明了你是想提供一个重写版本,而非错误地提供了一个相同的定义。意外的重写行为可能会导致不可预知的错误,任何缺少`override`关键字的重写都会在编译时被诊断为错误。
`override`关键字会提醒 Swift 编译器去检查该类的超类(或其中一个父类)是否有匹配重写版本的声明。这个检查可以确保你的重写定义是正确的。
### 访问超类的方法,属性及下标脚本
### 访问超类的方法,属性及下标
当你在子类中重写超类的方法,属性或下标脚本时,有时在你的重写版本中使用已经存在的超类实现会大有裨益。比如,你可以完善已有实现的行为,或在一个继承来的变量中存储一个修改过的值。
当你在子类中重写超类的方法,属性或下标时,有时在你的重写版本中使用已经存在的超类实现会大有裨益。比如,你可以完善已有实现的行为,或在一个继承来的变量中存储一个修改过的值。
在合适的地方,你可以通过使用`super`前缀来访问超类版本的方法,属性或下标脚本
在合适的地方,你可以通过使用`super`前缀来访问超类版本的方法,属性或下标:
* 在方法`someMethod()`的重写实现中,可以通过`super.someMethod()`来调用超类版本的`someMethod()`方法。
* 在属性`someProperty`的 getter 或 setter 的重写实现中,可以通过`super.someProperty`来访问超类版本的`someProperty`属性。
* 在下标脚本的重写实现中,可以通过`super[someIndex]`来访问超类版本中的相同下标脚本
* 在下标的重写实现中,可以通过`super[someIndex]`来访问超类版本中的相同下标。
### 重写方法
@ -231,8 +234,8 @@ print("AutomaticCar: \(automatic.description)")
<a name="preventing_overrides"></a>
## 防止重写
你可以通过把方法,属性或下标脚本标记为*`final`*来防止它们被重写,只需要在声明关键字前加上`final`修饰符即可(例如:`final var``final func``final class func`,以及`final subscript`)。
你可以通过把方法,属性或下标标记为*`final`*来防止它们被重写,只需要在声明关键字前加上`final`修饰符即可(例如:`final var``final func``final class func`,以及`final subscript`)。
如果你重写了`final`方法,属性或下标脚本,在编译时会报错。在类扩展中的方法,属性或下标脚本也可以在扩展的定义里标记为 final 的。
如果你重写了带有`final`标记的方法,属性或下标,在编译时会报错。在类扩展中的方法,属性或下标也可以在扩展的定义里标记为 final 的。
你可以通过在关键字`class`前添加`final`修饰符(`final class`)来将整个类标记为 final 的。这样的类是不可被继承的,试图继承这样的类会导致编译报错。

View File

@ -9,8 +9,12 @@
> 翻译+校对:[chenmingbiao](https://github.com/chenmingbiao)
> 2.1
> 翻译:[Channe](https://github.com/Channe)
> 校对:[shanks](http://codebuild.me)2015-10-30
> 翻译:[Channe](https://github.com/Channe)[Realank](https://github.com/Realank)
> 校对:[shanks](http://codebuild.me)2016-1-23
> 2.2
> 翻译:[pmst](https://github.com/colourful987)
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-14
本页包含内容:
@ -118,7 +122,7 @@ let freezingPointOfWater = Celsius(fromKelvin: 273.15)
跟函数和方法参数相同,构造参数也拥有一个在构造器内部使用的参数名字和一个在调用构造器时使用的外部参数名字。
然而构造器并不像函数和方法那样在括号前有一个可辨别的名字。因此在调用构造器时主要通过构造器中的参数名和类型来确定应该被调用的构造器。正因为参数如此重要如果你在定义构造器时没有提供参数的外部名字Swift 会为每个构造器的参数自动生成一个跟内部名字相同的外部名。
然而构造器并不像函数和方法那样在括号前有一个可辨别的名字。因此在调用构造器时主要通过构造器中的参数名和类型来确定应该被调用的构造器。正因为参数如此重要如果你在定义构造器时没有提供参数的外部名字Swift 会为构造器的每个参数自动生成一个跟内部名字相同的外部名。
以下例子中定义了一个结构体`Color`,它包含了三个常量:`red``green``blue`。这些属性可以存储`0.0``1.0`之间的值,用来指示颜色中红、绿、蓝成分的含量。
@ -183,7 +187,7 @@ let bodyTemperature = Celsius(37.0)
<a name="optional_property_types"></a>
### 可选属性类型
如果你定制的类型包含一个逻辑上允许取值为空的存储型属性——无论是因为它无法在初始化时赋值,还是因为它在之后某个时间点可以赋值为空——你都需要将它定义为可选类型`optional type`。可选类型的属性将自动初始化为`nil`,表示这个属性是有意在初始化时设置为空的。
如果你定制的类型包含一个逻辑上允许取值为空的存储型属性——无论是因为它无法在初始化时赋值,还是因为它在之后某个时间点可以赋值为空——你都需要将它定义为`可选类型`optional type。可选类型的属性将自动初始化为`nil`,表示这个属性是有意在初始化时设置为空的。
下面例子中定义了类`SurveyQuestion`,它包含一个可选字符串属性`response`
@ -204,12 +208,12 @@ cheeseQuestion.ask()
cheeseQuestion.response = "Yes, I do like cheese."
```
调查问题的答案在回答前是无法确定的,因此我们将属性`response`声明为`String?`类型,或者说是可选字符串类型`optional String`。当`SurveyQuestion`实例化时,它将自动赋值为`nil`,表明此字符串暂时还没有值。
调查问题的答案在回答前是无法确定的,因此我们将属性`response`声明为`String?`类型,或者说是`可选字符串类型`optional String。当`SurveyQuestion`实例化时,它将自动赋值为`nil`,表明此字符串暂时还没有值。
<a name="assigning_constant_properties_during_initialization"></a>
### 构造过程中常量属性的修改
你可以在构造过程中的任意时间点修改常量属性值,只要在构造过程结束时是一个确定的值。一旦常量属性被赋值,它将永远不可更改。
你可以在构造过程中的任意时间点常量属性指定一个值,只要在构造过程结束时是一个确定的值。一旦常量属性被赋值,它将永远不可更改。
> 注意
对于类的实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。
@ -236,7 +240,7 @@ beetsQuestion.response = "I also like beets. (But not with cheese.)"
<a name="default_initializers"></a>
## 默认构造器
如果结构体或类的所有属性都有默认值,同时没有自定义的构造器,那么 Swift 会给这些结构体或类提供一个默认构造器。这个默认构造器将简单地创建一个所有属性值都设置为默认值的实例。
如果结构体或类的所有属性都有默认值,同时没有自定义的构造器,那么 Swift 会给这些结构体或类提供一个默认构造器default initializers。这个默认构造器将简单地创建一个所有属性值都设置为默认值的实例。
下面例子中创建了一个类`ShoppingListItem`,它封装了购物清单中的某一物品的属性:名字(`name`)、数量(`quantity`)和购买状态 `purchase state`
@ -260,7 +264,7 @@ var item = ShoppingListItem()
下面例子中定义了一个结构体`Size`,它包含两个属性`width``height`。Swift 可以根据这两个属性的初始赋值`0.0`自动推导出它们的类型为`Double`
由于这两个存储型属性都有默认值,结构体`Size`自动获得了一个逐一成员构造器`init(width:height:)`。你可以用它来为`Size`创建新的实例:
结构体`Size`自动获得了一个逐一成员构造器`init(width:height:)`。你可以用它来为`Size`创建新的实例:
```swift
struct Size {
@ -270,15 +274,16 @@ let twoByTwo = Size(width: 2.0, height: 2.0)
```
<a name="initializer_delegation_for_value_types"></a>
## 值类型的构造器代理
构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能减少多个构造器间的代码重复。
构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给提供给它的构造器。类则不同,它可以继承自其它类(请参考[继承](./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)章节。
@ -351,11 +356,11 @@ Swift 为类类型提供了两种构造器来确保实例中所有存储型属
<a name="designated_initializers_and_convenience_initializers"></a>
### 指定构造器和便利构造器
*指定构造器*是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。
*指定构造器*designated initializers是类中最主要的构造器。一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。
每一个类都必须拥有至少一个指定构造器。在某些情况下,许多类通过继承了父类中的指定构造器而满足了这个条件。具体内容请参考后续章节[构造器的自动继承](#automatic_initializer_inheritance)。
*便利构造器*是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入值的实例。
*便利构造器*convenience initializers是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入值的实例。
你应当只在必要的时候为类提供便利构造器,比方说某种情况下通过使用便利构造器来快捷调用某个指定构造器,能够节省更多开发时间并让类的构造过程更清晰明了。
@ -415,14 +420,14 @@ convenience init(parameters) {
<a name="two_phase_initialization"></a>
### 两段式构造过程
Swift 中类的构造过程包含两个阶段。第一个阶段,每个存储型属性通过引入它们的类的构造器来设置初始值。当每个存储型属性值被确定后,第二阶段开始,它给每个类一次机会在新实例准备使用之前进一步定制它们的存储型属性。
Swift 中类的构造过程包含两个阶段。第一个阶段,每个存储型属性引入它们的类指定一个初始值。当每个存储型属性的初始值被确定后,第二阶段开始,它给每个类一次机会在新实例准备使用之前进一步定制它们的存储型属性。
两段式构造过程的使用让构造过程更安全,同时在整个类层级结构中给予了每个类完全的灵活性。两段式构造过程可以防止属性值在初始化之前被访问,也可以防止属性被另外一个构造器意外地赋予不同的值。
> 注意
Swift 的两段式构造过程跟 Objective-C 中的构造过程类似。最主要的区别在于阶段 1Objective-C 给每一个属性赋值`0`或空值(比如说`0``nil`。Swift 的构造流程则更加灵活,它允许你设置定制的初始值,并自如应对某些属性不能以`0``nil`作为合法默认值的情况。
Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程能顺利完成:
Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程能不出错地完成:
##### 安全检查 1
@ -485,7 +490,7 @@ Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造
<a name="initializer_inheritance_and_overriding"></a>
### 构造器的继承和重写
跟 Objective-C 中的子类不同Swift 中的子类默认情况下不会继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更专业的子类继承,并被错误地用来创建子类的实例。
跟 Objective-C 中的子类不同Swift 中的子类默认情况下不会继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更精细的子类继承,并被错误地用来创建子类的实例。
> 注意
父类的构造器仅会在安全和适当的情况下被继承。具体内容请参考后续章节[构造器的自动继承](#automatic_initializer_inheritance)。
@ -494,7 +499,7 @@ Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造
当你在编写一个和父类中指定构造器相匹配的子类构造器时,你实际上是在重写父类的这个指定构造器。因此,你必须在定义子类构造器时带上`override`修饰符。即使你重写的是系统自动提供的默认构造器,也需要带上`override`修饰符,具体内容请参考[默认构造器](#default_initializers)。
正如重写属性,方法或者是下标脚本`override`修饰符会让编译器去检查父类中是否有相匹配的指定构造器,并验证构造器参数是否正确。
正如重写属性,方法或者是下标,`override`修饰符会让编译器去检查父类中是否有相匹配的指定构造器,并验证构造器参数是否正确。
> 注意
当你重写一个父类的指定构造器时,你总是需要写`override`修饰符,即使你的子类将父类的指定构造器重写为了便利构造器。
@ -559,7 +564,7 @@ print("Bicycle: \(bicycle.description)")
##### 规则 2
如果子类提供了所有父类指定构造器的实现——无论是通过规则 1 继承过来的,还是提供了自定义实现——它将自动继承所有父类的便利构造器。(即使属性没有默认值,只要实现了父类的所有指定构造器,就会自动继承父类的所有便利构造器)
如果子类提供了所有父类指定构造器的实现——无论是通过规则 1 继承过来的,还是提供了自定义实现——它将自动继承所有父类的便利构造器。
即使你在子类中添加了更多的便利构造器,这两条规则仍然适用。
@ -605,7 +610,7 @@ let mysteryMeat = Food()
// mysteryMeat 的名字是 [Unnamed]
```
类层级中的第二个类是`Food`的子类`RecipeIngredient``RecipeIngredient`构建了食谱中的一味调味剂。它引入了`Int`类型的属性`quantity`(以及从`Food`继承过来的`name`属性),并且定义了两个构造器来创建`RecipeIngredient`实例:
类层级中的第二个类是`Food`的子类`RecipeIngredient``RecipeIngredient`用来表示食谱中的一项原料。它引入了`Int`类型的属性`quantity`(以及从`Food`继承过来的`name`属性),并且定义了两个构造器来创建`RecipeIngredient`实例:
```swift
class RecipeIngredient: Food {
@ -642,7 +647,7 @@ let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
```
类层级中第三个也是最后一个类是`RecipeIngredient`的子类,叫做`ShoppingListItem`。这个类构建了购物单中出现的某一种调味料。
类层级中第三个也是最后一个类是`RecipeIngredient`的子类,叫做`ShoppingListItem`。这个类构建了购物单中出现的某一种食谱原料。
购物单中的每一项总是从未购买状态开始的。为了呈现这一事实,`ShoppingListItem`引入了一个布尔类型的属性`purchased`,它的默认值是`false``ShoppingListItem`还添加了一个计算型属性`description`,它提供了关于`ShoppingListItem`实例的一些文字描述:
@ -691,7 +696,7 @@ for item in breakfastList {
如果一个类、结构体或枚举类型的对象,在构造过程中有可能失败,则为其定义一个可失败构造器。这里所指的“失败”是指,如给构造器传入无效的参数值,或缺少某种所需的外部资源,又或是不满足某种必要的条件等。
为了妥善处理这种构造过程中可能会失败的情况。你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在`init`关键字后面添问号`(init?)`
为了妥善处理这种构造过程中可能会失败的情况。你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在`init`关键字后面添问号(`init?`)
> 注意
可失败构造器的参数名和参数类型,不能与其它非可失败构造器的参数名,及其参数类型相同。
@ -806,44 +811,6 @@ if unknownUnit == nil {
// 打印 "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`属性是一个常量,所以一旦构造成功,`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>
### 构造失败的传递
@ -856,36 +823,38 @@ if let bowTie = Product(name: "bow tie") {
下面这个例子,定义了一个名为`CartItem``Product`类的子类。这个类建立了一个在线购物车中的物品的模型,它有一个名为`quantity`的常量存储型属性,并确保该属性的值至少为`1`
```swift
class Product {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
class CartItem: Product {
let quantity: Int!
let quantity: Int
init?(name: String, quantity: Int) {
if quantity < 1 { return nil }
self.quantity = quantity
super.init(name: name)
if quantity < 1 { return nil }
}
}
```
`Product`类中的`name`属性类似,`CartItem`类中的`quantity`属性也是隐式解包可选类型。这意味着在构造过程中,该属性在被赋予特定的值之前能有一个默认的初始值`nil`
`CartItem` 可失败构造器首先验证接收的 `quantity` 值是否大于等于 1 。倘若 `quantity` 值无效,则立即终止整个构造过程,返回失败结果,且不再执行余下代码。同样地,`Product` 的可失败构造器首先检查 `name` 值,假如 `name` 值为空字符串,则构造器立即执行失败
该可失败构造器以向上代理到父类的可失败构造器`init(name:)`开始。这满足了可失败构造器在触发构造失败前必须总是完成构造器代理调用这个条件。
如果由于`name`的值为空字符串而导致父类的可失败构造器构造失败,则`CartIem`类的整个构造过程都将立即失败,之后的构造代码将不会再被执行。如果父类构造成功,`CartIem`的可失败构造器会进一步验证`quantity`的值是否不小于`1`
> 译者注
> 上面的示例代码和描述也不相符,根据描述,`self.quantity = quantity`这句代码应该放在最后一行,而这却会导致编译错误`error: property 'self.quantity' not initialized at super.init call`,除非将`let quantity: Int!`改为`var quantity: Int!`。
如果你构造一个`name`的值为非空字符串,`quantity`的值不小于`1``CartItem`实例,则可成功构造:
如果你通过传入一个非空字符串 `name` 以及一个值大于等于 1 的 `quantity` 来创建一个 `CartItem` 实例,那么构造方法能够成功被执行:
```swift
if let twoSocks = CartItem(name: "sock", quantity: 2) {
print("Item: \(twoSocks.name), quantity: \(twoSocks.quantity)")
}
// 打印 "Item: sock, quantity: 2"
// 打印 "Item: sock, quantity: 2
```
如果你试图构造一个`quantity`的值为`0``CartItem`实例, 则`CartItem`的可失败构造器会触发构造失败:
倘若你以一个值为 0 的 `quantity` 来创建一个 `CartItem` 实例,那么将导致 `CartItem` 构造器失败:
```swift
if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
@ -893,10 +862,10 @@ if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
} else {
print("Unable to initialize zero shirts")
}
// 打印 "Unable to initialize zero shirts"
```
// 打印 "Unable to initialize zero shirts
```
类似的,如果你试图构造一个`name`的值为空字符串的`CartItem`实例,则父类`Product`的可失败构造器会触发构造失败:
同样地,如果你尝试传入一个值为空字符串的 `name`来创建一个 `CartItem` 实例,那么将导致父类 `Product` 的构造过程失败:
```swift
if let oneUnnamed = CartItem(name: "", quantity: 1) {
@ -904,7 +873,7 @@ if let oneUnnamed = CartItem(name: "", quantity: 1) {
} else {
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>
@ -968,7 +937,7 @@ class UntitledDocument: Document {
<a name="the_init!_failable_initializer"></a>
### 可失败构造器 init!
通常来说我们通过在`init`关键字后添加问号的方式(`init?`)来定义一个可失败构造器,但你也可以通过在`init`后面添加惊叹号的方式来定义一个可失败构造器(`(init!)`),该可失败构造器将会构建一个对应类型的隐式解包可选类型的对象。
通常来说我们通过在`init`关键字后添加问号的方式(`init?`)来定义一个可失败构造器,但你也可以通过在`init`后面添加惊叹号的方式来定义一个可失败构造器(`init!`),该可失败构造器将会构建一个对应类型的隐式解包可选类型的对象。
你可以在`init?`中代理到`init!`,反之亦然。你也可以用`init?`重写`init!`,反之亦然。你还可以用`init`代理到`init!`,不过,一旦`init!`构造失败,则会触发一个断言。
@ -1024,7 +993,7 @@ class SomeClass {
下面例子中定义了一个结构体`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`的元素表示一个白格。数组中第一个元素代表棋盘上左上角的格子,最后一个元素代表棋盘上右下角的格子。
@ -1035,8 +1004,8 @@ struct Checkerboard {
let boardColors: [Bool] = {
var temporaryBoard = [Bool]()
var isBlack = false
for i in 1...10 {
for j in 1...10 {
for i in 1...8 {
for j in 1...8 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
@ -1045,7 +1014,7 @@ struct Checkerboard {
return temporaryBoard
}()
func squareIsBlackAtRow(row: Int, column: Int) -> Bool {
return boardColors[(row * 10) + column]
return boardColors[(row * 8) + column]
}
}
```
@ -1056,6 +1025,6 @@ struct Checkerboard {
let board = Checkerboard()
print(board.squareIsBlackAtRow(0, column: 1))
// 打印 "true"
print(board.squareIsBlackAtRow(9, column: 9))
print(board.squareIsBlackAtRow(7, column: 7))
// 打印 "false"
```

View File

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

View File

@ -10,7 +10,10 @@
> 2.1
> 翻译:[Channe](https://github.com/Channe)
> 校对:[shanks](http://codebuild.me)2015-10-31
> 校对:[shanks](http://codebuild.me)[Realank](https://github.com/Realank) 2016-01-23
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-14
本页包含内容:
@ -23,7 +26,7 @@
Swift 使用自动引用计数ARC机制来跟踪和管理你的应用程序的内存。通常情况下Swift 内存管理机制会一直起作用你无须自己来考虑内存的管理。ARC 会在类的实例不再被使用时,自动释放其占用的内存。
然而在少数情况下,ARC 为了能帮助你管理内存,需要更多的关于你的代码之间关系的信息。本章描述了这些情况,并且为你示范怎样启用 ARC 来管理你的应用程序的内存。
然而在少数情况下,为了能帮助你管理内存,ARC 需要更多的代码之间关系的信息。本章描述了这些情况,并且为你示范怎样才能使 ARC 来管理你的应用程序的所有内存。
> 注意
引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型,也不是通过引用的方式存储和传递。
@ -31,7 +34,7 @@ Swift 使用自动引用计数ARC机制来跟踪和管理你的应用程
<a name="how_arc_works"></a>
## 自动引用计数的工作机制
当你每次创建一个类的新的实例的时候ARC 会分配一块内存来储存实例信息。内存中会包含实例的类型信息,以及这个实例所有相关属性的值。
当你每次创建一个类的新的实例的时候ARC 会分配一块内存来储存实例信息。内存中会包含实例的类型信息,以及这个实例所有相关的存储型属性的值。
此外当实例不再被使用时ARC 释放实例所占用的内存,并让释放的内存能挪作他用。这确保了不再被使用的实例,不会一直占用内存空间。
@ -134,7 +137,7 @@ class Apartment {
每一个`Person`实例有一个类型为`String`,名字为`name`的属性,并有一个可选的初始化为`nil``apartment`属性。`apartment`属性是可选的,因为一个人并不总是拥有公寓。
类似的,每个`Apartment`实例有一个叫`number`,类型为`Int`的属性,并有一个可选的初始化为`nil``tenant`属性。`tenant`属性是可选的,因为一栋公寓并不总是有居民。
类似的,每个`Apartment`实例有一个叫`unit`,类型为`String`的属性,并有一个可选的初始化为`nil``tenant`属性。`tenant`属性是可选的,因为一栋公寓并不总是有居民。
这两个类都定义了析构函数,用以在类实例被析构的时候输出信息。这让你能够知晓`Person``Apartment`的实例是否像预期的那样被销毁。
@ -411,7 +414,7 @@ print("\(country.name)'s capital city is called \(country.capitalCity.name)")
循环强引用的产生,是因为闭包和类相似,都是引用类型。当你把一个闭包赋值给某个属性时,你是将这个闭包的引用赋值给了属性。实质上,这跟之前的问题是一样的——两个强引用让彼此一直有效。但是,和两个类实例不同,这次一个是类实例,另一个是闭包。
Swift 提供了一种优雅的方法来解决这个问题称之为闭包捕获列表closuer capture list。同样的在学习如何用闭包捕获列表破循环强引用之前,先来了解一下这里的循环强引用是如何产生的,这对我们很有帮助。
Swift 提供了一种优雅的方法来解决这个问题,称之为`闭包捕获列表`closure capture list。同样的在学习如何用闭包捕获列表破循环强引用之前,先来了解一下这里的循环强引用是如何产生的,这对我们很有帮助。
下面的例子为你展示了当一个闭包引用了`self`后是如何产生一个循环强引用的。例子中定义了一个叫`HTMLElement`的类,用一种简单的模型表示 HTML 文档中的一个单独的元素:
@ -471,7 +474,7 @@ print(heading.asHTML())
```swift
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// 打印 “hello, world”
// 打印 “<p>hello, world</p>
```
> 注意
@ -528,9 +531,9 @@ lazy var someClosure: Void -> String = {
<a name="weak_and_unowned_references"></a>
###弱引用和无主引用
在闭包和捕获的实例总是互相引用并且总是同时销毁时,将闭包内的捕获定义为无主引用。
在闭包和捕获的实例总是互相引用并且总是同时销毁时,将闭包内的捕获定义为`无主引用`
相反的,在被捕获的引用可能会变为`nil`时,将闭包内的捕获定义为弱引用。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为`nil`。这使我们可以在闭包体内检查它们是否存在。
相反的,在被捕获的引用可能会变为`nil`时,将闭包内的捕获定义为`弱引用`。弱引用总是可选类型,并且当引用的实例被销毁后,弱引用的值会自动置为`nil`。这使我们可以在闭包体内检查它们是否存在。
> 注意
如果被捕获的引用绝对不会变为`nil`,应该用无主引用,而不是弱引用。

View File

@ -10,7 +10,10 @@
> 翻译+校对:[lyojo](https://github.com/lyojo)
> 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?`
如果创建一个新的`Person`实例,因为它的`residence`属性是可选的,`john`属性将初始化`nil`
`Residence`有一个`Int`类型的属性`numberOfRooms`,其默认值为`1``Person`具有一个可选的`residence`属性,其类型为`Residence?`
假如你创建一个新的`Person`实例,它的`residence`属性由于是是可选型而将初始化为`nil`,在下面的代码中,`john`有一个值`nil``residence`属性
```swift
let john = Person()
@ -314,7 +317,7 @@ if let firstRoomName = john.residence?[0].name {
```swift
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0]++
testScores["Bev"]?[0] += 1
testScores["Brian"]?[0] = 72
// "Dave" 数组现在是 [91, 82, 84]"Bev" 数组现在是 [80, 94, 81]
```

View File

@ -3,7 +3,10 @@
> 2.1
> 翻译+校对:[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>
##表示并抛出错误
@ -35,7 +38,7 @@ enum VendingMachineError: ErrorType {
}
```
抛出一个错误可以让你表明有意外情况发生,导致正常的执行流程无法继续执行。抛出错误使用`throws`关键字。例如,下面的代码抛出一个错误,提示贩卖机还需要`5`个硬币:
抛出一个错误可以让你表明有意外情况发生,导致正常的执行流程无法继续执行。抛出错误使用`throw`关键字。例如,下面的代码抛出一个错误,提示贩卖机还需要`5`个硬币:
```swift
throw VendingMachineError.InsufficientFunds(coinsNeeded: 5)
@ -68,7 +71,7 @@ func cannotThrowErrors() -> String
> 注意
只有 throwing 函数可以传递错误。任何在某个非 throwing 函数内部抛出的错误只能在函数内部处理。
下面的例子中,`VendingMechine`类有一个`vend(itemNamed:)`方法,如果请求的物品不存在、缺货或者花费超过了投入金额,该方法就会抛出一个相应的`VendingMachineError`
下面的例子中,`VendingMechine`类有一个`vend(itemNamed:)`方法,如果请求的物品不存在、缺货或者投入金额小于物品价格,该方法就会抛出一个相应的`VendingMachineError`
```swift
struct Item {
@ -88,7 +91,7 @@ class VendingMachine {
}
func vend(itemNamed name: String) throws {
guard var item = inventory[name] else {
guard let item = inventory[name] else {
throw VendingMachineError.InvalidSelection
}
@ -101,8 +104,11 @@ class VendingMachine {
}
coinsDeposited -= item.price
--item.count
inventory[name] = item
var newItem = item
newItem.count -= 1
inventory[name] = newItem
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 处理错误
@ -166,7 +185,7 @@ do {
###将错误转换成可选值
可以使用`try?`通过将错误转换成一个可选值来处理错误。如果在评估`try?`表达式时一个错误被抛出,那么表达式的值就是`nil`。例如下面代码中`x``y`有相同的
可以使用`try?`通过将错误转换成一个可选值来处理错误。如果在评估`try?`表达式时一个错误被抛出,那么表达式的值就是`nil`。例如,在下面代码中,`x``y`相同的数值和等价的含义
```swift
func someThrowingFunction() throws -> Int {
@ -197,7 +216,7 @@ func fetchData() -> Data? {
### 禁用错误传递
有时你知道某个 throwing 函数实际上在运行时是不会抛出错误的,在这种情况下,你可以在表达式前面写`try!`来禁用错误传递,这会把调用包装在一个断言不会有错误抛出的运行时断言中。如果实际上抛出了错误,你会得到一个运行时错误。
有时你知道某个 throwing 函数实际上在运行时是不会抛出错误的,在这种情况下,你可以在表达式前面写`try!`来禁用错误传递,这会把调用包装在一个不会有错误抛出的运行时断言中。如果真的抛出了错误,你会得到一个运行时错误。
例如,下面的代码使用了`loadImage(_:)`函数,该函数从给定的路径加载图片资源,如果图片无法载入则抛出一个错误。在这种情况下,因为图片是和应用绑定的,运行时不会有错误抛出,所以适合禁用错误传递:

View File

@ -10,6 +10,9 @@
> 2.1
> 校对:[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 {
if item is Movie {
++movieCount
movieCount += 1
} else if item is Song {
++songCount
songCount += 1
}
}
@ -164,12 +167,10 @@ Swift 为不确定类型提供了两种特殊的类型别名:
<a name="anyobject"></a>
### `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` 类型的实例:

View File

@ -9,14 +9,17 @@
> 翻译+校对:[SergioChan](https://github.com/SergioChan)
> 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)
- [引用嵌套类型](#referring_to_nested_types)
枚举常被用于为特定类或结构体实现某些功能。类似的,也能够在某个复杂的类型中,方便定义工具类或结构体使用。为了实现这种功能Swift 允许你定义嵌套类型,可以在支持的类型中定义嵌套的枚举、类和结构体。
枚举常被用于为特定类或结构体实现某些功能。类似的,枚举可以方便定义工具类或结构体,从而为某个复杂的类型所使用。为了实现这种功能Swift 允许你定义嵌套类型,可以在支持的类型中定义嵌套的枚举、类和结构体。
要在一个类型中嵌套另一个类型,将嵌套类型的定义写在其外部类型的`{}`内,而且可以根据需要定义多级嵌套。

View File

@ -10,6 +10,9 @@
> 2.1
> 校对:[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)中描述的,如果你把定制的构造器写在值类型的原始实现中,上述规则将不再适用。
下面的例子定义了一个用于描述几何矩形的结构体 `Rect`。这个例子同时定义了两个辅助结构体 `Size``Point`,它们都把 `0.0` 作为所有属性的默认值:
@ -223,11 +226,10 @@ someInt.square()
```swift
extension Int {
subscript(var digitIndex: Int) -> Int {
subscript(digitIndex: Int) -> Int {
var decimalBase = 1
while digitIndex > 0 {
for _ in 0..<digitIndex {
decimalBase *= 10
--digitIndex
}
return (self / decimalBase) % 10
}
@ -295,7 +297,7 @@ func printIntegerKinds(numbers: [Int]) {
print("")
}
printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
// 打印 “+ + - 0 - 0 +”
// 打印 “+ + - 0 - 0 +
```
函数 `printIntegerKinds(_:)` 接受一个 `Int` 数组,然后对该数组进行迭代。在每次迭代过程中,对当前整数的计算型属性 `kind` 的值进行评估,并打印出适当的描述。

View File

@ -10,7 +10,13 @@
> 2.1
> 翻译:[小铁匠Linus](https://github.com/kevin833752)
> 校对:[shanks](http://codebuild.me)2015-11-01
> 校对:[shanks](http://codebuild.me)
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK)
>
> 3.0
> 校对:[CMB](https://github.com/chenmingbiao)
本页包含内容:
@ -65,11 +71,11 @@ class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
<a name="property_requirements"></a>
## 属性要求
协议可以要求采纳协议的类型提供特定名称和类型的实例属性或类型属性。协议不指定属性是存储型属性还是计算型属性,它只指定属性的名称和类型。此外,协议还指定属性是读的还是可读可写的。
协议可以要求采纳协议的类型提供特定名称和类型的实例属性或类型属性。协议不指定属性是存储型属性还是计算型属性,它只指定属性的名称和类型。此外,协议还指定属性是读的还是可读可写的。
如果协议要求属性是可读可写的,那么该属性不能是常量属性或只读的计算型属性。如果协议只要求属性是读的,那么该属性不仅可以是读的,如果代码需要的话,还可以是可写的。
如果协议要求属性是可读可写的,那么该属性不能是常量属性或只读的计算型属性。如果协议只要求属性是读的,那么该属性不仅可以是读的,如果代码需要的话,还可以是可写的。
协议通常`var` 关键字来声明变量属性,在类型声明后加上 `{ set get }` 来表示属性是可读可写的,读属性则用 `{ get }` 来表示:
协议总是`var` 关键字来声明变量属性,在类型声明后加上 `{ set get }` 来表示属性是可读可写的,读属性则用 `{ get }` 来表示:
```swift
protocol SomeProtocol {
@ -94,7 +100,7 @@ protocol FullyNamed {
}
```
`FullyNamed` 协议除了要求采纳协议的类型提供 `fullName` 属性外,并没有其他特别的要求。这个协议表示,任何采纳 `FullyNamed` 的类型,都必须有一个读的 `String` 类型的实例属性 `fullName`
`FullyNamed` 协议除了要求采纳协议的类型提供 `fullName` 属性外,并没有其他特别的要求。这个协议表示,任何采纳 `FullyNamed` 的类型,都必须有一个读的 `String` 类型的实例属性 `fullName`
下面是一个采纳 `FullyNamed` 协议的简单结构体:
@ -155,7 +161,7 @@ protocol RandomNumberGenerator {
`RandomNumberGenerator` 协议并不关心每一个随机数是怎样生成的,它只要求必须提供一个随机数生成器。
如下所示,下边是一个采纳 `RandomNumberGenerator` 协议的类。该类实现了一个叫做 *线性同余生成器linear congruential generator* 的伪随机数算法。
如下所示,下边是一个采纳并符合 `RandomNumberGenerator` 协议的类。该类实现了一个叫做 *线性同余生成器linear congruential generator* 的伪随机数算法。
```swift
class LinearCongruentialGenerator: RandomNumberGenerator {
@ -348,7 +354,7 @@ protocol DiceGameDelegate {
`DiceGame` 协议可以被任意涉及骰子的游戏采纳。`DiceGameDelegate` 协议可以被任意类型采纳,用来追踪 `DiceGame` 的游戏过程。
如下所示,`SnakesAndLadders` 是 [Control Flow](./05_Control_Flow.html) 章节引入的蛇梯棋游戏的新版本。新版本使用 `Dice` 实例作为骰子,并且实现了 `DiceGame``DiceGameDelegate` 协议,后者用来记录游戏的过程:
如下所示,`SnakesAndLadders` 是 [控制流](./05_Control_Flow.html) 章节引入的蛇梯棋游戏的新版本。新版本使用 `Dice` 实例作为骰子,并且实现了 `DiceGame``DiceGameDelegate` 协议,后者用来记录游戏的过程:
```swift
class SnakesAndLadders: DiceGame {
@ -383,9 +389,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` 属性提供骰子摇出的值。
@ -408,7 +414,7 @@ class DiceGameTracker: DiceGameDelegate {
print("The game is using a \(game.dice.sides)-sided dice")
}
func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
++numberOfTurns
numberOfTurns += 1
print("Rolled a \(diceRoll)")
}
func gameDidEnd(game: DiceGame) {
@ -442,7 +448,7 @@ game.play()
<a name="adding_protocol_conformance_with_an_extension"></a>
## 通过扩展添加协议一致性
即便无法修改源代码,依然可以通过扩展令已有类型采纳并符合协议。扩展可以为已有类型添加属性、方法、下标脚本以及构造器,因此可以符合协议中的相应要求。详情请在[扩展](./21_Extensions.html)章节中查看。
即便无法修改源代码,依然可以通过扩展令已有类型采纳并符合协议。扩展可以为已有类型添加属性、方法、下标以及构造器,因此可以符合协议中的相应要求。详情请在[扩展](./21_Extensions.html)章节中查看。
> 注意
> 通过扩展令已有类型采纳并符合协议时,该类型的所有实例也会随之获得协议中定义的各项功能。
@ -470,8 +476,8 @@ extension Dice: TextRepresentable {
现在所有 `Dice` 的实例都可以看做 `TextRepresentable` 类型:
```swift
let d12 = Dice(sides: 12,generator: LinearCongruentialGenerator())
print(d12. textualDescription)
let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator())
print(d12.textualDescription)
// 打印 “A 12-sided dice”
```
@ -611,7 +617,7 @@ protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
<a name="protocol_composition"></a>
## 协议合成
有时候需要同时采纳多个协议,你可以将多个协议采用 `protocol<SomeProtocol, AnotherProtocol>` 这样的格式进行组合,称为 *协议合成protocol composition*。你可以`<>`罗列任意多个你想要采纳的协议,以逗号分隔。
有时候需要同时采纳多个协议,你可以将多个协议采用 `SomeProtocol & AnotherProtocol` 这样的格式进行组合,称为 *协议合成protocol composition*。你可以罗列任意多个你想要采纳的协议,以与符号(`&`)分隔。
下面的例子中,将 `Named``Aged` 两个协议按照上述语法组合成一个协议,作为函数参数的类型:
@ -626,17 +632,17 @@ struct Person: Named, Aged {
var name: String
var age: Int
}
func wishHappyBirthday(celebrator: protocol<Named, Aged>) {
print("Happy birthday \(celebrator.name) - you're \(celebrator.age)!")
func wishHappyBirthday(to celebrator: Named & Aged) {
print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")
}
let birthdayPerson = Person(name: "Malcolm", age: 21)
wishHappyBirthday(birthdayPerson)
wishHappyBirthday(to: birthdayPerson)
// 打印 “Happy birthday Malcolm - you're 21!”
```
`Named` 协议包含 `String` 类型的 `name` 属性。`Aged` 协议包含 `Int` 类型的 `age` 属性。`Person` 结构体采纳了这两个协议。
`wishHappyBirthday(_:)` 函数的参数 `celebrator` 的类型为 `protocol<NamedAged>`。这意味着它不关心参数的具体类型,只要参数符合这两个协议即可。
`wishHappyBirthday(_:)` 函数的参数 `celebrator` 的类型为 `Named & Aged`。这意味着它不关心参数的具体类型,只要参数符合这两个协议即可。
上面的例子创建了一个名为 `birthdayPerson``Person` 的实例,作为参数传递给了 `wishHappyBirthday(_:)` 函数。因为 `Person` 同时符合这两个协议,所以这个参数合法,函数将打印生日问候语。
@ -652,7 +658,7 @@ wishHappyBirthday(birthdayPerson)
* `as?` 返回一个可选值,当实例符合某个协议时,返回类型为协议类型的可选值,否则返回 `nil`
* `as!` 将实例强制向下转换到某个协议类型,如果强转失败,会引发运行时错误。
下面的例子定义了一个 `HasArea` 协议,该协议定义了一个 `Double` 类型的读属性 `area`
下面的例子定义了一个 `HasArea` 协议,该协议定义了一个 `Double` 类型的读属性 `area`
```swift
protocol HasArea {
@ -726,7 +732,7 @@ for object in objects {
> 注意
> 可选的协议要求只能用在标记 `@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` 类采纳,其他类以及结构体和枚举均不能采纳这种协议。
下面的例子定义了一个名为 `Counter` 的用于整数计数的类,它使用外部的数据源来提供每次的增量。数据源由 `CounterDataSource` 协议定义,包含两个可选要求:
@ -795,7 +801,7 @@ for _ in 1...4 {
// 12
```
上述代码新建了一个 `Counter` 实例,并将它的数据源设置为一个 `TreeSource` 的实例,然后调用 `increment()` 方法四次。和预期一样,每次调用都会将 `count` 的值增加 `3`.
上述代码新建了一个 `Counter` 实例,并将它的数据源设置为一个 `ThreeSource` 的实例,然后调用 `increment()` 方法四次。和预期一样,每次调用都会将 `count` 的值增加 `3`.
下面是一个更为复杂的数据源 `TowardsZeroSource`,它将使得最后的值变为 `0`
@ -834,7 +840,7 @@ for _ in 1...5 {
<a name="protocol_extensions"></a>
## 协议扩展
协议可以通过扩展来为采纳协议的类型提供属性、方法以及下标脚本的实现。通过这种方式,你可以基于协议本身来实现这些功能,而无需在每个采纳协议的类型中都重复同样的实现,也无需使用全局函数。
协议可以通过扩展来为采纳协议的类型提供属性、方法以及下标的实现。通过这种方式,你可以基于协议本身来实现这些功能,而无需在每个采纳协议的类型中都重复同样的实现,也无需使用全局函数。
例如,可以扩展 `RandomNumberGenerator` 协议来提供 `randomBool()` 方法。该方法使用协议中定义的 `random()` 方法来返回一个随机的 `Bool` 值:
@ -859,7 +865,7 @@ print("And here's a random Boolean: \(generator.randomBool())")
<a name="providing_default_implementations"></a>
### 提供默认实现
可以通过协议扩展来为协议要求的属性、方法以及下标脚本提供默认的实现。如果采纳协议的类型为这些要求提供了自己的实现,那么这些自定义实现将会替代扩展中的默认实现被使用。
可以通过协议扩展来为协议要求的属性、方法以及下标提供默认的实现。如果采纳协议的类型为这些要求提供了自己的实现,那么这些自定义实现将会替代扩展中的默认实现被使用。
> 注意
> 通过协议扩展为协议要求提供的默认实现和可选的协议要求不同。虽然在这两种情况下,采纳协议的类型都无需自己实现这些要求,但是通过扩展提供的默认实现可以直接调用,而无需使用可选链式调用。
@ -877,7 +883,7 @@ extension PrettyTextRepresentable {
<a name="adding_constraints_to_protocol_extensions"></a>
### 为协议扩展添加限制条件
在扩展协议的时候,可以指定一些限制条件,只有采纳协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用 `where` 子句来描述,正如[Where子句](./23_Generics.html#where_clauses))中所描述的。
在扩展协议的时候,可以指定一些限制条件,只有采纳协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用 `where` 子句来描述,正如[Where子句](./23_Generics.html#where_clauses)中所描述的。
例如,你可以扩展 `CollectionType` 协议,但是只适用于集合中的元素采纳了 `TextRepresentable` 协议的情况:

View File

@ -12,6 +12,10 @@
> 2.1
> 校对:[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)
@ -24,9 +28,9 @@
- [关联类型](#associated_types)
- [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>
## 泛型所解决的问题
@ -96,11 +100,11 @@ func swapTwoInts(inout a: Int, inout _ b: Int)
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`
@ -131,22 +135,22 @@ swapTwoValues(&someString, &anotherString)
<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>
## 泛型类型
除了泛型函数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)
@ -154,9 +158,9 @@ swapTwoValues(&someString, &anotherString)
2. 第四个值被压入到栈的顶部。
3. 现在有四个值在栈中,最近入栈的那个值在顶部。
4. 栈中最顶部的那个值被移除,或称之为出栈。
5. 移除掉一个值后,现在栈再一次只有三个值。
5. 移除掉一个值后,现在栈只有三个值
下面展示了如何编写一个非泛型版本的栈,在这种情况下是 `Int` 型的栈:
下面展示了如何编写一个非泛型版本的栈, `Int` 型的栈为例
```swift
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` 类型的空数组对其进行初始化。
- 指定 `push(_:)` 方法的一参数 `item` 的类型必须是 `Element` 类型。
- 指定 `push(_:)` 方法的一参数 `item` 的类型必须是 `Element` 类型。
- 指定 `pop()` 方法的返回值类型必须是 `Element` 类型。
由于 `Stack` 是泛型类型,因此可以用来创建 Swift 中任意有效类型的栈,如同 `Array``Dictionary`
由于 `Stack` 是泛型类型,因此可以用来创建 Swift 中任意有效类型的栈,就像 `Array``Dictionary` 那样
你可以通过在尖括号中写出栈中需要存储的数据类型来创建并初始化一个 `Stack` 实例。例如,要创建一个 `String` 类型的栈,可以写成 `Stack<String>()`
@ -227,7 +231,7 @@ let fromTheTop = stackOfStrings.pop()
<a name="extending_a_generic_type"></a>
## 扩展一个泛型类型
当你扩展一个泛型类型的时候,你并不需要在扩展的定义中提供类型参数列表。更加方便的是,原始类型定义中声明的类型参数列表在扩展中可以直接使用,并且这些来自原始类型中的参数名称会被用作原始定义中类型参数的引用。
当你扩展一个泛型类型的时候,你并不需要在扩展的定义中提供类型参数列表。原始类型定义中声明的类型参数列表在扩展中可以直接使用,并且这些来自原始类型中的参数名称会被用作原始定义中类型参数的引用。
下面的例子扩展了泛型类型 `Stack`,为其添加了一个名为 `topItem` 的只读计算型属性,它将会返回当前栈顶端的元素而不会将其从栈中移除:
@ -243,7 +247,7 @@ extension Stack {
注意,这个扩展并没有定义一个类型参数列表。相反的,`Stack` 类型已有的类型参数名称 `Element`,被用在扩展中来表示计算型属性 `topItem` 的可选类型。
计算型属性 `topItem` 现在可以用来访问任意 `Stack` 实例的顶端元素而不是移除它:
计算型属性 `topItem` 现在可以用来访问任意 `Stack` 实例的顶端元素且不移除它:
```swift
if let topItem = stackOfStrings.topItem {
@ -255,18 +259,18 @@ if let topItem = stackOfStrings.topItem {
<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>
### 类型约束语法
你可以在一个类型参数名后面放置一个类名或者协议名,通过冒号分隔,从而定义类型约束,它们将为类型参数列表的一部分。这种基本的类型约束作用于泛型函数时的语法如下所示(作用于泛型类型时的语法与之相同):
你可以在一个类型参数名后面放置一个类名或者协议名,并用冒号进行分隔,定义类型约束,它们将为类型参数列表的一部分。对泛型函数添加类型约束的基本语法如下所示(作用于泛型类型时的语法与之相同):
```swift
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>
### 类型约束实践
这里有个名为 `findStringIndex` 的非泛型函数,该函数的功能是在 `String` 值的数组中查找给定 `String` 值的索引。若查找到匹配的字符串,`findStringIndex(_:_:)` 函数返回该字符串在数组中的索引值,反之则返回 `nil`
这里有个名为 `findStringIndex` 的非泛型函数,该函数的功能是在一个 `String` 数组中查找给定 `String` 值的索引。若查找到匹配的字符串,`findStringIndex(_:_:)` 函数返回该字符串在数组中的索引值,则返回 `nil`
```swift
func findStringIndex(array: [String], _ valueToFind: String) -> Int? {
@ -302,9 +306,9 @@ if let foundIndex = findStringIndex(strings, "llama") {
// 打印 “The index of llama is 2”
```
如果只能查找字符串在数组中的索引,用处不是很大。不过,你可以写出相同功能的泛型函数 `findIndex(_:_:)`用占位类型 `T` 替换 `String` 类型。
如果只能查找字符串在数组中的索引,用处不是很大。不过,你可以用占位类型 `T` 替换 `String` 类型来写出具有相同功能的泛型函数 `findIndex(_:_:)`
下面展示了 `findStringIndex(_:_:)` 函数的泛型版本 `findIndex(_:_:)`。请注意这个函数仍然返回 `Int?`是因为函数返回的是一个可选的索引数,而不是从数组中得到的一个可选值。需要提醒的是,这个函数无法通过编译,原因在例子后面说明:
下面展示了 `findStringIndex(_:_:)` 函数的泛型版本 `findIndex(_:_:)`。请注意这个函数返回值的类型仍然是 `Int?`是因为函数返回的是一个可选的索引数,而不是从数组中得到的一个可选值。需要提醒的是,这个函数无法通过编译,原因在例子后面说明:
```swift
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` 类型约束作为类型参数定义的一部分:
@ -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`
@ -348,7 +352,7 @@ let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea")
<a name="associated_types"></a>
## 关联类型
定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分将会非常有用。关联类型为协议的一部分,为某个类型提供了一个占位名(或者说别名),其代表的实际类型在协议被采纳时才会被指定。你可以通过 `typealias` 关键字来指定关联类型。
定义一个协议时,有的时候声明一个或多个关联类型作为协议定义的一部分将会非常有用。关联类型为协议的某个类型提供了一个占位名(或者说别名),其代表的实际类型在协议被采纳时才会被指定。你可以通过 `associatedtype` 关键字来指定关联类型。
<a name="associated_types_in_action"></a>
### 关联类型实践
@ -357,26 +361,26 @@ let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea")
```swift
protocol Container {
typealias ItemType
associatedtype ItemType
mutating func append(item: ItemType)
var count: Int { get }
subscript(i: Int) -> ItemType { get }
}
```
`Container` 协议定义了三个任何采纳协议的类型必须提供的功能:
`Container` 协议定义了三个任何采纳了该协议的类型(即容器)必须提供的功能:
- 必须可以通过 `append(_:)` 方法添加一个新元素到容器里。
- 必须可以通过 `count` 属性获取容器中元素的数量,并返回一个 `Int` 值。
- 必须可以通过接受 `Int` 索引值的下标检索到每一个元素。
- 必须可以通过索引值类型为 `Int` 的下标检索到容器中的每一个元素。
这个协议没有指定容器中元素该如何存储,以及元素必须是何种类型。这个协议只指定了三个任何采纳 `Container` 协议的类型必须提供的功能。采纳协议的类型在满足这三个条件的情况下也可以提供其他额外的功能。
这个协议没有指定容器中元素该如何存储,以及元素必须是何种类型。这个协议只指定了三个任何遵从 `Container` 协议的类型必须提供的功能。遵从协议的类型在满足这三个条件的情况下也可以提供其他额外的功能。
任何采纳 `Container` 协议的类型必须能够指定其存储的元素的类型,必须保证只有正确类型的元素可以加进容器中,必须明确通过其下标返回的元素的类型。
任何遵从 `Container` 协议的类型必须能够指定其存储的元素的类型,必须保证只有正确类型的元素可以加进容器中,必须明确通过其下标返回的元素的类型。
为了定义这三个条件,`Container` 协议需要在不知道容器中元素的具体类型的情况下引用这种类型。`Container` 协议需要指定任何通过 `append(_:)` 方法添加到容器中的元素和容器中的元素是相同类型,并且通过容器下标返回的元素的类型也是这种类型。
为了达到目的,`Container` 协议声明了一个关联类型 `ItemType`,写作 `typealias ItemType`。这个协议无法定义 `ItemType` 是什么类型的别名,这个信息将留给采纳协议的类型来提供。尽管如此,`ItemType` 别名提供了一种方式来引用 `Container` 中元素的类型,并将之用于 `append(_:)` 方法和下标,从而保证任何 `Container`预期行为都能够被执行。
为了达到这个目的,`Container` 协议声明了一个关联类型 `ItemType`,写作 `associatedtype ItemType`。这个协议无法定义 `ItemType` 是什么类型的别名,这个信息将留给遵从协议的类型来提供。尽管如此,`ItemType` 别名提供了一种方式来引用 `Container` 中元素的类型,并将之用于 `append(_:)` 方法和下标,从而保证任何 `Container` 的行为都能够正如预期地被执行。
下面是先前的非泛型的 `IntStack` 类型,这一版本采纳并符合了 `Container` 协议:
@ -406,11 +410,11 @@ struct 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
struct Stack<Element>: Container {
@ -442,7 +446,7 @@ struct Stack<Element>: Container {
[通过扩展添加协议一致性](./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
extension Array: Container {}
@ -455,30 +459,29 @@ extension Array: Container {}
[类型约束](#type_constraints)让你能够为泛型函数或泛型类型的类型参数定义一些强制要求。
为关联类型定义约束也是非常有用的。你可以在参数列表中通过 `where` 子句为关联类型定义约束。一个 `where` 子句能够使一个关联类型符合某个特定的协议,以及某个特定的类型参数和关联类型必须类型相同。你可以通过将 `where` 关键字紧跟在类型参数列表后面来定义 `where` 子句,`where` 子句后跟一个或者多个针对关联类型的约束,以及一个或多个类型参数和关联类型间的相等关系。
为关联类型定义约束也是非常有用的。你可以在参数列表中通过 `where` 子句为关联类型定义约束。你能通过 `where` 子句要求一个关联类型遵从某个特定的协议,以及某个特定的类型参数和关联类型必须类型相同。你可以通过将 `where` 关键字紧跟在类型参数列表后面来定义 `where` 子句,`where` 子句后跟一个或者多个针对关联类型的约束,以及一个或多个类型参数和关联类型间的相等关系。你可以在函数体或者类型的大括号之前添加 where 子句。
下面的例子定义了一个名为 `allItemsMatch` 的泛型函数,用来检查两个 `Container` 实例是否包含相同顺序的相同元素。如果所有的元素能够匹配,那么返回 `true`,否则返回 `false`
被检查的两个 `Container` 可以不是相同类型的容器(虽然它们可以相同),但它们必须拥有相同类型的元素。这个要求通过一个类型约束以及一个 `where` 子句来表示:
```swift
func allItemsMatch<
C1: Container, C2: Container
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
(someContainer: C1, _ anotherContainer: C2) -> Bool {
func allItemsMatch<C1: Container, C2: Container>
(_ someContainer: C1, _ anotherContainer: C2) -> Bool
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
// 检查两个容器含有相同数量的元素
if someContainer.count != anotherContainer.count {
return false
}
// 检查每一对元素是否相等
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
// 所有元素都匹配,返回 true
return true
}
@ -486,7 +489,7 @@ func allItemsMatch<
这个函数接受 `someContainer``anotherContainer` 两个参数。参数 `someContainer` 的类型为 `C1`,参数 `anotherContainer` 的类型为 `C2``C1``C2` 是容器的两个占位类型参数,函数被调用时才能确定它们的具体类型。
这个函数的类型参数列表还定义了两个类型参数的要求:
这个函数的类型参数列表还定义了两个类型参数的要求:
- `C1` 必须符合 `Container` 协议(写作 `C1: Container`)。
- `C2` 必须符合 `Container` 协议(写作 `C2: Container`)。
@ -504,7 +507,7 @@ func allItemsMatch<
第三个和第四个要求结合起来意味着 `anotherContainer` 中的元素也可以通过 `!=` 操作符来比较,因为它们和 `someContainer` 中的元素类型相同。
这些要求让 `allItemsMatch(_:_:)` 函数能够比较两个容器,即使它们是不同的容器类型。
这些要求让 `allItemsMatch(_:_:)` 函数能够比较两个容器,即使它们的容器类型不同
`allItemsMatch(_:_:)` 函数首先检查两个容器是否拥有相同数量的元素,如果它们的元素数量不同,那么一定不匹配,函数就会返回 `false`
@ -530,4 +533,4 @@ if allItemsMatch(stackOfStrings, arrayOfStrings) {
// 打印 “All items match.”
```
上面的例子创建一个 `Stack` 实例来存储一些 `String` 值,然后将三个字符串压入栈中。这个例子还通过数组字面量创建了一个 `Array` 实例,数组中包含三个同栈中一样的字符串。即使栈和数组是不同的类型,但它们都符合 `Container` 协议,而且它们都包含相同类型的值。因此你可以用这两个容器作为参数来调用 `allItemsMatch(_:_:)` 函数。在上面的例子中,`allItemsMatch(_:_:)` 函数正确地显示了这两个容器中的所有元素都是相互匹配的。
上面的例子创建一个 `Stack` 实例来存储一些 `String` 值,然后将三个字符串压入栈中。这个例子还通过数组字面量创建了一个 `Array` 实例,数组中包含同栈中一样的三个字符串。即使栈和数组是不同的类型,但它们都遵从 `Container` 协议,而且它们都包含相同类型的值。因此你可以用这两个容器作为参数来调用 `allItemsMatch(_:_:)` 函数。在上面的例子中,`allItemsMatch(_:_:)` 函数正确地显示了这两个容器中的所有元素都是相互匹配的。

View File

@ -11,6 +11,9 @@
> 2.1
> 翻译:[Prayer](https://github.com/futantan)
> 校对:[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
var value: String = "" {
didSet {
numberOfEdits++
numberOfEdits += 1
}
}
}
@ -306,7 +309,7 @@ public struct TrackedString {
public private(set) var numberOfEdits = 0
public var value: String = "" {
didSet {
numberOfEdits++
numberOfEdits += 1
}
}
public init() {}

View File

@ -10,6 +10,12 @@
> 2.1
> 校对:[shanks](http://codebuild.me)2015-11-01
>
> 2.2
> 翻译+校对:[SketchK](https://github.com/SketchK) 2016-05-17
>
> 3.0
> 翻译+校对:[mmoaay](https://github.com/mmoaay) 2016-09-20
本页内容包括:
@ -181,7 +187,7 @@ let blueComponent = pink & 0x0000FF // blueComponent 是 0x99即 153
其次,使用二进制补码可以使负数的按位左移和右移运算得到跟正数同样的效果,即每向左移一位就将自身的数值乘以 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")
@ -317,12 +323,15 @@ signedOverflow = signedOverflow &- 1
struct Vector2D {
var x = 0.0, y = 0.0
}
func + (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y + right.y)
extension Vector2D {
static func + (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y + right.y)
}
}
```
该运算符函数被定义为一个全局函数,并且函数的名字与它要进行重载的 `+` 名字一致。因为算术加法运算符是双目运算符,所以这个运算符函数接收两个类型为 `Vector2D` 的参数,同时有一个 `Vector2D` 类型的返回值。
该运算符函数被定义为 `Vector2D` 上的一个类方法,并且函数的名字与它要进行重载的 `+` 名字一致。因为加法运算并不是一个向量必需的功能,所以这个类方法被定义在 `Vector2D` 的一个扩展中,而不是 `Vector2D` 结构体声明内。而算术加法运算符是双目运算符,所以这个运算符函数接收两个类型为 `Vector2D` 的参数,同时有一个 `Vector2D` 类型的返回值。
在这个实现中,输入参数分别被命名为 `left``right`,代表在 `+` 运算符左边和右边的两个 `Vector2D` 实例。函数返回了一个新的 `Vector2D` 实例,这个实例的 `x``y` 分别等于作为参数的两个实例的 `x``y` 的值之和。
@ -342,13 +351,15 @@ let combinedVector = vector + anotherVector
<a name="prefix_and_postfix_operators"></a>
### 前缀和后缀运算符
上个例子演示了一个双目中缀运算符的自定义实现。类与结构体也能提供标准单目运算符的实现。单目运算符只运算一个值。当运算符出现在值之前时,它就是前缀的(例如 `-a`),而当它出现在值之后时,它就是后缀的(例如 `i++`)。
上个例子演示了一个双目中缀运算符的自定义实现。类与结构体也能提供标准单目运算符的实现。单目运算符只运算一个值。当运算符出现在值之前时,它就是前缀的(例如 `-a`),而当它出现在值之后时,它就是后缀的(例如 `b!`)。
要实现前缀或者后缀运算符,需要在声明运算符函数的时候在 `func` 关键字之前指定 `prefix` 或者 `postfix` 修饰符:
```swift
prefix func - (vector: Vector2D) -> Vector2D {
return Vector2D(x: -vector.x, y: -vector.y)
extension Vector2D {
static prefix func - (vector: Vector2D) -> Vector2D {
return Vector2D(x: -vector.x, y: -vector.y)
}
}
```
@ -369,8 +380,10 @@ let alsoPositive = -negative
复合赋值运算符将赋值运算符(`=`)与其它运算符进行结合。例如,将加法与赋值结合成加法赋值运算符(`+=`)。在实现的时候,需要把运算符的左参数设置成 `inout` 类型,因为这个参数的值会在运算符函数内直接被修改。
```swift
func += (inout left: Vector2D, right: Vector2D) {
left = left + right
extension Vector2D {
static func += (left: inout Vector2D, right: Vector2D) {
left = left + right
}
}
```
@ -383,23 +396,6 @@ original += vectorToAdd
// 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` 进行重载。
@ -412,11 +408,13 @@ let afterIncrement = ++toIncrement
为了使用等价运算符能对自定义的类型进行判等运算,需要为其提供自定义实现,实现的方法与其它中缀运算符一样:
```swift
func == (left: Vector2D, right: Vector2D) -> Bool {
return (left.x == right.x) && (left.y == right.y)
}
func != (left: Vector2D, right: Vector2D) -> Bool {
return !(left == right)
extension Vector2D {
static func == (left: Vector2D, right: Vector2D) -> Bool {
return (left.x == right.x) && (left.y == right.y)
}
static func != (left: Vector2D, right: Vector2D) -> Bool {
return !(left == right)
}
}
```
@ -444,18 +442,17 @@ if twoThree == anotherTwoThree {
prefix operator +++ {}
```
上面的代码定义了一个新的名为 `+++` 的前缀运算符。对于这个运算符,在 Swift 中并没有意义,因此我们针对 `Vector2D` 的实例来定义它的意义。对这个示例来讲,`+++` 被实现为“前缀双自增”运算符。它使用了前面定义的复合加法运算符来让矩阵对自身进行相加,从而让 `Vector2D` 实例的 `x` 属性和 `y` 属性的值翻倍:
上面的代码定义了一个新的名为 `+++` 的前缀运算符。对于这个运算符,在 Swift 中并没有意义,因此我们针对 `Vector2D` 的实例来定义它的意义。对这个示例来讲,`+++` 被实现为“前缀双自增”运算符。它使用了前面定义的复合加法运算符来让矩阵对自身进行相加,从而让 `Vector2D` 实例的 `x` 属性和 `y` 属性的值翻倍。实现 `+++` 运算符的方式如下
```swift
prefix func +++ (inout vector: Vector2D) -> Vector2D {
vector += vector
return vector
extension Vector2D {
static prefix func +++ (vector: inout Vector2D) -> Vector2D {
vector += vector
return vector
}
}
```
`Vector2D``+++` 的实现和 `++` 的实现很相似,唯一不同的是前者对自身进行相加,而后者是与另一个值为 `(1.0, 1.0)` 的向量相加。
```swift
var toBeDoubled = Vector2D(x: 1.0, y: 4.0)
let afterDoubling = +++toBeDoubled
// toBeDoubled 现在的值为 (2.0, 8.0)
@ -463,20 +460,20 @@ let afterDoubling = +++toBeDoubled
```
<a name="precedence_and_associativity_for_custom_infix_operators"></a>
### 自定义中缀运算符的优先级和结合性
### 自定义中缀运算符的优先级
自定义中缀运算符也可以指定优先级和结合性。[优先级和结合性](#precedence_and_associativity)中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。
每个自定义中缀运算符都属于某个优先级组。这个优先级组指定了这个运算符和其他中缀运算符的优先级和结合性。[优先级和结合性](#precedence_and_associativity)中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。
结合性可取的值有` left``right``none`。当左结合运算符跟其他相同优先级的左结合运算符写在一起时,会跟左边的值进行结合。同理,当右结合运算符跟其他相同优先级的右结合运算符写在一起时,会跟右边的值进行结合。而非结合运算符不能跟其他相同优先级的运算符写在一起
而没有明确放入优先级组的自定义中缀运算符会放到一个默认的优先级组内,其优先级高于三元运算符
结合性的默认值是 `none`,优先级的默认值 `100`
以下例子定义了一个新的中缀运算符 `+-`,此运算符的结合性为 `left`,并且它的优先级为 `140`
以下例子定义了一个新的自定义中缀运算符 `+-`,此运算符属于 `AdditionPrecedence` 优先组:
```swift
infix operator +- { associativity left precedence 140 }
func +- (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y - right.y)
infix operator +-: AdditionPrecedence
extension Vector2D {
static func +- (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y - right.y)
}
}
let firstVector = Vector2D(x: 1.0, y: 2.0)
let secondVector = Vector2D(x: 3.0, y: 4.0)
@ -484,7 +481,7 @@ let plusMinusVector = firstVector +- secondVector
// plusMinusVector 是一个 Vector2D 实例,并且它的值为 (4.0, -2.0)
```
这个运算符把两个向量的 `x` 值相加,同时用第一个向量的 `y` 值减去第二个向量的 `y` 值。因为它本质上是属于“相加型”运算符,所以将它的结合性和优先级被分别设置为 `left``140`,这与 `+``-` 等默认的中缀“相加型”运算符相同的。关于 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)。
这个运算符把两个向量的 `x` 值相加,同时用第一个向量的 `y` 值减去第二个向量的 `y` 值。因为它本质上是属于“相加型”运算符,所以将它放置 `+``-` 等默认的中缀“相加型”运算符相同的优先级组中。关于 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)。而更多关于优先级组以及自定义操作符和优先级组的语法,请参考[运算符声明](#operator_declaration)
> 注意
> 当定义前缀与后缀运算符的时候,我们并没有指定优先级。然而,如果对同一个值同时使用前缀与后缀运算符,则后缀运算符会先参与运算。

View File

@ -5,32 +5,41 @@
> 翻译:[superkam](https://github.com/superkam)
> 校对:[numbbbbb](https://github.com/numbbbbb)
> 2.0
> 翻译+校对:[buginux](https://github.com/buginux)
> 2.1
> 翻译:[mmoaay](https://github.com/mmoaay)
> 2.2
> 翻译+校对:[星夜暮晨](https://github.com/semperidem)2016-04-06
本页包含内容:
- [空白与注释](#whitespace_and_comments)
- [标识符](#identifiers)
- [关键字和标点符号](#keywords)
- [字面量](#literals)
- [整数字面量](#integer_literals)
- [浮点数字面量](#floating_point_literals)
- [字符串字面量](#string_literals)
- [整数字面量](#integer_literals)
- [浮点数字面量](#floating_point_literals)
- [字符串字面量](#string_literals)
- [运算符](#operators)
Swift 的“词法结构描述了能构成该语言中合法符号的字符序列。这些合法符号组成了语言中最底层的构建基块,并在之后的章节中用于描述语言的其他部分。一个合法符号由一个标识符、关键字、标点符号、字面量或运算符组成。
Swift 的*“词法结构 (lexical structure)”* 描述了能构成该语言中合法符号 (token) 的字符序列。这些合法符号组成了语言中最底层的构建基块,并在之后的章节中用于描述语言的其他部分。一个合法符号由一个标识符 (identifier)、关键字 (keyword)、标点符号 (punctuation)、字面量 (literal) 或运算符 (operator) 组成。
通常情况下,符号是根据随后将介绍的语法约束,由 Swift 源文件的输入文本中提取可能的最长子串生成。这种方法称为“最长匹配,或者“最大适合
通常情况下,通过考虑输入文本当中可能的最长子串,并且在随后将介绍的语法约束之下,根据随后将介绍的语法约束生成的,根据 Swift 源文件当中的字符来生成相应的“符号”。这种方法称为*“最长匹配 (longest match)”*,或者*“最大适合(maximal munch)”*
<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。多行注释由 `/*` 开始,以 `*/` 结束。注释允许嵌套,但注释标记必须匹配。
@ -39,9 +48,9 @@ Swift 的“词法结构”描述了能构成该语言中合法符号的字符
<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` 等等。这些命名在闭包作用域范围内是合法的标识符。
@ -51,6 +60,7 @@ Swift 的“词法结构”描述了能构成该语言中合法符号的字符
> *标识符* → [*头部标识符*](#identifier-head) [*标识符字符组*](#identifier-characters)<sub>可选</sub>
> *标识符* → \`[*头部标识符*](#identifier-head) [*标识符字符组*](#identifier-characters)<sub>可选</sub>\`
> *标识符* → [*隐式参数名*](#implicit-parameter-name)
<a id="identifier-list"></a>
> *标识符列表* → [*标识符*](#identifier) | [*标识符*](#identifier) **,** [*标识符列表*](#identifier-list)
@ -76,7 +86,7 @@ Swift 的“词法结构”描述了能构成该语言中合法符号的字符
> *标识符字符* → 数值 0 - 9
> *标识符字符* → U+0300U+036FU+1DC0U+1DFFU+20D0U+20FF或者 U+FE20U+FE2F
> *标识符字符* → [*头部标识符*](#identifier-head)
<a id="identifier-characters"></a>
> <a id="identifier-characters"></a>
> *标识符字符组* → [*标识符字符*](#identifier-character) [*标识符字符组*](#identifier-characters)<sub>可选</sub>
<a id="implicit-parameter-name"></a>
@ -85,20 +95,21 @@ Swift 的“词法结构”描述了能构成该语言中合法符号的字符
<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>
## 字面量
字面量用来表示源码中某种特定类型的值,比如一个数字或字符串。
*字面量 (literal)* 用来表示源码中某种特定类型的值,比如一个数字或字符串。
下面是字面量的一些示例:
@ -106,7 +117,7 @@ Swift 的“词法结构”描述了能构成该语言中合法符号的字符
42 // 整数字面量
3.14159 // 浮点数字面量
"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`。
@ -114,26 +125,25 @@ true // 布尔值字面量
当为一个字面量值指定了类型标注的时候,这个标注的类型必须能通过这个字面量值实例化。也就是说,这个类型必须符合这些 Swift 标准库协议中的一个:整数字面量的 `IntegerLiteralConvertible` 协议、浮点数字面量的 `FloatingPointLiteralConvertible` 协议、字符串字面量的 `StringLiteralConvertible` 协议以及布尔值字面量的 `BooleanLiteralConvertible` 协议。比如,`Int8` 符合 `IntegerLiteralConvertible` 协议,因此它能在 `let x: Int8 = 42` 这个声明中作为整数字面量 `42` 的类型标注。
> 字面量语法
> *字面量* → [*数值字面量*](#numeric-literal) | [*字符串字面量*](#string-literal) | [*布尔值字面量*](#boolean-literal) | [*nil 字面量*](#nil-literal)
<a id="numeric-literal"></a>
> *数值字面量* → **-**<sub>可选</sub> [*整数字面量*](#integer-literal) | **-**<sub>可选</sub> [*浮点数字面量*](#floating-point-literal)
<a id="boolean-literal"></a>
> <a id="boolean-literal"></a>
> *布尔值字面量* → **true** | **false**
<a id="nil-literal"></a>
> <a id="nil-literal"></a>
> *nil 字面量* → **nil**
<a id="integer_literals"></a>
### 整数字面量
整数字面量表示未指定精度整数的值。整数字面量默认用十进制表示,可以加前缀来指定其他的进制二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x`。
*整数字面量 (Integer Literals)* 表示未指定精度整数的值。整数字面量默认用十进制表示,可以加前缀来指定其他的进制二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x`。
十进制字面量包含数字 `0` 至 `9`。二进制字面量只包含 `0` 或 `1`,八进制字面量包含数字 `0` 至 `7`,十六进制字面量包含数字 `0` 至 `9` 以及字母 `A` 至 `F`(大小写均可)。
负整数的字面量在整数字面量前加负号 `-`,比如 `-42`。
整型字面面可以使用下划线 `_` 来增加数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。
整型字面面可以使用下划线 (`_`) 来增加数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`这同样也会被系统所忽略,并不会影响字面量的值。
除非特别指定,整数字面量的默认推导类型为 Swift 标准库类型中的 `Int`。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 [整数](../chapter2/01_The_Basics.html#integers)。
@ -149,54 +159,54 @@ true // 布尔值字面量
> *二进制字面量* → **0b** [*二进制数字*](#binary-digit) [*二进制字面量字符组*](#binary-literal-characters)<sub>可选</sub>
> <a id="binary-digit"></a>
> *二进制数字* → 数值 0 到 1
<a id="binary-literal-character"></a>
> <a id="binary-literal-character"></a>
> *二进制字面量字符* → [*二进制数字*](#binary-digit) | _
<a id="binary-literal-characters"></a>
> <a id="binary-literal-characters"></a>
> *二进制字面量字符组* → [*二进制字面量字符*](#binary-literal-character) [*二进制字面量字符组*](#binary-literal-characters)<sub>可选</sub>
<a id="octal-literal"></a>
> *八进制字面量* → **0o** [*八进字数字*](#octal-digit) [*八进制字符组*](#octal-literal-characters)<sub>可选</sub>
<a id="octal-digit"></a>
> <a id="octal-digit"></a>
> *八进字数字* → 数值 0 到 7
<a id="octal-literal-character"></a>
> <a id="octal-literal-character"></a>
> *八进制字符* → [*八进字数字*](#octal-digit) | _
<a id="octal-literal-characters"></a>
> <a id="octal-literal-characters"></a>
> *八进制字符组* → [*八进制字符*](#octal-literal-character) [*八进制字符组*](#octal-literal-characters)<sub>可选</sub>
<a id="decimal-literal"></a>
> *十进制字面量* → [*十进制数字*](#decimal-digit) [*十进制字符组*](#decimal-literal-characters)<sub>可选</sub>
<a id="decimal-digit"></a>
> <a id="decimal-digit"></a>
> *十进制数字* → 数值 0 到 9
<a id="decimal-digits"></a>
> <a id="decimal-digits"></a>
> *十进制数字组* → [*十进制数字*](#decimal-digit) [*十进制数字组*](#decimal-digits)<sub>可选</sub>
<a id="decimal-literal-character"></a>
> <a id="decimal-literal-character"></a>
> *十进制字符* → [*十进制数字*](#decimal-digit) | _
<a id="decimal-literal-characters"></a>
> <a id="decimal-literal-characters"></a>
> *十进制字符组* → [*十进制字符*](#decimal-literal-character) [*十进制字符组*](#decimal-literal-characters)<sub>可选</sub>
<a id="hexadecimal-literal"></a>
> *十六进制字面量* → **0x** [*十六进制数字*](#hexadecimal-digit) [*十六进制字面量字符组*](#hexadecimal-literal-characters)<sub>可选</sub>
<a id="hexadecimal-digit"></a>
> <a id="hexadecimal-digit"></a>
> *十六进制数字* → 数值 0 到 9, 字母 a 到 f, 或 A 到 F
<a id="hexadecimal-literal-character"></a>
> <a id="hexadecimal-literal-character"></a>
> *十六进制字符* → [*十六进制数字*](#hexadecimal-digit) | _
<a id="hexadecimal-literal-characters"></a>
> <a id="hexadecimal-literal-characters"></a>
> *十六进制字面量字符组* → [*十六进制字符*](#hexadecimal-literal-character) [*十六进制字面量字符组*](#hexadecimal-literal-characters)<sub>可选</sub>
<a id="floating_point_literals"></a>
### 浮点数字面量
浮点数字面量表示未指定精度浮点数的值。
*浮点数字面量 (Floating-point literals)* 表示未指定精度浮点数的值。
浮点数字面量默认用十进制表示(无前缀),也可以用十六进制表示(加前缀 `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 位浮点数。
@ -208,19 +218,19 @@ true // 布尔值字面量
<a id="decimal-fraction"></a>
> *十进制分数* → **.** [*十进制字面量*](#decimal-literal)
<a id="decimal-exponent"></a>
> <a id="decimal-exponent"></a>
> *十进制指数* → [*十进制指数 e*](#floating-point-e) [*正负号*](#sign)<sub>可选</sub> [*十进制字面量*](#decimal-literal)
<a id="hexadecimal-fraction"></a>
> *十六进制分数* → **.** [*十六进制数字*](#hexadecimal-digit) [*十六进制字面量字符组*](#hexadecimal-literal-characters)<sub>可选</sub>
<a id="hexadecimal-exponent"></a>
> <a id="hexadecimal-exponent"></a>
> *十六进制指数* → [*十六进制指数 p*](#floating-point-p) [*正负号*](#sign)<sub>可选</sub> [*十进制字面量*](#decimal-literal)
<a id="floating-point-e"></a>
> *十进制指数 e* → **e** | **E**
<a id="floating-point-p"></a>
> <a id="floating-point-p"></a>
> *十六进制指数 p* → **p** | **P**
<a id="sign"></a>
> <a id="sign"></a>
> *正负号* → **+** | **-**
<a id="string_literals"></a>
@ -243,7 +253,7 @@ true // 布尔值字面量
* 单引号 `\'`
* Unicode 标量 `\u{`n`}`n 为一到八位的十六进制数字
字符串字面量允许在反斜杠 `\` 后的括号 `()` 中插入表达式的值。插入表达式可以包含字符串字面量,但不能包含未转义的双引号 `"`、未转义的反斜线 `\`、回车符换行符。
字符串字面量允许在反斜杠 (`\`) 后的括号 `()` 中插入表达式的值。插入表达式可以包含字符串字面量,但不能包含未转义的双引号 (`"`)、未转义的反斜线 (`\`)、回车符以及换行符。
例如,以下所有字符串字面量的值都是相同的:
@ -271,23 +281,23 @@ let textB = "Hello world"
<a id="static-string-literal"></a>
> *静态字符串字面量* → **"**[*引用文本*](#quoted-text)<sub>可选</sub>**"**
<a id="quoted-text"></a>
> <a id="quoted-text"></a>
> *引用文本* → [*引用文本项*](#quoted-text-item) [*引用文本*](#quoted-text)<sub>可选</sub>
<a id="quoted-text-item"></a>
> <a id="quoted-text-item"></a>
> *引用文本项* → [*转义字符*](#escaped-character)
> *引用文本项* → 除了 **"**、**\\**、U+000A、U+000D 以外的所有 Unicode 字符
<a id="interpolated-string-literal"></a>
> *插值字符串字面量* → **"**[*插值文本*](#interpolated-text)<sub>可选</sub>**"**
<a id="interpolated-text"></a>
> <a id="interpolated-text"></a>
> *插值文本* → [*插值文本项*](#interpolated-text-item) [*插值文本*](#interpolated-text)<sub>可选</sub>
<a id="interpolated-text-item"></a>
> <a id="interpolated-text-item"></a>
> *插值文本项* → **\\****(**[*表达式*](./04_Expressions.html)**)** | [*引用文本项*](#quoted-text-item)
<a id="escaped-character"></a>
> *转义字符* → **\\****0** | **\\****\\** | **\t** | **\n** | **\r** | **\\"** | **\\'**
> *转义字符* → **\u {** [*unicode 标量数字*](#unicode-scalar-digits) **}**
<a id="unicode-scalar-digits"></a>
> <a id="unicode-scalar-digits"></a>
> *unicode 标量数字* → 一到八位的十六进制数字
<a id="operators"></a>
@ -295,17 +305,21 @@ let textB = "Hello world"
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+FE20U+FE2F
> *运算符字符* → U+E0100U+E01EF
<a id="operator-characters"></a>
> <a id="operator-characters"></a>
> *运算符字符组* → [*运算符字符*](#operator-character) [*运算符字符组*](#operator-characters)<sub>可选</sub>
<a id="dot-operator-head"></a>
> *头部点运算符* → **..**
<a id="dot-operator-character"></a>
> <a id="dot-operator-character"></a>
> *点运算符字符* → **.** | [*运算符字符*](#operator-character)
<a id="dot-operator-characters"></a>
> <a id="dot-operator-characters"></a>
> *点运算符字符组* → [*点运算符字符*](#dot-operator-character) [*点运算符字符组*](#dot-operator-characters)<sub>可选</sub>
<a id="binary-operator"></a>
> *二元运算符* → [*运算符*](#operator)
<a id="prefix-operator"></a>
> <a id="prefix-operator"></a>
> *前缀运算符* → [*运算符*](#operator)
<a id="postfix-operator"></a>
> <a id="postfix-operator"></a>
> *后缀运算符* → [*运算符*](#operator)

View File

@ -26,7 +26,7 @@
- [类型继承子句](#type_inheritance_clause)
- [类型推断](#type_inference)
Swift 语言存在两种类型:命名型类型和复合型类型。命名型类型是指定义时可以给定名字的类型。命名型类型包括类、结构体、枚举和协议。比如,一个用户定义的类 MyClass 的实例拥有类型 MyClass。除了用户定义的命名型类型Swift 标准库也定义了很多常用的命名型类型,包括那些表示数组、字典和可选值的类型。
Swift 语言存在两种类型:命名型类型和复合型类型。命名型类型是指定义时可以给定名字的类型。命名型类型包括类、结构体、枚举和协议。比如,一个用户定义的类 `MyClass` 的实例拥有类型 `MyClass`。除了用户定义的命名型类型Swift 标准库也定义了很多常用的命名型类型,包括那些表示数组、字典和可选值的类型。
那些通常被其它语言认为是基本或原始的数据型类型,比如表示数字、字符和字符串的类型,实际上就是命名型类型,这些类型在 Swift 标准库中是使用结构体来定义和实现的。因为它们是命名型类型,因此你可以按照 [扩展](../chapter2/21_Extensions.html) 和 [扩展声明](05_Declarations.html#extension_declaration) 中讨论的那样,声明一个扩展来增加它们的行为以满足你程序的需求。
@ -36,7 +36,7 @@ Swift 语言存在两种类型:命名型类型和复合型类型。命名型
> 类型语法
<a name="type"></a>
> *类型* → [*数组类型*](#array-type) | [*字典类型*](#dictionary-type) | [*函数类型*](#function-type) | [*类型标识*](#type-identifier) | [*元组类型*](#tuple-type) | [*可选类型*](#optional-type) | [*隐式解析可选类型*](#implicitly-unwrapped-optional-type) | [*协议合成类型*](#protocol-composition-type) | [*元型类型*](#metatype-type)
> *类型* → [*数组类型*](#array-type) | [*字典类型*](#dictionary-type) | [*函数类型*](#function-type) | [*类型标识*](#type-identifier) | [*元组类型*](#tuple-type) | [*可选类型*](#optional-type) | [*隐式解析可选类型*](#implicitly-unwrapped-optional-type) | [*协议合成类型*](#protocol-composition-type) | [*元型类型*](#metatype-type) | **任意类型** | **自身类型**
<a name="type_annotation"></a>
## 类型注解
@ -53,7 +53,7 @@ func someFunction(a: Int) { /* ... */ }
> 类型注解语法
<a name="type-annotation"></a>
> *类型注解* → **:** [*特性列表*](06_Attributes.html#attributes)<sub>可选</sub> [*类型*](#type)
> *类型注解* → **:** [*特性列表*](06_Attributes.html#attributes)<sub>可选</sub> **输入输出参数**<sub>可选</sub> [*类型*](#type)
<a name="type_identifier"></a>
## 类型标识符
@ -87,20 +87,27 @@ var someValue: ExampleModule.MyType
元组类型是使用括号括起来的零个或多个类型,类型间用逗号隔开。
你可以使用元组类型作为一个函数的返回类型,这样就可以使函数返回多个值。你也可以命名元组类型中的元素,然后用这些名字来引用每个元素的值。元素的名字由一个标识符紧跟一个冒号 `(:)` 组成。[函数和多返回值](../chapter2/06_Functions.html#functions_with_multiple_return_values) 章节里有一个展示上述特性的例子。
当一个元组类型的元素有名字的时候,这个名字就是类型的一部分。
```swift
var someTuple = (top: 10, bottom: 12) // someTuple 的类型为 (top: Int, bottom: Int)
someTuple = (top: 4, bottom: 42) // 正确:命名类型匹配
someTuple = (9, 99) // 正确:命名类型被自动推断
someTuple = (left: 5, right: 5) // 错误:命名类型不匹配
```
`Void` 是空元组类型 `()` 的别名。如果括号内只有一个元素,那么该类型就是括号内元素的类型。比如,`(Int)` 的类型是 `Int` 而不是 `(Int)`。所以,只有当元组类型包含的元素个数在两个及以上时才可以命名元组元素。
> 元组类型语法
> 元组类型语法
<a name="tuple-type"></a>
> *元组类型* → **(** [*元组类型主体*](#tuple-type-body)<sub>可选</sub> **)**
<a name="tuple-type-body"></a>
> *元组类型主体* → [*元组类型元素列表*](#tuple-type-element-list) **...**<sub>可选</sub>
> *元组类型* → **(** [*元组类型元素列表*](#tuple-type-element-list) <sub>可选</sub> **)**
<a name="tuple-type-element-list"></a>
> *元组类型元素列表* → [*元组类型元素*](#tuple-type-element) | [*元组类型元素*](#tuple-type-element) **,** [*元组类型元素列表*](#tuple-type-element-list)
<a name="tuple-type-element"></a>
> *元组类型元素* → [*特性列表*](06_Attributes.html#attributes)<sub>可选</sub> **inout**<sub>可选</sub> [*类型*](#type) | **inout**<sub>可选</sub> [*元素名*](#element-name) [*类型注解*](#type-annotation)
> *元组类型元素* → [*元素名*](#element-name) [*类型注解*](#type-annotation) | [*类型*](#type)
<a name="element-name"></a>
> *元素名* → [*标识符*](02_Lexical_Structure.html#identifier)
> *元素名* → [*标识符*](02_Lexical_Structure.html#identifier)
<a name="function_type"></a>
## 函数类型
@ -109,22 +116,49 @@ var someValue: ExampleModule.MyType
> `参数类型` -> `返回值类型`
由于参数类型和返回值类型可以是元组类型,所以函数类型支持多参数与多返回值的函数与方法。
参数类型是由逗号间隔的类型列表。由于参数类型和返回值类型可以是元组类型,所以函数类型支持多参数与多返回值的函数与方法。
你可以对函数参数使用 `autoclosure` 特性。这会自动将参数表达式转化为闭包,表达式的结果即闭包返回值。这从语法结构上提供了一种便捷:延迟对表达式的求值,直到其值在函数体中被使用。以自动闭包做为参数的函数类型的例子详见 [自动闭包](../chapter2/07_Closures.html#autoclosures) 。
函数类型可以拥有一个可变长参数作为参数类型中的最后一个参数。从语法角度上讲,可变长参数由一个基础类型名字紧随三个点(`...`)组成,如 `Int...`。可变长参数被认为是一个包含了基础类型元素的数组。即 `Int...` 就是 `[Int]`。关于使用可变长参数的例子,请参阅 [可变参数](../chapter2/06_Functions.html#variadic_parameters)。
为了指定一个 `in-out` 参数,可以在参数类型前加 `inout` 前缀。但是你不可以对可变长参数或返回值类型使用 `inout`。关于这种参数的详细讲解请参阅 [输入输出参数](../chapter2/06_Functions.html#in_out_parameters)。
函数和方法中的参数名并不是函数类型的一部分。例如:
```swift
func someFunction(left: Int, right: Int) {}
func anotherFunction(left: Int, right: Int) {}
func functionWithDifferentLabels(top: Int, bottom: Int) {}
var f = someFunction // 函数f的类型为 (Int, Int) -> Void, 而不是 (left: Int, right: Int) -> Void.
f = anotherFunction // 正确
f = functionWithDifferentLabels // 正确
func functionWithDifferentArgumentTypes(left: Int, right: String) {}
func functionWithDifferentNumberOfArguments(left: Int, right: Int, top: Int) {}
f = functionWithDifferentArgumentTypes // 错误
f = functionWithDifferentNumberOfArguments // 错误
```
柯里化函数的函数类型从右向左进行组合。例如,函数类型 `Int -> Int -> Int` 可以理解为 `Int -> (Int -> Int)`,也就是说,该函数类型的参数为 `Int` 类型,其返回类型是一个参数类型为 `Int`,返回类型为 `Int` 的函数类型。关于柯里化函数的讨论见章节 [柯里化函数](05_Declarations.html#curried_functions)。
如果一个函数类型包涵多个箭头(->),那么函数类型从右向左进行组合。例如,函数类型 `Int -> Int -> Int` 可以理解为 `Int -> (Int -> Int)`,也就是说,该函数类型的参数为 `Int` 类型,其返回类型是一个参数类型为 `Int`,返回类型为 `Int` 的函数类型。
函数类型若要抛出错误就必须使用 `throws` 关键字来标记,若要重抛错误则必须使用 `rethrows` 关键字来标记。`throws` 关键字是函数类型的一部分,非抛出函数是抛出函数函数的一个子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。对于柯里化函数,`throws` 关键字只应用于最里层的函数。抛出和重抛函数的相关描述见章节 [抛出函数与方法](05_Declarations.html#throwing_functions_and_methods) 和 [重抛函数与方法](05_Declarations.html#rethrowing_functions_and_methods)。
函数类型若要抛出错误就必须使用 `throws` 关键字来标记,若要重抛错误则必须使用 `rethrows` 关键字来标记。`throws` 关键字是函数类型的一部分,非抛出函数是抛出函数函数的一个子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。抛出和重抛函数的相关描述见章节 [抛出函数与方法](05_Declarations.html#throwing_functions_and_methods) 和 [重抛函数与方法](05_Declarations.html#rethrowing_functions_and_methods)。
> 函数类型语法
<a name="function-type"></a>
> *函数类型* → [*类型*](#type) **throws**<sub>可选</sub> **->** [*类型*](#type)
> *函数类型* → [*类型*](#type) **rethrows**<sub>可选</sub> **->** [*类型*](#type)
<a name="function-type"></a>
> *函数类型* → [*特性列表*](06_Attributes.html#attributes)<sub>可选</sub> [*函数类型子句*](#function-type-argument-clause) **throws**<sub>可选</sub> **->** [*类型*](#type)
> *函数类型* → [*特性列表*](06_Attributes.html#attributes)<sub>可选</sub> [*函数类型子句*](#function-type-argument-clause) **rethrows­** **->** [*类型*](#type)
<a name="function-type-argument-clause"></a>
> *函数类型子句* → (­)­
> *函数类型子句* → ([*函数类型参数列表*](#function-type-argument-list)*...*­<sub>可选</sub>)­
<a name="function-type-argument-list"></a>
> *函数类型参数列表* → [*函数类型参数*](function-type-argument) | [*函数类型参数*](function-type-argument) [*函数类型参数列表*](#function-type-argument-list)
<a name="function-type-argument"></a>
> *函数类型参数* → [*特性列表*](06_Attributes.html#attributes)<sub>可选</sub> **输入输出参数**<sub>可选</sub> [*类型*](#type) | [*参数标签*](#argument-label) [*类型注解*](#type-annotation)
<a name="argument-label"></a>
> *参数标签* → [*标识符*](02_Lexical_Structure.html#identifier)
<a name="array_type"></a>
## 数组类型
@ -194,7 +228,7 @@ var optionalInteger: Optional<Int>
在上述两种情况下,变量 `optionalInteger` 都被声明为可选整型类型。注意在类型和 `?` 之间没有空格。
类型 `Optional<Wrapped>` 是一个枚举,有两个成员,`None``Some(Wrapped)`,用来表示可能有也可能没有的值。任意类型都可以被显式地声明(或隐式地转换)为可选类型。如果你在声明或定义可选变量或属性的时候没有提供初始值,它的值则会自动赋为默认值 `nil`
类型 `Optional<Wrapped>` 是一个枚举,有两个成员,`none``some(Wrapped)`,用来表示可能有也可能没有的值。任意类型都可以被显式地声明(或隐式地转换)为可选类型。如果你在声明或定义可选变量或属性的时候没有提供初始值,它的值则会自动赋为默认值 `nil`
如果一个可选类型的实例包含一个值,那么你就可以使用后缀运算符 `!` 来获取该值,正如下面描述的:
@ -216,21 +250,29 @@ optionalInteger! // 42
<a name="implicitly_unwrapped_optional_type"></a>
## 隐式解析可选类型
Swift 语言定义后缀 `!` 作为标准库中命名类型 `ImplicitlyUnwrappedOptional<Wrapped>` 的语法糖。换句话说,下面两个声明等价:
当可以被访问时,Swift 语言定义后缀 `!` 作为标准库中命名类型 `Optional<Wrapped>` 的语法糖,来实现自动解包的功能。换句话说,下面两个声明等价:
```swift
var implicitlyUnwrappedString: String!
var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional<String>
var explicitlyUnwrappedString: Optional<String>
```
上述两种情况下,变量 `implicitlyUnwrappedString` 被声明为一个隐式解析可选类型的字符串。注意类型与 `!` 之间没有空格。
你可以在使用可选类型的地方使用隐式解析可选类型比如,你可以将隐式解析可选类型的值赋给变量、常量和可选属性,反之亦然。
注意类型与 `!` 之间没有空格。
由于隐式解包修改了包涵其类型的声明语义,嵌套在元组类型或泛型的可选类型比如字典元素类型或数组元素类型),不能被标记为隐式解包。例如:
```swift
let tupleOfImplicitlyUnwrappedElements: (Int!, Int!) // 错误
let implicitlyUnwrappedTuple: (Int, Int)! // 正确
let arrayOfImplicitlyUnwrappedElements: [Int!] // 错误
let implicitlyUnwrappedArray: [Int]! // 正确
```
由于隐式解析可选类型和可选类型有同样的表达式`Optional<Wrapped>`,你可以在使用可选类型的地方使用隐式解析可选类型。比如,你可以将隐式解析可选类型的值赋给变量、常量和可选属性,反之亦然。
正如可选类型一样,你在声明隐式解析可选类型的变量或属性的时候也不用指定初始值,因为它有默认值 `nil`
由于隐式解析可选类型的值会在使用时自动解析,所以没必要使用操作符 `!` 来解析它。也就是说,如果你使用值为 `nil` 的隐式解析可选类型,就会导致运行错误。
可以使用可选链式调用来在隐式解析可选表达式上选择性地执行操作。如果值为 `nil`,就不会执行任何操作,因此也不会产生运行错误。
关于隐式解析可选类型的更多细节,请参阅 [隐式解析可选类型](../chapter2/01_The_Basics.html#implicityly_unwrapped_optionals)。
@ -246,19 +288,19 @@ var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional<String>
协议合成类型的形式如下:
> protocol<`Protocol 1`, `Procotol 2`>
> `Protocol 1` & `Procotol 2`
协议合成类型允许你指定一个值,其类型符合多个协议的要求且不需要定义一个新的命名型协议来继承它想要符合的各个协议。比如,协议合成类型 `protocol<Protocol A, Protocol B, Protocol C>` 等效于一个从 `Protocol A``Protocol B` `Protocol C` 继承而来的新协议 `Protocol D`,很显然这样做有效率的多,甚至不需引入一个新名字。
协议合成类型允许你指定一个值,其类型符合多个协议的要求且不需要定义一个新的命名型协议来继承它想要符合的各个协议。比如,协议合成类型 `Protocol A & Protocol B & Protocol C` 等效于一个从 `Protocol A``Protocol B` `Protocol C` 继承而来的新协议 `Protocol D`,很显然这样做有效率的多,甚至不需引入一个新名字。
协议合成列表中的每项必须是协议名或协议合成类型的类型别名。如果列表为空,它就会指定一个空协议合成列表,每个类型都符合它。
协议合成列表中的每项必须是协议名或协议合成类型的类型别名。
> 协议合成类型语法
<a name="protocol-composition-type"></a>
> *协议合成类型* → **protocol** **<** [*协议标识符列表*](#protocol-identifier-list)<sub>可选</sub> **>**
<a name="protocol-identifier-list"></a>
> *协议标识符列表* → [*协议标识符*](#protocol-identifier) | [*协议标识符*](#protocol-identifier) **,** [*协议标识符列表*](#protocol-identifier-list)
> *协议合成类型* → [*协议标识符*](#protocol-identifier) & [*协议合成延续*](#protocol-composition-continuation)
<a name="protocol-composition-continuation"></a>
> *协议合成延续* → [*协议标识符*](#protocol-identifier) | [*协议合成类型*](#protocol-composition-type)
<a name="protocol-identifier"></a>
> *协议标识符* → [*类型标识符*](#type-identifier)
> *协议标识符* → [*类型标识符*](#type-identifier)
<a name="metatype_type"></a>
## 元类型
@ -267,7 +309,7 @@ var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional<String>
类、结构体或枚举类型的元类型是相应的类型名紧跟 `.Type`。协议类型的元类型——并不是运行时符合该协议的具体类型——而是该协议名字紧跟 `.Protocol`。比如,类 `SomeClass` 的元类型就是 `SomeClass.Type`,协议 `SomeProtocol` 的元类型就是 `SomeProtocal.Protocol`
你可以使用后缀 `self` 表达式来获取类型。比如,`SomeClass.self` 返回 `SomeClass` 本身,而不是 `SomeClass` 的一个实例。同样,`SomeProtocol.self` 返回 `SomeProtocol` 本身,而不是运行时符合 `SomeProtocol` 的某个类型的实例。还可以对类型的实例使用 `dynamicType` 表达式来获取该实例在运行阶段的类型,如下所示:
你可以使用后缀 `self` 表达式来获取类型。比如,`SomeClass.self` 返回 `SomeClass` 本身,而不是 `SomeClass` 的一个实例。同样,`SomeProtocol.self` 返回 `SomeProtocol` 本身,而不是运行时符合 `SomeProtocol` 的某个类型的实例。还可以对类型的实例使用 `type(of:)` 表达式来获取该实例在运行阶段的类型,如下所示:
```swift
class SomeBaseClass {
@ -283,19 +325,19 @@ class SomeSubClass: SomeBaseClass {
let someInstance: SomeBaseClass = SomeSubClass()
// someInstance 在编译期是 SomeBaseClass 类型,
// 但是在运行期则是 SomeSubClass 类型
someInstance.dynamicType.printClassName()
type(of: someInstance).printClassName()
// 打印 “SomeSubClass”
```
可以使用恒等运算符(`===``!==`)来测试一个实例的运行时类型和它的编译时类型是否一致。
```swift
if someInstance.dynamicType === SomeBaseClass.self {
print("The dynamic type of someInstance is SomeBaseCass")
} else {
print("The dynamic type of someInstance isn't SomeBaseClass")
}
// 打印 The dynamic type of someInstance isn't SomeBaseClass”
if type(of: someInstance) === someInstance.self {
print("The dynamic and static type of someInstance are the same")
} else {
print("The dynamic and static type of someInstance are different")
}
// 打印 "The dynamic and static type of someInstance are different"
```
可以使用初始化表达式从某个类型的元类型构造出一个该类型的实例。对于类实例,被调用的构造器必须使用 `required` 关键字标记,或者整个类使用 `final` 关键字标记。
@ -321,7 +363,7 @@ let anotherInstance = metatype.init(string: "some string")
<a name="type_inheritance_clause"></a>
## 类型继承子句
类型继承子句被用来指定一个命名型类型继承自哪个类、采纳哪些协议。类型继承子句也用来指定一个类类型专属协议。类型继承子句开始于冒号 `:`,其后是类的超类或者一系列类型标识符
类型继承子句被用来指定一个命名型类型继承自哪个类、采纳哪些协议。类型继承子句也用来指定一个类类型专属协议。类型继承子句开始于冒号 `:`,其后是所需要的类、类型标识符列表或两者都有
类可以继承单个超类,采纳任意数量的协议。当定义一个类时,超类的名字必须出现在类型标识符列表首位,然后跟上该类需要采纳的任意数量的协议。如果一个类不是从其它类继承而来,那么列表可以以协议开头。关于类继承更多的讨论和例子,请参阅 [继承](../chapter2/13_Inheritance.html)。
@ -354,3 +396,5 @@ let eFloat: Float = 2.71828 // eFloat 的类型为 Float
```
Swift 中的类型推断在单独的表达式或语句上进行。这意味着所有用于类型推断的信息必须可以从表达式或其某个子表达式的类型检查中获取到。

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -14,137 +14,132 @@
本页内容包括:
- [声明特性](#declaration_attributes)
- [Interface Builder 使用的声明特性](#declaration_attributes_used_by_interface_builder)
- [Interface Builder 使用的声明特性](#declaration_attributes_used_by_interface_builder)
- [类型特性](#type_attributes)
特性提供了关声明和类型的更多信息。在 Swift 中有两特性,分别用于修饰声明和类型。
特性提供了关声明和类型的更多信息。在Swift中有两特性,分别用于修饰声明和类型。
通过以下方式指定一个特性符号 `@` 后面跟特性名,如果包含参数,则把参数带上
您可以通过以下方式指定一个特性:符号`@`后跟特性的名称和特性接收的任何参数
> @`特性名`
> @`特性名`(`特性参数`)
> @ `特性名`
> @ `特性名``特性参数`
有些声明特性通过接收参数来指定特性的更多信息以及它是如何修饰某个特定的声明的。这些特性的参数写在圆括号内,它们的格式由它们所属的特性来定义。
<a name="declaration_attributes"></a>
## 声明特性
声明特性只能应用于声明。然而,你也可以将 `noreturn` 特性应用于函数或方法类型。
`autoclosure`
这个特性通过把表达式自动封装成无参数的闭包来延迟表达式的计算。它可以修饰类型为返回表达式结果类型的无参数函数类型的函数参数。含有 `autoclosure` 特性的声明同时也具有 `noescape` 的特性,除非传递可选特性参数 `escaping`。关于如何使用 `autoclosure` 特性的例子,请参阅 [自动闭包](../chapter2/07_Closures.md#autoclosures) 和 [函数类型](03_Types.md#function_type)。
##声明特性
声明特性只能应用于声明。
`available`
`available` 特性用于声明时,意味着该声明的生命周期会依赖于特定的平台和操作系统版本。
`available` 特性用于声明时,表示该声明的生命周期特定的平台和操作系统版本有关
`available` 特性经常与参数列表一同出现,该参数列表至少有两个特性参数,参数之间由逗号分隔。这些参数由以下这些平台名字中的一个起头:
- `iOS`
- `iOSApplicationExtension`
- `OSX`
- `OSXApplicationExtension`
- `watchOS`
- `watchOSApplicationExtension`
- `tvOS`
- `tvOSApplicationExtension`
- iOS
- iOSApplicationExtension
- macOS
- macOSApplicationExtension
- watchOS
- watchOSApplicationExtension
- tvOS
- tvOSApplicationExtension
当然,你也可以用一个星号(`*`)来表示上面提到的所有平台。
剩下的参数,可以以任何顺序出现,并且可以添加关于声明生命周期的附加信息,包括重要事件。
- `unavailable` 参数表示该声明在指定的平台上是无效的。
当然,你也可以用一个星号(*)来表示上面提到的所有平台。
其余的参数,可以按照任何顺序出现,并且可以添加关于声明生命周期的附加信息,包括重要事件。
- `unavailable`参数表示该声明在指定的平台上是无效的。
- `introduced` 参数表示指定平台从哪一版本开始引入该声明。格式如下:
`introduced`=`版本号`
`introduced`=`版本号`
版本号由一个十进制正整数或正浮点数构成
*版本号*由一个或多个正整数构成,由句点分隔的
- `deprecated` 参数表示指定平台从哪一版本开始弃用该声明。格式如下:
- `deprecated`参数表示指定平台从哪一版本开始弃用该声明。格式如下:
`deprecated`=`版本号`
`deprecated`=`版本号`
版本号由一个十进制正整数或正浮点数构成
可选的*版本号*由一个或多个正整数构成,由句点分隔的。省略版本号表示该声明目前已弃用,当弃用出现时无需给出任何有关信息。如果你省略了版本号,冒号(:)也可省略
- `obsoleted` 参数表示指定平台从哪一版本开始废弃该声明。当一个声明被废弃后,它就从平台中移除,不能再被使用。格式如下:
`obsoleted`=`版本号`
`obsoleted`=`版本号`
版本号由一个十进制正整数或正浮点数构成
*版本号*由一个或多个正整数构成,由句点分隔的
- `message` 参数用来提供文本信息。当使用被弃用或者被废弃的声明时,编译器会抛出警告或错误信息。格式如下:
`message`=`信息内容`
`message`=`信息内容`
信息内容由一个字符串字面量构成。
信息内容由一个字符串构成。
- `renamed` 参数用来提供文本信息,用以表示被重命名的声明的新名字。当使用声明的旧名字时,编译器会报错提示新名字。格式如下:
`renamed`=`新名字`
`renamed`=`新名字`
新名字由一个字符串字面量构成。
新名字由一个字符串构成。
你可以将 `renamed` 参数和 `unavailable` 参数以及类型别名声明组合使用,以此向用户表示某个声明已经被重命名。当某个声明的名字在一个框架或者库的不同发布版本间发生变化时,这会相当有用。
你可以将`renamed` 参数和 `unavailable` 参数以及类型别名声明组合使用,以此向用户表示某个声明已经被重命名。当某个声明的名字在一个框架或者库的不同发布版本间发生变化时,这会相当有用。
```swift
// 首发版本
protocol MyProtocol {
// 这里是协议定义
// 这里是协议定义
}
```
```swift
// 后续版本重命名了 MyProtocol
protocol MyRenamedProtocol {
// 这里是协议定义
// 这里是协议定义
}
@available(*, unavailable, renamed="MyRenamedProtocol")
@available(*, unavailable, renamed:"MyRenamedProtocol")
typealias MyProtocol = MyRenamedProtocol
```
你可以在某个声明上使用多个 `available` 特性,以指定该声明在不同平台上的有效性。编译器只有在当前目标平台和 `available` 特性中指定的平台匹配时,才会使用 `available` 特性。
你可以在某个声明上使用多个 `available` 特性,以指定该声明在不同平台上的可用性。编译器只有在当前目标平台和 `available` 特性中指定的平台匹配时,才会使用 `available` 特性。
如果 `available` 特性除了平台名称参数外,只指定了一个 `introduced` 参数,那么可以使用以下简写语法代替:
> @available(`平台名称` `版本号`, *)
@available平台名称 版本号,*
`available` 特性的简写语法可以简明地表达出声明在多个平台上的可用性。尽管这两种形式在功能上是相同的,但请尽可能地使用简写语法形式。
```swift
@available(iOS 8.0, OSX 10.10, *)
@available(iOS 10.0, macOS 10.12, *)
class MyClass {
// 这里是类定义
// 这里是类定义
}
```
`discardableResult`
该特性用于的函数或方法声明,以抑制编译器中 函数或方法的返回值被调而没有使用其结果的警告。
`GKInspectable`
应用此属性暴露一个自定义GameplayKit组件属性给SpriteKit编辑器UI。
`objc`
该特性用于修饰任何可以在 Objective-C 中表示的声明。比如,非嵌套类、协议、非泛型枚举(仅限原始值为整型的枚举)、类和协议中的属性和方法(包括存取方法)、构造器、析构器以及下标。`objc` 特性告诉编译器这个声明可以在 Objective-C 代码中使用。
该特性用于修饰任何可以在 Objective-C 中表示的声明。比如,非嵌套类、协议、非泛型枚举(仅限原始值为整型的枚举)、类和协议中的属性和方法(包括存取方法)、构造器、析构器以及下标运算符`objc` 特性告诉编译器这个声明可以在 Objective-C 代码中使用。
标有 `objc` 特性的类必须继承自 Objective-C 中定义的类。如果你将 `objc` 特性应用于一个类或协议,它也会隐式地应用于类或协议中兼容 Objective-C 的成员。对于标记了 `objc` 特性的类,编译器会隐式地为它的子类添加 `objc` 特性。标记了 `objc` 特性的协议不能继承没有标记 `objc` 的协议。
如果你将 `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
@objc
class ExampleClass: NSObject {
var enabled: Bool {
@objc(isEnabled) get {
// 返回适当的值
}
}
var enabled: Bool {
@objc(isEnabled) get {
// 返回适当的值 }
}
}
```
`noescape`
在函数或者方法声明上使用该特性,它表示参数将不会被存储以供延迟执行,这将确保参数不会超出函数调用的生命周期。在使用 `noescape` 声明特性的函数类型中访问属性和方法时不需要显式地使用 `self.`。关于如何使用 `noescape` 特性的例子,请参阅 [非逃逸闭包](../chapter2/07_Closures.md#nonescaping_closures)。
`nonobjc`
该特性用于方法、属性、下标、或构造器的声明,这些声明本可以在 Objective-C 代码中使用,而使用 `nonobjc` 特性则告诉编译器这个声明不能在 Objective-C 代码中使用。
@ -153,77 +148,56 @@ class ExampleClass: NSObject {
标有 `nonobjc` 特性的方法不能重写标有 `objc` 特性的方法。然而,标有 `objc` 特性的方法可以重写标有 `nonobjc` 特性的方法。同样,标有 `nonobjc` 特性的方法不能满足标有 `@objc` 特性的协议中的方法要求。
`noreturn`
该特性用于修饰函数或方法声明,表明该函数或方法的对应类型 `T``@noreturn T`。你可以用这个特性修饰函数或方法类型,以此表明函数或方法不会返回到它的调用者。
对于没有用 `noreturn` 特性标记的函数或方法,你可以将它重写为用该特性标记的。相反,对于已经用 `noreturn` 特性标记的函数或方法,你不可以将它重写为没使用该特性标记的。当你在一个某个采纳协议的类型中实现协议方法时,该规则同样适用。
`NSApplicationMain`
在类上使用该特性表示该类是应用程序委托类,使用该特性与调用 `NSApplicationMain(_:_:)` 函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
在类上使用该特性表示该类是应用程序委托类,使用该特性与调用 `NSApplicationMain`(\_:_:) 函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
如果你不想使用这个特性,可以提供一个 `main.swift` 文件,并且提供一个 `main()` 函数去调用 `NSApplicationMain(_:_:)` 函数。比如,如果你的应用程序使用一个继承于 `NSApplication` 的自定义子类作为主要类,你可以调用 `NSApplicationMain(_:_:)` 函数而不是使用该特性。
如果你不想使用这个特性,可以提供一个 main.swift 文件,并在代码**顶层**调用`NSApplicationMain`(\_:_:) 函数,如下所示:
```swift
import AppKit
NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
```
`NSCopying`
该特性用于修饰一个类的存储型变量属性。该特性将使属性的设值方法使用传入值的副本进行赋值,这个值由传入值的 `copyWithZone(_:)` 方法返回。该属性的类型必需符合 `NSCopying` 协议。
该特性用于修饰一个类的存储型变量属性。该特性将使属性的设值方法使用传入值的副本进行赋值,这个值由传入值的 `copyWithZone`(\_:) 方法返回。该属性的类型必需符合 `NSCopying` 协议。
`NSCopying` 特性的行为与 Objective-C 中的 `copy` 特性相似。
`NSManaged`
该特性用于修饰 `NSManagedObject` 子类中的实例方法或存储型变量属性,表明它们的实现由 Core Data 在运行时基于相关实体描述动态提供。对于标记了 `NSManaged` 特性的属性Core Data 也会在运行时为其提供存储。
该特性用于修饰 `NSManagedObject` 子类中的实例方法或存储型变量属性,表明它们的实现由 `Core Data` 在运行时基于相关实体描述动态提供。对于标记了 `NSManaged` 特性的属性,`Core Data` 也会在运行时为其提供存储。应用这个特性也意味着`objc`特性。
`testable`
在导入允许测试的编译模块时,该特性用于修饰 `import` 声明,这样就能访问被导入模块中的任何标有 `internal` 访问级别修饰符的实体,犹如它们被标记了 `public` 访问级别修饰符。
在导入允许测试的编译模块时,该特性用于修饰 `import` 声明,这样就能访问被导入模块中的任何标有 `internal` 访问级别修饰符的实体,犹如它们被标记了 `public` 访问级别修饰符。测试也可以访问使用`internal`或者`public`访问级别修饰符标记的类和类成员,就像它们是`open`访问修饰符声明的。
`UIApplicationMain`
在类上使用该特性表示该类是应用程序委托类,使用该特性与调用 `UIApplicationMain(_:_:)` 函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
在类上使用该特性表示该类是应用程序委托类,使用该特性与调用 `UIApplicationMain`函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
如果你不想使用这个特性,可以提供一个 `main.swift` 文件,并且提供一个 `main()` 函数去调用 `UIApplicationMain(_:_:)` 函数。比如,如果你的应用程序使用一个继承于 `UIApplication` 的自定义子类作为主要类,你可以调用 `UIApplicationMain(_:_:_:)` 函数而不是使用该特性。
`warn_unused_result`
该特性应用于方法或函数声明,当方法或函数被调用,但其结果未被使用时,该特性会让编译器会产生警告。
当某个方法有一个对应的变异版本,但是用户却还是错误地使用了非变异版本时,你可以使用这个特性提供一个警告信息。
`warn_unused_result` 特性可以选择接受下面两个参数之中的一个。
- `message` 参数用来提供警告信息。当方法或函数被调用,但其结果未被使用时,会显示警告信息。格式如下:
`message`=`警告信息`
警告信息由一个字符串字面量构成。
- `mutable_variant` 参数用于提供方法的变异版本的名称,如果非变异方法在一个可变值上调用,但是返回结果却未被使用时,应该使用此方法的变异版本。格式如下:
`mutable_variant`=`变异版本的方法名`
变异版本的方法名由一个字符串字面量构成。
比如Swift 标准库同时为元素符合 `Comparable` 协议的集合类型提供了变异方法 `sortInPlace()` 和非变异方法 `sort()`。如果你调用了 `sort()` 方法,却没有使用它的返回结果,那么你很可能本想使用变异方法 `sortInPlace()`
如果你不想使用这个特性,可以提供一个 main.swift 文件,并在代码顶层调用 `UIApplicationMain`(\_:\_:\_:) 函数。比如,如果你的应用程序使用一个继承于 UIApplication 的自定义子类作为主要类,你可以调用 `UIApplicationMain`(\_:\_:\_:) 函数而不是使用该特性。
<a name="declaration_attributes_used_by_interface_builder"></a>
### Interface Builder 使用的声明特性
Interface Builder 特性是 Interface Builder 用来与 Xcode 同步的声明特性。Swift 提供了以下的 Interface Builder 特性:`IBAction``IBDesignable``IBInspectable`,以及 `IBOutlet`。这些特性与 Objective-C 中对应的特性在概念上是相同的。
###Interface Builder 使用的声明特性
`Interface Builder` 特性是 `Interface Builder` 用来与 Xcode 同步的声明特性。`Swift` 提供了以下的 `Interface Builder` 特性:`IBAction``IBOutlet``IBDesignable`,以及`IBInspectable` 。这些特性与 Objective-C 中对应的特性在概念上是相同的。
`IBOutlet``IBInspectable` 用于修饰一个类的属性声明,`IBAction` 特性用于修饰一个类的方法声明,`IBDesignable` 用于修饰类的声明。
<a name="type_attributes"></a>
## 类型特性
`IBAction``IBOutlet` 特性都意味着`objc`特性。
类型特性只能用于修饰类型。然而,你也可以用 `noreturn` 特性去修饰函数或方法声明。
<a name="type_attributes"></a>
##类型特性
类型特性只能用于修饰类型。
`autoclosure`
这个特性通过把表达式自动封装成无参数的闭包来延迟表达式的计算。它可以修饰类型为返回表达式结果类型的无参数函数类型的函数参数。关于如何使用 autoclosure 特性的例子,请参阅 [自动闭包](http://wiki.jikexueyuan.com/project/swift/chapter2/07_Closures.html/) 和 [函数类型](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html)。
`convention`
该特性用于修饰函数类型,它指出了函数调用的约定。
`convention` 特性总是与下面的参数之一一起出现。
convention 特性总是与下面的参数之一一起出现。
- `swift` 参数用于表示一个 Swift 函数引用。这是 Swift 中函数值的标准调用约定。
@ -233,26 +207,27 @@ Interface Builder 特性是 Interface Builder 用来与 Xcode 同步的声明特
使用 C 函数调用约定的函数也可用作使用 Objective-C 块调用约定的函数,同时使用 Objective-C 块调用约定的函数也可用作使用 Swift 函数调用约定的函数。然而,只有非泛型的全局函数、局部函数以及未捕获任何局部变量的闭包,才可以被用作使用 C 函数调用约定的函数。
`noreturn`
`escaping`
在函数或者方法声明上使用该特性,它表示参数将不会被存储以供延迟执行,这将确保参数不会超出函数调用的生命周期。在使用 `escaping` 声明特性的函数类型中访问属性和方法时不需要显式地使用 `self.`。关于如何使用 `escaping` 特性的例子,请参阅 [逃逸闭包](http://wiki.jikexueyuan.com/project/swift/chapter2/07_Closures.html)。
特性用于修饰函数或方法的类型,表明该函数或方法不会返回到它的调用者。你也可以用它标记函数或方法的声明,表示函数或方法的相应类型 `T``@noreturn T`
>特性语法
> 特性语法
> *特性 *→ @ <font color = 0x3386c8>特性名 特性参数子句</font><sub>可选</sub>
<a name="attribute"></a>
> *特性* → **@** [*特性名*](#attribute-name) [*特性参数子句*](#attribute-argument-clause)<sub>可选</sub>
<a name="attribute-name"></a>
> *特性名* → [*标识符*](02_Lexical_Structure.md#identifiers)
<a name="attribute-argument-clause"></a>
> *特性参数子句* → **(** [*均衡令牌列表*](#balanced-tokens)<sub>可选</sub> **)**
<a name="attributes"></a>
> *特性列表* → [*特性*](#attribute) [*特性列表*](#attributes)<sub>可选</sub>
> *特性名* → <font color = 0x3386c8>标识符
<a name="balanced-tokens"></a>
> *均衡令牌列表* → [*均衡令牌*](#balanced-token) [*均衡令牌列表*](#balanced-tokens)<sub>可选</sub>
<a name="balanced-token"></a>
> *均衡令牌* → **(** [*均衡令牌列表*](#balanced-tokens)<sub>可选</sub> **)**
> *均衡令牌* → **[** [*均衡令牌列表*](#balanced-tokens)<sub>可选</sub> **]**
> *均衡令牌* → **{** [*均衡令牌列表*](#balanced-tokens)<sub>可选</sub> **}**
> *均衡令牌* → **任意标识符,关键字,字面量或运算符**
> *均衡令牌* → **任意标点除了 ()[]{,或 }**
> *特性参数子句* → ( <font color = 0x3386c8>均衡令牌列表</font><sub>可选</sub> )
> *特性列表* → <font color = 0x3386c8>特性 特性列表</font><sub>可选</sub>
> *均衡令牌列表* → <font color = 0x3386c8>均衡令牌 均衡令牌列表</font><sub>可选</sub>
> *均衡令牌* → ( <font color = 0x3386c8>均衡令牌列表</font><sub>可选</sub> )
> *均衡令牌* → [ <font color = 0x3386c8>均衡令牌列表</font><sub>可选</sub> ]
> *均衡令牌* → { <font color = 0x3386c8>均衡令牌列表</font><sub>可选</sub>}
> *均衡令牌* → 任意标识符,关键字,字面量或运算符
> *均衡令牌* → 任意标点除了 ()[]{,或 }

View File

@ -233,7 +233,7 @@ case ("0", "0"):
default:
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
> 翻译+校对:[wardenNScaiyi](https:github.com/wardenNScaiyi)
> 3.0
> 翻译+校对:[chenmingjia](https:github.com/chenmingjia)
本页包含内容:
- [泛型形参子句](#generic_parameter)
@ -21,10 +24,9 @@
<a name="generic_parameter"></a>
## 泛型形参子句
泛型形参子句指定泛型类型或函数的类型形参,以及这些参数相关的约束和要求。泛型形参子句用尖括号(`<>`)包住,并且有以下两种形式:
泛型形参子句指定泛型类型或函数的类型形参,以及这些参数相关的约束和要求。泛型形参子句用尖括号(`<>`)包住,形式如下
> <`泛型形参列表`>
> <`泛型形参列表` where `类型要求`>
泛型形参列表中泛型形参用逗号分开,其中每一个采用以下形式:
@ -36,7 +38,7 @@
```swift
func simpleMax<T: Comparable>(x: T, _ y: T) -> T {
func simpleMax<T: Comparable>(_ x: T, _ y: T) -> T {
if x < y {
return y
}
@ -54,18 +56,19 @@ simpleMax(3.14159, 2.71828) // T 被推断为 Double 类型
<a name="where_clauses"></a>
### 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
struct Dictionary<Key: Hashable, Value>: CollectionType, DictionaryLiteralConvertible {

View File

@ -239,7 +239,7 @@
> *声明* → [*构造器声明*](../chapter3/05_Declarations.html#initializer_declaration)
> *声明* → [*析构器声明*](../chapter3/05_Declarations.html#deinitializer_declaration)
> *声明* → [*扩展声明*](../chapter3/05_Declarations.html#extension_declaration)
> *声明* → [*下标脚本声明*](../chapter3/05_Declarations.html#subscript_declaration)
> *声明* → [*下标声明*](../chapter3/05_Declarations.html#subscript_declaration)
> *声明* → [*运算符声明*](../chapter3/05_Declarations.html#operator_declaration)
> *声明(Declarations)集* → [*声明*](../chapter3/05_Declarations.html#declaration) [*声明(Declarations)集*](../chapter3/05_Declarations.html#declarations) _可选_
@ -373,7 +373,7 @@
> *协议成员声明* → [*协议属性声明*](../chapter3/05_Declarations.html#protocol_property_declaration)
> *协议成员声明* → [*协议方法声明*](../chapter3/05_Declarations.html#protocol_method_declaration)
> *协议成员声明* → [*协议构造器声明*](../chapter3/05_Declarations.html#protocol_initializer_declaration)
> *协议成员声明* → [*协议下标脚本声明*](../chapter3/05_Declarations.html#protocol_subscript_declaration)
> *协议成员声明* → [*协议下标声明*](../chapter3/05_Declarations.html#protocol_subscript_declaration)
> *协议成员声明* → [*协议关联类型声明*](../chapter3/05_Declarations.html#protocol_associated_type_declaration)
> *协议成员声明(Declarations)集* → [*协议成员声明*](../chapter3/05_Declarations.html#protocol_member_declaration) [*协议成员声明(Declarations)集*](../chapter3/05_Declarations.html#protocol_member_declarations) _可选_
@ -394,8 +394,8 @@
<!-- -->
> 协议下标脚本声明语法
> *协议下标脚本声明* → [*下标脚本头(Head)*](../chapter3/05_Declarations.html#subscript_head) [*下标脚本结果(Result)*](../chapter3/05_Declarations.html#subscript_result) [*getter-setter关键字(Keyword)块*](../chapter3/05_Declarations.html#getter_setter_keyword_block)
> 协议下标声明语法
> *协议下标声明* → [*下标头(Head)*](../chapter3/05_Declarations.html#subscript_head) [*下标结果(Result)*](../chapter3/05_Declarations.html#subscript_result) [*getter-setter关键字(Keyword)块*](../chapter3/05_Declarations.html#getter_setter_keyword_block)
<!-- -->
@ -426,12 +426,12 @@
<!-- -->
> 下标脚本声明语法
> *下标脚本声明* → [*下标脚本头(Head)*](../chapter3/05_Declarations.html#subscript_head) [*下标脚本结果(Result)*](../chapter3/05_Declarations.html#subscript_result) [*代码块*](../chapter3/05_Declarations.html#code_block)
> *下标脚本声明* → [*下标脚本头(Head)*](../chapter3/05_Declarations.html#subscript_head) [*下标脚本结果(Result)*](../chapter3/05_Declarations.html#subscript_result) [*getter-setter块*](../chapter3/05_Declarations.html#getter_setter_block)
> *下标脚本声明* → [*下标脚本头(Head)*](../chapter3/05_Declarations.html#subscript_head) [*下标脚本结果(Result)*](../chapter3/05_Declarations.html#subscript_result) [*getter-setter关键字(Keyword)块*](../chapter3/05_Declarations.html#getter_setter_keyword_block)
> *下标脚本头(Head)* → [*属性(Attributes)集*](../chapter3/06_Attributes.html#attributes) _可选_ [*声明修改器(declaration-modifiers)*](TODO) _可选_ **subscript** [*参数从句*](../chapter3/05_Declarations.html#parameter_clause)
> *下标脚本结果(Result)* → **->** [*属性(Attributes)集*](../chapter3/06_Attributes.html#attributes) _可选_ [*类型*](../chapter3/03_Types.html#type)
> 下标声明语法
> *下标声明* → [*下标头(Head)*](../chapter3/05_Declarations.html#subscript_head) [*下标结果(Result)*](../chapter3/05_Declarations.html#subscript_result) [*代码块*](../chapter3/05_Declarations.html#code_block)
> *下标声明* → [*下标头(Head)*](../chapter3/05_Declarations.html#subscript_head) [*下标结果(Result)*](../chapter3/05_Declarations.html#subscript_result) [*getter-setter块*](../chapter3/05_Declarations.html#getter_setter_block)
> *下标声明* → [*下标头(Head)*](../chapter3/05_Declarations.html#subscript_head) [*下标结果(Result)*](../chapter3/05_Declarations.html#subscript_result) [*getter-setter关键字(Keyword)块*](../chapter3/05_Declarations.html#getter_setter_keyword_block)
> *下标头(Head)* → [*属性(Attributes)集*](../chapter3/06_Attributes.html#attributes) _可选_ [*声明修改器(declaration-modifiers)*](TODO) _可选_ **subscript** [*参数从句*](../chapter3/05_Declarations.html#parameter_clause)
> *下标结果(Result)* → **->** [*属性(Attributes)集*](../chapter3/06_Attributes.html#attributes) _可选_ [*类型*](../chapter3/03_Types.html#type)
<!-- -->

View File

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