230 Commits

Author SHA1 Message Date
ddaa180905 fix:for -> for-in 2021-06-23 00:04:12 +08:00
aba2a5fb55 Language Guide - Structures and Classes (#1139)
* feat:添加 note,新建「并发」章节的 md 文件,修改文件顺序

* fix:删除冗余的字符串

* Revert "feat:添加 note,新建「并发」章节的 md 文件,修改文件顺序"

This reverts commit 898930b0ec.

# Conflicts:
#	source/02_language_guide/18_Concurrency.md

* feat:新增 `note` 一句

* feat:新建「并发」章节 md 文件

* fix:修改 SUMMARY.md

* Update 09_Structures_And_Classes.md

Co-authored-by: Jie Liang <lj925184928@gmail.com>
2021-06-22 10:56:03 -05:00
096f2acfc4 feat: 更新链接 (#1145) 2021-06-22 08:51:07 -05:00
8b99054c0c feat: 更新示例代码 (#1144) 2021-06-22 08:50:55 -05:00
d9d2952e85 feat:「参数名称缩写 」节第二段和第三段有更新 (#1142) 2021-06-21 18:54:23 -05:00
9b302e76cb feat: 新增对 for-in 的描述 (#1141) 2021-06-21 10:39:22 -05:00
9d8588726a Welcome to Swift - Version Compatibility (#1137)
* feat: 更新版本兼容性章节

* fix: 修复语句不顺的问题
2021-06-21 09:59:50 -05:00
59e95dc3a5 feat: 更新 swift tour 代码变更 (#1138) 2021-06-21 09:57:15 -05:00
bb4d0c7fcd eat: 更新代码示例 (#1140) 2021-06-21 09:57:01 -05:00
e0c37b748c Update README.md 2021-06-16 10:23:59 -05:00
bf18871349 Update README.md 2021-06-16 09:21:01 -05:00
13c47f495d Update 27_Advanced_Operators.md (#1110)
修正一处 typo:结核性 -> 结合性;及一处文字缺失。
2021-05-04 11:23:34 -05:00
6b38f6d7a7 Update 24_Automatic_Reference_Counting.md 2021-02-03 07:33:11 -06:00
262a06affd Update 01_The_Basics.md (#1106)
英文原文: almost any
原中文翻译: 任何的
正确翻译: 几乎所有的
2021-01-20 22:14:39 -06:00
558814b61d 修复一处错别字 (#1105)
"字句" 应为 "子句"
2021-01-11 06:55:34 -06:00
2408ee2066 修复运算符声明文档错误 (#1103)
* 修复运算符声明文档错误

* infix-operator-attributes修改为infix-operator-group
2021-01-08 22:02:32 -06:00
7725dfb9ad Update 22_Generics.md (#1099) 2020-12-07 07:09:08 -06:00
2e82e1621a Fix #1095 “会”的位置错误 (#1096) 2020-10-25 07:27:51 -05:00
f2999daf52 Update 01_The_Basics.md (#1090)
* Update 01_The_Basics.md

* Update 01_The_Basics.md

Co-authored-by: Jie Liang <lj925184928@gmail.com>
2020-10-05 08:37:33 -05:00
83500eba22 fix wrong links, fix #1086 2020-07-15 20:55:11 -05:00
be7dca4e18 Update 10_Properties.md 2020-07-15 11:00:24 -05:00
99453d1d4e GenericParametersAndArguments (#1084)
* chore: 增加新段落的翻译

* Update 09_Generic_Parameters_and_Arguments.md

Co-authored-by: wamaker <fangzhou@dxy.cn>
Co-authored-by: Jie Liang <lj925184928@gmail.com>
2020-07-09 07:52:48 -05:00
72cb5a61b8 Advanced_Operators Swift 5.3 beata (#1083) 2020-07-07 11:32:34 -05:00
c9d8b0be6d update Protocols.md (#1082)
Co-authored-by: dingtongzhou <dingtongzhou@bytedance.com>
2020-07-07 07:06:02 -05:00
9abdbd4e4a Merge pull request #1081 from Nemocdz/gh-pages
更新 epub
2020-07-06 12:07:53 +08:00
a6656c4221 update 5.3 beta version 2020-07-06 12:06:53 +08:00
b73ff1765c update 5.2 release version 2020-07-06 12:06:35 +08:00
528f2109de Merge pull request #1076 from Nemocdz/gh-pages
update Swift5.3 beta
2020-07-06 11:51:26 +08:00
bf8ac090d8 Merge pull request #1080 from Nemocdz/fix_typo
fix typo
2020-07-06 11:46:34 +08:00
e7d525781a fix typo 2020-07-06 11:43:53 +08:00
fee0c4a388 Update 07_Closures.md (#1079)
add translation
2020-07-05 22:11:37 -05:00
da42305134 fix typo 2020-07-06 10:23:41 +08:00
9a84b62fab fix typo 2020-07-06 10:23:22 +08:00
9683a0b82f review 2020-07-06 10:22:59 +08:00
a00bd5b888 Generics (#1078)
* chore: 增加新句子

* chore: 添加新的段落

* fix: fix typo
2020-07-05 08:16:41 -05:00
2fd7ccd829 summary_of_the_grammar Swift 5.3 beta 2020-07-01 21:47:43 +08:00
f5c85a2eed expressions Swift 5.3 beta 2020-07-01 21:46:15 +08:00
65f605fc00 Update to Swift 5.3 beta (#1077)
* Update 01_The_Basics to Swift 5.3 beta

* Update 10_Properties to Swift 5.3 beta
2020-06-30 08:09:57 -05:00
6f2488e19d statements Swift 5.3 beta 2020-06-30 21:02:28 +08:00
7f94b446ef lexical_structure Swift 5.3 beta 2020-06-30 17:36:27 +08:00
50c356dade update error_handling Swift5.3beta 2020-06-29 22:18:16 +08:00
aa77e5cdd6 basic_operators to Swift 5.3 beta 2020-06-28 23:01:40 +08:00
31a51ccace strings_and_characters to Swift 5.3 beta 2020-06-28 22:45:18 +08:00
4fff00bd5c version_compatibility to Swift 5.3 beta 2020-06-28 22:09:36 +08:00
e18e9c422a Merge pull request #1051 from Nemocdz/gh-pages
fix typo
2020-06-15 23:19:50 +08:00
ff8a9ca1a4 fix typo 2020-06-15 23:00:40 +08:00
51b864f711 Update 21_Protocols.md 2020-06-14 22:29:46 -05:00
67df77108c Merge pull request #1049 from Nemocdz/gh-pages
统一翻译
2020-05-30 23:35:36 +08:00
f7c9433bff 统一翻译 2020-05-30 23:31:27 +08:00
05e7dafe9e Update README.md 2020-05-09 08:17:58 -05:00
764753697c Update README.md 2020-05-06 22:15:16 -05:00
2aef26f66a Merge pull request #1044 from Nemocdz/gh-pages
翻译修改
2020-04-25 23:08:13 +08:00
dcc5837a53 update translation 2020-04-25 23:05:52 +08:00
d21f043b64 Update 20_Extensions.md (#1043)
修改翻译中的逻辑错误
2020-04-08 11:31:24 -05:00
52c81ff7b1 Update 06_Functions.md (#1042)
更正一个错误词语
2020-03-31 12:20:50 -05:00
2c5d4a51ff fix url 2020-02-17 22:44:43 +08:00
f47766a015 Update README.md 2020-02-15 23:03:09 -06:00
08e7cf1e63 Swift 5.2 新增内容的部分翻译 (#1036)
* Add translations of swift 5.2.

* Add translations of swift 5.2.

* Add translations of swift 5.2.

* Add translations of swift 5.2.

* Update 04_Expressions.md

* Refine translations

* Refine translations

* Update 07_Attributes.md

Co-authored-by: Jie Liang <lj925184928@gmail.com>
2020-02-15 23:02:01 -06:00
3d38eaa8a0 Merge pull request #1038 from Nemocdz/gh-pages
update Swift 5.2 beta
2020-02-15 18:23:33 +08:00
e91cdd5d29 Merge remote-tracking branch 'upstream/gh-pages' into gh-pages 2020-02-15 18:20:14 +08:00
ad085a8563 update history revision 2020-02-15 18:14:44 +08:00
39bf300bd4 update declaration.md in swift 5.2 (#1037) 2020-02-14 12:32:39 -06:00
77b1824d6b update Revision History 2020-02-08 14:07:45 +08:00
93c7fada67 Merge pull request #1035 from Nemocdz/gh-pages
update Swift 5.2 beta
2020-02-08 13:48:31 +08:00
72829a29b9 update Summary of the Grammar 2020-02-08 13:36:55 +08:00
9a2bc92a6b Revert "update Summary of the Grammar"
This reverts commit f333332ee6.
2020-02-08 13:31:38 +08:00
f333332ee6 update Summary of the Grammar 2020-02-08 13:11:13 +08:00
5bf3e735a5 update Statements 2020-02-08 13:09:14 +08:00
f82f40fd47 update Properties 2020-02-08 13:03:33 +08:00
b894831e80 update Version Compatibility 2020-02-08 12:55:00 +08:00
4ea6ea7d7a Merge pull request #1034 from Nemocdz/gh-pages
更新 Swift 5.2 beta epub
2020-02-08 01:56:21 +08:00
cc9f46c3dc add Swift 5.2 beta epub 2020-02-08 01:53:52 +08:00
c945edb4e5 Merge remote-tracking branch 'upstream/gh-pages' into gh-pages 2020-02-08 00:03:59 +08:00
7d2b949236 Update 02_Basic_Operators.md 2020-02-06 11:52:12 -06:00
f416e311eb Update 03_Strings_and_Characters.md (#1022)
* Update 03_Strings_and_Characters.md

* SubString -> Substring

* Fix StringProtocol documentation URL
2020-01-29 10:16:12 -06:00
e59b995eda Merge pull request #1021 from ElfSundae/patch-1
Fix anchor link
2020-01-27 22:41:21 +08:00
89aa08ab57 Fix anchor link 2020-01-27 13:16:35 +08:00
670cef47ed typo fix (#1018) 2019-11-18 19:58:08 -06:00
55001fd340 Update 10_Properties.md
增加代码块高亮
2019-11-11 21:36:27 +08:00
d54d3a6c78 Update contributors.md
更新贡献者
2019-11-11 21:23:55 +08:00
ec456eac27 Update README.md
更新至 Swift 5.1
2019-11-11 21:13:11 +08:00
d4f1442dc9 Update README.md
更新至 Swift 5.1 正式版
2019-11-11 21:09:26 +08:00
f6da2e83a6 Merge branch 'gh-pages' of github.com:SwiftGGTeam/the-swift-programming-language-in-chinese into gh-pages 2019-11-10 11:06:21 -06:00
c4f7fac162 Merge branch 'SunsetWan-gh-pages' into gh-pages 2019-11-10 11:06:10 -06:00
10ff6be465 Merge branch 'gh-pages' of https://github.com/SunsetWan/the-swift-programming-language-in-chinese into SunsetWan-gh-pages 2019-11-10 11:05:44 -06:00
028268e7a6 Refine the translation 2019-11-09 16:18:31 +08:00
b31fa0ef60 Merge remote-tracking branch 'upstream/gh-pages' into gh-pages 2019-11-06 09:41:55 +08:00
caa9beebee update Swift 5.1 (#1017) 2019-11-05 15:10:01 -06:00
1489d076e0 Update SUMMARY.md 2019-11-05 15:09:31 -06:00
9a48057873 update Swift 5.1 2019-11-05 21:14:41 +08:00
92c89aa4da Merge pull request #1016 from Nemocdz/gh-pages
格式更改
2019-11-05 20:35:01 +08:00
7220830354 格式更改 2019-11-05 20:34:10 +08:00
cb11ad439e Merge pull request #1015 from Nemocdz/gh-pages
update Swift 5.1
2019-11-05 19:36:42 +08:00
1f2ffa1668 update Swift 5.1 2019-11-05 19:34:56 +08:00
453266a7c1 Merge pull request #1014 from Nemocdz/gh-pages
update Swift 5.1
2019-11-05 12:35:50 +08:00
8e7dbad8a7 update Swift 5.1 2019-11-05 12:30:30 +08:00
e8ae235524 Merge pull request #1013 from Nemocdz/gh-pages
update Swift 5.1
2019-11-04 12:33:27 +08:00
e5e81b81d1 update Swift 5.1 2019-11-04 12:30:07 +08:00
f7b9eb5e1e Merge remote-tracking branch 'upstream/gh-pages' into gh-pages
# Conflicts:
#	source/02_language_guide/24_Automatic_Reference_Counting.md
#	source/03_language_reference/03_Types.md
2019-11-04 11:49:42 +08:00
663a21536b Merge pull request #1012 from Phenmod/a-swift-tour-5.1
03_a_swift_tour 修订
2019-11-04 10:55:27 +08:00
7a115a996a 修改 2019-11-04 10:53:16 +08:00
9988f57550 Refine translation 2019-11-02 22:49:14 +08:00
32fa806e77 Keep the style of the old README.md 2019-11-02 22:37:36 +08:00
273a7bdd3b 修改部分翻译 & 部分原文有改动需要重新翻译 2019-11-01 16:03:53 +08:00
3063f18a78 改进些小翻译和格式 2019-10-30 20:57:18 +08:00
1f0844bb36 赋新值或者传参时,类型可推定 2019-10-30 17:27:43 +08:00
8d0a87909e 少一段 2019-10-30 17:17:46 +08:00
dfd3383bd0 末尾引号开始列之后的内容缩进不会被删除 2019-10-30 17:02:03 +08:00
7656143f00 Finish translation of Property Wrappers Section 2019-10-27 17:02:31 +08:00
eeda1ed0ed update according to #1010 2019-10-26 08:03:14 -05:00
3434b008eb 修改 !== 为 != 运算符。 (#1009) 2019-10-21 08:45:37 -05:00
1704931ac4 Update Patterns (#1008)
* 修复文章排版问题

* 修复图片饮用链接失效问题

* 增加缺失的标点

* 初次修改

* 修改 链接文件格式

* style: 去除 pattern 中右边文字的斜体

* style: 去除 pattern 中右边文字的斜体

* style: 去除错误样式

* chore: 新增枚举用例模式的翻译
2019-10-20 08:52:20 -05:00
b0a9b63e1f Translation in progress 2019-10-20 16:52:28 +08:00
2b715ddb06 Merge pull request #1007 from tbchen/hotfix-links
修改为正确的超级链接
2019-10-16 22:48:57 +08:00
05be6d4ba7 修改为正确的超级链接 2019-10-16 14:20:14 +09:00
631a2cbe92 #987 Lexicalstructure (#1006)
* 04_Collection_Types 5.1初版

* 校对以后的更新

* 第二次校对以后的修正

* 初次5.1更改

* 装饰改为包装

* 校对以后的更改

* 二次校对

* 5.1更新

* 初次校对后的更改
2019-10-15 21:41:45 -05:00
5d3d15b836 Translation in progress 2019-10-12 00:23:08 +08:00
11aa5ab0d6 fix anchor name and link format 2019-10-10 17:55:30 -05:00
028a6aa795 修改文档名称及顺序,与官方文档保持一致 (#1005) 2019-10-10 14:00:57 -05:00
7ba5a56301 Translation in progress 2019-10-11 00:10:57 +08:00
4dcdd13e74 use english punctuation, ref #1004 2019-10-09 11:38:51 -05:00
8f53cc7d82 fix wrong translation, ref #1003 2019-10-09 11:36:24 -05:00
48373d7791 Translation in progress 2019-10-09 23:43:32 +08:00
603a40adb0 初步完成 #985 (#996)
* 04_Collection_Types 5.1初版

* 校对以后的更新

* 第二次校对以后的修正

* 初次5.1更改

* 装饰改为包装

* 校对以后的更改

* 二次校对
2019-10-08 22:53:57 -05:00
06e3e12621 Subscripts #984 更新 5.1 (#999)
* 04_Collection_Types 5.1初版

* 校对以后的更新

* 第二次校对以后的修正

* 初次5.1更改

* 装饰改为包装

* 校对以后的更改

* 5.1更新初次完成

* 还原Attributes相关更新,避免冲突

* 初次校对的更改
2019-10-08 14:32:30 -05:00
5392b093d2 修改空格 (#1001) 2019-10-01 17:04:22 -05:00
8b743fafe5 update Types 5.1 (#1000)
* update swift 5.1

* update post

* update swift 5.1
2019-10-01 08:14:17 -05:00
30db13bc1e Update README.md 2019-10-01 18:17:42 +08:00
d34aee47cd update swift 5.1 2019-10-01 18:14:33 +08:00
64f80c9cba update 25_Access_Control to 5.1 (#994)
* update 25_Access_Control to 5.1

* Update 25_Access_Control.md

* Update 25_Access_Control.md

修改语句不通顺的翻译。
2019-09-30 09:26:00 -05:00
db48920fa3 update ARC (#998)
* update swift 5.1

* update post
2019-09-29 22:30:36 -05:00
9284579c0b Update Swift 5.1 21_protocols (#997) 2019-09-29 22:29:32 -05:00
20f0cb7f81 update post 2019-09-30 00:40:09 +08:00
ca027e9132 update swift 5.1 2019-09-29 23:40:39 +08:00
6d6118e7a3 Update README.md 2019-09-28 22:26:58 -05:00
d1b4d709b6 Merge pull request #993 from Nemocdz/gh-pages
修改文档,更新 epub
2019-09-24 21:09:08 +08:00
0e784e110d feat: update document 2019-09-24 21:05:03 +08:00
7bd73172ed fix: duplicate 2019-09-24 21:03:29 +08:00
f3a4630915 Update 03_Strings_and_Characters.md (#978)
错别字修改
2019-08-22 11:03:02 -05:00
02e0b33fd6 Update 21_Protocols.md (#976) 2019-08-08 08:55:03 -05:00
38ba1198b7 修正示例代码 (#975) 2019-07-25 23:05:50 -05:00
eedcd69d28 Update 25_Access_Control.md (#974) 2019-07-23 23:35:29 -05:00
b7a8566a0f 校对 (#973)
* Update 09_Structures_And_Classes.md

驼峰

* Update 17_Error_Handling.md

病句
2019-07-23 23:31:38 -05:00
ebda86cd05 Merge pull request #969 from xuezhulian/patch-1
Update 05_Control_Flow.md
2019-07-18 15:31:53 +08:00
546db6bc47 Update 05_Control_Flow.md
swith -> switch
2019-07-18 13:49:10 +08:00
3d64a8e5e6 trigger gitbook building 2019-07-15 06:35:00 -05:00
b95202eb6d Update 03_Strings_and_Characters.md (#967) 2019-07-15 06:30:00 -05:00
ed7eb9e045 Update 22_Generics.md 2019-07-15 05:24:12 -05:00
15ee26ca6d Update 04_revision_history.md 2019-07-15 05:23:45 -05:00
dca10b1aa0 修正翻译错误 (#965)
* 修正翻译错误

* Update 14_Initialization.md
2019-07-15 04:50:12 -05:00
2d10a8d6ec 修改翻译 (#964) 2019-07-14 20:57:21 -07:00
45c537a42f 修改翻译 2019-07-15 10:48:01 +08:00
a6d6e468f6 Merge pull request #963 from Nemocdz/gh-pages
fix 一处格式问题
2019-07-12 11:38:40 +08:00
72708963ab fix typo 2019-07-12 11:37:23 +08:00
22265fa31d Merge remote-tracking branch 'upstream/gh-pages' into gh-pages 2019-07-12 11:36:26 +08:00
771c5b3da0 trigger gitbook update 2019-07-11 10:39:39 +08:00
0369884f87 Update contributors.md 2019-07-10 20:24:13 +08:00
75e0837fb0 Update contributors.md 2019-07-10 20:22:55 +08:00
0afcd2ebf1 Update README.md 2019-07-10 18:32:54 +08:00
d07fdbfb98 更新 Revision History (#962)
* 术语表更新

* 更新文档内术语

* 术语表更新

* update Control Flow

* update Version Compatibility

* update Methods

* update Types

* 术语表更新

* update Types

* Update 03_Types.md

* update links

* update Links

* 统一版本历史记录格式和翻译

* fix typo

* fix typo

* Update 04_revision_history.md

* update Document Revision History

* update Revision History&Types
2019-07-10 18:07:50 +08:00
21e80f09b5 update Revision History&Types 2019-07-10 17:01:13 +08:00
2b140081de Merge remote-tracking branch 'upstream/gh-pages' into gh-pages 2019-07-10 16:17:49 +08:00
60cc810915 Update SUMMARY.md 2019-07-10 15:59:55 +08:00
68e51d7f57 完成翻译:不透明类型 (#961)
* 新增翻译:不透明类型

* 优化格式
2019-07-10 15:57:13 +08:00
4381756f0f Merge pull request #960 from lal603743923/gh-pages
Update 03_a_swift_tour.md
2019-07-10 14:48:33 +08:00
96f3c84b8a Update 03_a_swift_tour.md
修正翻译错误.
2019-07-09 20:57:20 +08:00
cc029194be 04_Collection_Types (#959)
* 04_Collection_Types 5.1初版

* 校对以后的更新

* 第二次校对以后的修正
2019-07-07 11:08:11 +08:00
ae117bdfbc #939 & #949 (#958)
* Protocol -  protocol type downcasting

* Protocols - 统一用语

* Properties - Shorthand getter

* Properties - 标点及语气调整。

* Update 10_Properties.md
2019-07-03 20:36:45 -07:00
5fc4252bfc Update FUNDING.yml 2019-07-03 14:12:24 +08:00
9ec61ba488 Create FUNDING.yml 2019-07-03 14:09:16 +08:00
fc89e3ca5d Merge pull request #957 from SunsetWan/sunsetwan-translation
Complete the translation of issue  942 and 941
2019-07-01 16:52:49 +08:00
9a629a57e3 Merge branch 'sunsetwan-translation' of https://github.com/SunsetWan/the-swift-programming-language-in-chinese into sunsetwan-translation 2019-07-01 13:41:53 +08:00
787687cb1e Refine the translation. 2019-07-01 13:40:57 +08:00
b699654ab8 update Document Revision History 2019-07-01 00:23:20 +08:00
d902be8367 Merge branch 'gh-pages' into sunsetwan-translation 2019-07-01 00:09:07 +08:00
d90c50ec74 Refine the translation. 2019-07-01 00:06:35 +08:00
4894951c3b 22 Generics Swift 5.1 更新 (#956) 2019-06-30 07:25:08 -05:00
b5defb4c0e Complete the translation of issue 942 and 941. 2019-06-30 18:55:05 +08:00
b1887d01ac Merge remote-tracking branch 'upstream/gh-pages' into gh-pages 2019-06-30 00:10:02 +08:00
5f0da139f0 统一版本历史记录翻译及格式 (#955)
* 术语表更新

* 更新文档内术语

* 术语表更新

* update Control Flow

* update Version Compatibility

* update Methods

* update Types

* 术语表更新

* update Types

* Update 03_Types.md

* update links

* update Links

* 统一版本历史记录格式和翻译

* fix typo

* fix typo

* Update 04_revision_history.md
2019-06-29 11:00:30 -05:00
53eaaeb19c Update 04_revision_history.md 2019-06-29 11:00:19 -05:00
c9c9bb980e fix typo 2019-06-29 23:45:34 +08:00
51ceab425c Merge remote-tracking branch 'upstream/gh-pages' into gh-pages
# Conflicts:
#	source/chapter1/04_revision_history.md
2019-06-29 23:39:14 +08:00
16cae8adf6 fix typo 2019-06-29 23:34:09 +08:00
bab43e7db6 统一版本历史记录格式和翻译 2019-06-29 23:33:23 +08:00
4cace74528 Complete the translation of issue 937 and 935. (#954) 2019-06-29 08:28:58 -05:00
b807a6edb9 Complete the translation of issue 937 and 935. 2019-06-29 19:34:54 +08:00
1a6d56d228 统一链接格式 (#953)
* 术语表更新

* 更新文档内术语

* 术语表更新

* update Control Flow

* update Version Compatibility

* update Methods

* update Types

* 术语表更新

* update Types

* Update 03_Types.md

* update links

* update Links
2019-06-27 17:35:37 -05:00
004fc0906d Merge remote-tracking branch 'upstream/gh-pages' into gh-pages
# Conflicts:
#	source/chapter2/11_Methods.md
#	source/chapter3/03_Types.md
2019-06-28 00:59:06 +08:00
850cf97f83 Merge branch 'gh-pages' of github.com:Nemocdz/the-swift-programming-language-in-chinese into gh-pages 2019-06-28 00:54:45 +08:00
d6862fa4dc update Links 2019-06-28 00:54:02 +08:00
d3efb989d3 update links 2019-06-28 00:09:46 +08:00
0f2ab87dca Delete config.json 2019-06-26 11:16:25 -05:00
9a6f016f45 Delete change_cdn.py 2019-06-26 11:16:11 -05:00
2164db3b8b Update index.html 2019-06-26 11:16:01 -05:00
fb2dfa6764 Delete googleb0a4f5a22e9cb82f.html 2019-06-26 11:15:16 -05:00
d8068f4325 Delete CNAME 2019-06-26 11:14:35 -05:00
4e4e291956 更新文档翻译 (#952)
* 术语表更新

* 更新文档内术语

* 术语表更新

* update Control Flow

* update Version Compatibility

* update Methods

* update Types

* 术语表更新

* update Types

* Update 03_Types.md
2019-06-26 11:05:31 -05:00
2f430495d4 Update 03_Types.md 2019-06-26 11:05:20 -05:00
7b123505db Merge remote-tracking branch 'upstream/gh-pages' into gh-pages
# Conflicts:
#	README.md
#	source/chapter3/06_Declarations.md
2019-06-26 23:55:25 +08:00
e7def896cb update Types 2019-06-26 23:42:11 +08:00
5fbfc38fd7 remove extra > 2019-06-26 10:02:48 -05:00
b26f8aacff 术语表更新 2019-06-26 22:42:22 +08:00
b76f8cf111 Translate Chapter3 06_Declarations and 07_Attributes (#951)
* 完成 Chapter3 Declarations Swift5.1 Update

* Update 07_Attributes.md
2019-06-26 09:37:39 -05:00
ca87420665 update Types 2019-06-26 21:20:01 +08:00
93c390dd5a update Methods 2019-06-26 19:44:58 +08:00
7020682acd update Version Compatibility 2019-06-26 19:31:54 +08:00
aa91ad4692 update Control Flow 2019-06-26 19:16:32 +08:00
367cece0f5 术语更新 (#950)
* 术语表更新

* 更新文档内术语

* 术语表更新
2019-06-25 10:00:42 -05:00
3f0cc6c047 术语表更新 2019-06-25 22:49:28 +08:00
65dc6b133f 更新文档内术语 2019-06-25 22:43:20 +08:00
b58d744852 术语表更新 2019-06-25 22:40:17 +08:00
e9aaa026c6 Merge pull request #949 from Nemocdz/gh-pages
remove unused files
2019-06-23 23:03:11 +08:00
1167a215c9 remove unused files 2019-06-23 23:00:55 +08:00
f5c9d9c5e5 Update README.md 2019-06-23 09:30:05 -05:00
71f0757ba0 Merge pull request #931 from Nemocdz/gh-pages
更新 Swift 5.1 文档(beta)
2019-06-23 11:25:24 +08:00
afc8cddb0e update document 2019-06-23 11:17:57 +08:00
a3a7770b9a Merge remote-tracking branch 'upstream/gh-pages' into gh-pages
# Conflicts:
#	source/chapter2/09_Structures_And_Classes.md
2019-06-23 10:59:04 +08:00
1ffeeeedaf Update 14_Initialization.md 2019-06-16 07:43:03 -05:00
b991a4eb75 Update 04_Expressions.md (#929)
有几处的binary被错误翻译成了二进制,本意应为二元
2019-05-19 11:28:54 +08:00
e3c9bd7992 Update 02_version_compatibility.md (#926) 2019-05-03 18:57:44 -05:00
425d534e7f Update 25_Access_Control.md (#925) 2019-04-21 11:44:58 -05:00
e0af10bc8e Update 21_Protocols.md (#924) 2019-04-20 21:43:29 -05:00
3210e30972 Revert "test gitbook anchor"
This reverts commit e82cea3866.
2019-04-19 12:28:19 -05:00
e82cea3866 test gitbook anchor 2019-04-19 12:23:34 -05:00
497d68be37 fix invalid url (#923) 2019-04-15 08:37:38 -05:00
cb2ebd282b Update 01_The_Basics.md 2019-04-12 07:24:30 -05:00
9fd540b8d7 校对: chapter2/03_Strings_and_Characters 方法名变化 (#921)
index(of:) 已更名为: firstIndex(of:)
2019-04-11 21:32:20 -05:00
86690d4472 Update contributors.md 2019-04-11 19:26:46 -05:00
de559ce2d8 update swift 5.0 2019-02-04 01:25:04 +08:00
74 changed files with 12234 additions and 11001 deletions

12
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: [SketchK, numbbbbb]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

1
CNAME
View File

@ -1 +0,0 @@
gg.swiftguide.cn

View File

@ -5,19 +5,20 @@
[英文原版在线版](https://docs.swift.org/swift-book/)
[英文原版ePub版](https://docs.swift.org/swift-book/TheSwiftProgrammingLanguageSwift5.epub)
# 在线阅读
使用 GitBook 制作,可以直接 [在线阅读](https://swiftgg.gitbook.io/swift/)。
使用 GitBook 制作,可以 [GitBook](https://swiftgg.gitbook.io/swift/) 网站阅读
# 当前阶段
- 更新到 Swift 5.22020-02-15
- 更新到 Swift 5.12019-11-11
- 更新到 Swift 5.02019-04-05
- 更新到 Swift 4.22019-01-29
- 更新到 Swift 4.12018-04-12感谢 [@Mylittleswift](https://github.com/Mylittleswift)
- 更新到 Swift 3.02016-09-23
# 贡献力量
如果想做出贡献的话,你可以:
@ -57,24 +58,28 @@ diff 操作如下:
| 术语 | 备选翻译 |
| --- | --- |
| property wrapper | 属性包装器([翻译相关讨论](https://github.com/SwiftGGTeam/the-swift-programming-language-in-chinese/issues/982#issuecomment-536244784) |
| projected value | 被呈现值 |
| wrapped value | 被包装值 |
| argument | 实参 |
| parameter | 形参 |
| associatedtype | 关联类型 |
| variadic parameters| 可变参数 |
| associated type | 关联类型 |
| range | 区间 |
| type property | 类型属性 |
| Unary operator | 一元运算符 |
| Binary operator | 二元运算符 |
| Ternary operator | 三元运算符 |
| Labeled Statement | 具名语句 |
| conform Protocol | 遵循协议 |
| unary operator | 一元运算符 |
| binary operator | 二元运算符 |
| ternary operator | 三元运算符 |
| labeled statement | 具名语句 |
| conform protocol | 遵循协议 |
| availability-condition | 可用性条件 |
| fallthrough | 贯穿 |
| Branch Statement | 分支语句 |
| Control Transfer Statement | 控制传递语句 |
| Type Annotation | 类型注 |
| Type Identifier | 类型标识符 |
| Metatype Type | 元类型 |
| Protocol Composition Type | 复合协议类型 |
| branch statement | 分支语句 |
| control transfer statement | 控制传递语句 |
| type annotation | 类型注 |
| type identifier | 类型标识符 |
| metatype type | 元类型 |
| protocol composition type | 复合协议类型 |
| associated value | 关联值 |
| raw value | 原始值 |
| computed property | 计算属性 |
@ -87,21 +92,21 @@ diff 操作如下:
| statement | 语句 |
| expression | 表达式 |
| optional | 可选 |
| Implicitly Unwrapped optional | 隐式解包可选值 |
| Optional Binding | 可选绑定 |
| implicitly unwrapped optional | 隐式解包可选值 |
| optional binding | 可选绑定 |
| optional chaining | 可选链 |
| collection | 集合 |
| convention | 约定 |
| iterate | 迭代 |
| nest | 嵌套 |
| Inheritance | 继承 |
| inheritance | 继承 |
| override | 重写 |
| base class | 基类 |
| Designated Initializer | 指定构造器 |
| Convenience Initializer | 便利构造器 |
| Automatic Reference Counting | 自动引用计数 |
| designated initializer | 指定构造器 |
| convenience initializer | 便利构造器 |
| automatic reference counting | 自动引用计数 |
| type inference | 类型推断 |
| Type Casting | 类型转换 |
| type casting | 类型转换 |
| unwrapped | 解包 |
| wrapped | 包装 |
| note | 注意 |
@ -109,7 +114,7 @@ diff 操作如下:
| tuple | 元组 |
| first-class | 一等 |
| deinitializer | 析构器 |
| Initializer | 构造器 |
| initializer | 构造器 |
| initialization | 构造过程 |
| deinitialization | 析构过程 |
| getter | 不翻译 |
@ -118,15 +123,18 @@ diff 操作如下:
| property | 属性 |
| attribute | 特性或者属性,根据上下文 |
| method | 方法 |
| Enumeration | 枚举 |
| Structure | 结构体 |
| Protocol | 协议 |
| Extension | 扩展 |
| Generic | 泛型 |
| enumeration | 枚举 |
| structure | 结构体 |
| protocol | 协议 |
| extension | 扩展 |
| generic | 泛型 |
| literal value | 字面量 |
| alias | 别名 |
| Assertion | 断言 |
| assertion | 断言 |
| conditional compilation | 条件编译 |
| opaque type | 不透明类型 |
| function | 函数 |
| runtime | 运行时 |
# 贡献者

View File

@ -1,22 +0,0 @@
#!/usr/bin/python
# coding:utf-8
import os
def iter(path):
for root, dirs, files in os.walk(path):
for fn in files:
if fn.endswith(".html"):
with open(root + '/' + fn, 'r') as f:
content = f.read()
content = content.replace('<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/ace.js"></script>', '<script src="http://cdn.bootcss.com/ace/1.1.3/ace.js"></script>').replace('<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.1.3/mode-javascript.js"></script>', '<script src="http://cdn.bootcss.com/ace/1.1.3/mode-javascript.js"></script>')
insert_pos = content.find("</li>", content.find("Generated using GitBook")) + 6
content = content[:insert_pos] + '''<li style="margin-left:15%;"> <iframe src="http://ghbtns.com/github-btn.html?user=numbbbbb&repo=the-swift-programming-language-in-chinese&type=watch&count=true&size=large"
allowtransparency="true" frameborder="0" scrolling="0" width="170" height="30"></iframe></li>''' + content[insert_pos:]
content.replace(r'<title>.*?</title>', "<title>《The Swift Programming Language》完整中文版</title>")
with open(root + '/' + fn, 'w') as f:
f.write(content)
iter(os.getcwd())

View File

@ -1,9 +0,0 @@
{
"name": "The Swift Programming Language 中文版",
"introduction": "中文版《The Swift Programming Language》",
"path": {
"content": "source",
"toc": "source/SUMMARY.md",
"readme": "source/README.md"
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1 +0,0 @@
google-site-verification: googleb0a4f5a22e9cb82f.html

View File

@ -1,19 +1,8 @@
<!DOCTYPE HTML>
<html lang="en-US" manifest="./manifest.appcache">
<head>
<meta http-equiv="refresh" content="0; url=http://wiki.jikexueyuan.com/project/swift/" />
<meta http-equiv="refresh" content="0; url=https://swiftgg.gitbook.io/swift/" />
</head>
<body>
<script type="text/javascript">
var _bdhmProtocol = (("https:" == document.location.protocol) ? " https://" : " http://");
document.write(unescape("%3Cscript src='" + _bdhmProtocol + "hm.baidu.com/h.js%3F21e159ce3496d7b5f80aa3c4f1370b04' type='text/javascript'%3E%3C/script%3E"));
</script>
</body>
</html>

View File

@ -2,7 +2,7 @@
Swift 是一种非常好的编写软件的方式,无论是手机,台式机,服务器,还是其他运行代码的设备。它是一种安全,快速和互动的编程语言,将现代编程语言的精华和苹果工程师文化的智慧,以及来自开源社区的多样化贡献结合了起来。编译器对性能进行了优化,编程语言对开发进行了优化,两者互不干扰,鱼与熊掌兼得。
Swift 对于初学者来说也很友好。它是第一个既满足工业标准又像脚本语言一样充满表现力和趣味的系统编程语言。它支持代码预览playgrounds这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。
Swift 对于初学者来说也很友好。它是一门满足工业标准的编程语言,但又有着脚本语言般的表达力和可玩性。它支持代码预览playgrounds这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。
Swift 通过采用现代编程模式来避免大量常见编程错误:

View File

@ -0,0 +1,13 @@
# 版本兼容性
本书描述的是在 Xcode 13 中默认包含的 Swift 5.5 版本。你可以使用 Xcode 13 来构建 Swift 5.5、Swift 4.2 或 Swift 4 写的项目。
使用 Xcode 13 构建 Swift 4 和 Swift 4.2 代码时Swift 5.5 的大多数功能都适用。但以下功能仅支持 Swift 5.5 或更高版本:
* 返回值是不透明类型的函数依赖 Swift 5.1 运行时。
* **try?** 表达式不会为已返回可选类型的代码引入额外的可选类型层级。
* 大数字的整型字面量初始化代码的类型将会被正确推导,例如 **UInt64(0xffff_ffff_ffff_ffff)** 将会被推导为整型类型而非溢出。
并发特性需要 Swift 5.5 及以上版本,以及一个提供了并发相关类型的 Swift 标准库版本。要应用于苹果平台,请至少将部署版本设置为 iOS 15、macOS 12、tvOS 15 或 watchOS 8.0。
用 Swift 5.5 写的项目可以依赖用 Swift 4.2 或 Swift 4 写的项目反之亦然。这意味着如果你将一个大的项目分解成多个框架framework你可以逐个地将框架从 Swift 4 代码迁移到 Swift 5.5。

View File

@ -14,11 +14,11 @@ print("Hello, world!")
>
> 最好的体验是把这一章作为 Playground 文件在 Xcode 中打开。 Playgrounds 允许你可以编辑代码并立刻看到输出结果。
>
> [Download Playground](https://docs.swift.org/swift-book/GuidedTour/GuidedTour.playground.zip)
> [下载 Playground](https://docs.swift.org/swift-book/GuidedTour/GuidedTour.playground.zip)
## 简单值 {#simple-values}
使用 `let` 来声明常量,使用 `var` 来声明变量。一个常量的值,在编译的时候,并不需要有明确的值,但是你只能为它赋值一次。这说明你可以用一个常量来命名一个值,一次赋值就可在多个地方使用。
使用 `let` 来声明常量,使用 `var` 来声明变量。一个常量的值,在编译的时候,并不需要有明确的值,但是你只能为它赋值一次。这说明你可以用一个常量来命名一个值,一次赋值就可在多个地方使用。
```swift
var myVariable = 42
@ -65,7 +65,7 @@ let fruitSummary = "I have \(apples + oranges) pieces of fruit."
>
> 使用 `\()` 来把一个浮点计算转换成字符串,并加上某人的名字,和他打个招呼。
使用一对三个引号(`"""`)来包含多行字符串内容,字符串中的内容(包括引号、空格、换行符等)都会保留下来。举个例子:
使用三个引号(`"""`)来包含多行字符串内容。每行行首的缩进会被去除,直到和结尾引号的缩进相匹配。举个例子:
```swift
let quotation = """
@ -87,14 +87,21 @@ var occupations = [
occupations["Jayne"] = "Public Relations"
```
数组在添加元素时会自动变大。
```swift
shoppingList.append("blue paint")
print(shoppingList)
```
使用初始化语法来创建一个空数组或者空字典。
```swift
let emptyArray = [String]()
let emptyDictionary = [String: Float]()
let emptyArray: [String] = []
let emptyDictionary: [String: Float] = [:]
```
如果类型信息可以被推断出来,你可以用 `[]``[:]` 来创建空数组和空字典——就像你声明变量或者给函数传参数的时候一样
如果类型信息可以被推断出来,你可以用 `[]``[:]` 来创建空数组和空字典——比如,在给变量赋新值或者给函数传参数的时候。
```swift
shoppingList = []
@ -138,6 +145,7 @@ if let name = optionalName {
> 把 `optionalName` 改成 `nil`greeting 会是什么?添加一个 `else` 语句,当 `optionalName``nil` 时给 greeting 赋一个不同的值。
如果变量的可选值是 `nil`,条件会判断为 `false`,大括号中的代码会被跳过。如果不是 `nil`,会将值解包并赋给 `let` 后面的常量,这样代码块中就可以使用这个值了。
另一种处理可选值的方法是通过使用 `??` 操作符来提供一个默认值。如果可选值缺失的话,可以使用默认值来代替。
```swift
@ -179,7 +187,7 @@ let interestingNumbers = [
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
for (_, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
@ -187,11 +195,12 @@ for (kind, numbers) in interestingNumbers {
}
}
print(largest)
// 输出 "25"
```
> 练习
>
> 添加另一个变量来记录最大数字的种类kind同时仍然记录这个最大数字的值
> 将 _ 替换成变量名,以确定哪种类型的值是最大的
使用 `while` 来重复运行一段代码直到条件改变。循环条件也可以在结尾,保证能至少循环一次。
@ -234,7 +243,7 @@ greet(person:"Bob", day: "Tuesday")
> 练习
>
> 删除 `day` 参数,添加一个参数来表示今天吃了什么午饭
> 删除 `day` 参数,在这个欢迎语中添加一个参数来表示今天的特价菜
默认情况下,函数使用它们的参数名称作为它们参数的标签,在参数名称前可以自定义参数标签,或者使用 `_` 表示不使用参数标签。
@ -314,7 +323,7 @@ var numbers = [20, 19, 7, 12]
hasAnyMatches(list: numbers, condition: lessThanTen)
```
函数实际上是一种特殊的闭包:它是一段能之后被调取的代码。闭包中的代码能访问闭包作用域中的变量和函数,即使闭包是在一个不同的作用域被执行的 - 你已经在嵌套函数的例子中看过了。你可以使用 `{}` 来创建一个匿名闭包。使用 `in` 将参数和返回值类型的声明与闭包函数体进行分离。
函数实际上是一种特殊的闭包:它是一段能之后被调取的代码。闭包中的代码能访问闭包作用域中的变量和函数,即使闭包是在一个不同的作用域被执行的——你已经在嵌套函数的例子中看过了。你可以使用 `{}` 来创建一个匿名闭包。使用 `in` 将参数和返回值类型的声明与闭包函数体进行分离。
```swift
numbers.map({
@ -335,7 +344,7 @@ let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)
```
你可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。当一个闭包是传给函数的唯一参数,你可以完全忽略括号。
你可以通过参数位置而不是参数名字来引用参数——这个方法在非常短的闭包中非常有用。当一个闭包作为最后一个参数传给一个函数的时候,它可以直接跟在括号后面。当一个闭包是传给函数的唯一参数,你可以完全忽略括号。
```swift
let sortedNumbers = numbers.sorted { $0 > $1 }
@ -419,7 +428,7 @@ test.simpleDescription()
>
> 创建 `NamedShape` 的另一个子类 `Circle`,构造器接收两个参数,一个是半径一个是名称,在子类 `Circle` 中实现 `area()``simpleDescription()` 方法。
除了储存简单的属性之外,属性可以有 getter 和 setter 。
除了简单的存储属性,还有使用 getter 和 setter 的计算属性
```swift
class EquilateralTriangle: NamedShape {
@ -450,7 +459,7 @@ triangle.perimeter = 9.9
print(triangle.sideLength)
```
`perimeter` 的 setter 中,新值的名字是 `newValue`。你可以在 `set` 之后显式设置一个名字。
`perimeter` 的 setter 中,新值的名字是 `newValue`。你可以在 `set` 之后的圆括号中显式设置一个名字。
注意 `EquilateralTriangle` 类的构造器执行了三步:
@ -484,7 +493,7 @@ triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)
```
处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加 `?`。如果 `?` 之前的值是 `nil``?` 后面的东西都会被忽略,并且整个表达式返回 `nil`。否则,`?` 之后的东西都会被运行。在这两种情况下,整个表达式的值也是一个可选值。
处理变量的可选值时,你可以在操作(比如方法、属性和子脚本)之前加 `?`。如果 `?` 之前的值是 `nil``?` 后面的东西都会被忽略,并且整个表达式返回 `nil`。否则,可选值会被解包,之后的所有代码都会按照解包后的值运行。在这两种情况下,整个表达式的值也是一个可选值。
```swift
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
@ -525,7 +534,7 @@ let aceRawValue = ace.rawValue
默认情况下Swift 按照从 0 开始每次加 1 的方式为原始值进行赋值,不过你可以通过显式赋值进行改变。在上面的例子中,`Ace` 被显式赋值为 1并且剩下的原始值会按照顺序赋值。你也可以使用字符串或者浮点数作为枚举的原始值。使用 `rawValue` 属性来访问一个枚举成员的原始值。
使用 `init?(rawValue:)` 初始化构造器来创建一个带有原始值的枚举成员。如果存在与原始值相应的枚举成员就返回该枚举成员,否则就返回 `nil`
使用 `init?(rawValue:)` 初始化构造器来从原始值创建一个枚举实例。如果存在与原始值相应的枚举成员就返回该枚举成员,否则就返回 `nil`
```swift
if let convertedRank = Rank(rawValue: 3) {
@ -559,9 +568,9 @@ let heartsDescription = hearts.simpleDescription()
>
> 给 `Suit` 添加一个 `color()` 方法,对 `spades``clubs` 返回 “black” ,对 `hearts``diamonds` 返回 “red” 。
注意在上面的例子中用了两种方式引用 `hearts` 枚举成员:给 `hearts` 常量赋值时,枚举成员 `Suit.hearts` 需要用全名来引用,因为常量没有显式指定类型。在 `switch` 里,枚举成员使用缩写 `.hearts` 来引用,因为 `self` 的值已经是一个 `suit` 类型,在已知变量类型的情况下可以使用缩写。
注意在上面的例子中用了两种方式引用 `hearts` 枚举成员:给 `hearts` 常量赋值时,枚举成员 `Suit.hearts` 需要用全名来引用,因为常量没有显式指定类型。在 `switch` 里,枚举成员使用缩写 `.hearts` 来引用,因为 `self` 的值已经是一个 `suit` 类型。在任何已知变量类型的情况下可以使用缩写。
如果枚举成员的实例有原始值,那么这些值是在声明的时候就已经决定了,这意味着不同枚举实例的枚举成员总会有一个相同的原始值。当然我们也可以为枚举成员设定关联值,关联值是在创建实例时决定的。这意味着不同的枚举成员的关联值可以不同。你可以把关联值想象成枚举成员的寄存属性。例如,考虑从服务器获取日出和日落的时间的情况。服务器会返回正常结果或者错误信息。
如果枚举成员的实例有原始值,那么这些值是在声明的时候就已经决定了,这意味着不同枚举实例的枚举成员总会有一个相同的原始值。当然我们也可以为枚举成员设定关联值,关联值是在创建实例时决定的。这意味着同一枚举成员不同实例的关联值可以不同。你可以把关联值想象成枚举成员实例的存储属性。例如,考虑从服务器获取日出和日落的时间的情况。服务器会返回正常结果或者错误信息。
```swift
enum ServerResponse {
@ -584,7 +593,7 @@ case let .failure(message):
>
> 给 `ServerResponse` 和 switch 添加第三种情况。
注意日升和日落时间是如何从 `ServerResponse` 中提取到并`switch` `case` 相匹配的。
注意 `ServerResponse` 的值在`switch`分支匹配时,日升和日落时间是如何从该值中提取出来的。
使用 `struct` 来创建一个结构体。结构体和类有很多相同的地方,包括方法和构造器。它们之间最大的一个区别就是结构体是传值,类是传引用。
@ -602,7 +611,7 @@ let threeOfSpadesDescription = threeOfSpades.simpleDescription()
> 练习
>
> `Card` 添加一个方法,创建一副完整的扑克牌并把每张牌的 rank 和 suit 对应起来
> 一个方法,创建一副完整的扑克牌,这些牌是所有 rank 和 suit 的组合
## 协议和扩展 {#protocols-and-extensions}
@ -642,7 +651,7 @@ let bDescription = b.simpleDescription
> 练习
>
> 写一个实现这个协议的枚举。
> `ExampleProtocol` 再增加一个要求。你需要怎么改 `SimpleClass``SimpleStructure` 才能保证它们仍旧遵循这个协议?
注意声明 `SimpleStructure` 时候 `mutating` 关键字用来标记一个会修改结构体的方法。`SimpleClass` 的声明不需要标记任何方法,因为类中的方法通常可以修改类属性(类的性质)。
@ -763,7 +772,7 @@ print(fridgeIsOpen)
```swift
func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
var result = [Item]()
var result: [Item] = []
for _ in 0..<numberOfTimes {
result.append(item)
}
@ -788,7 +797,8 @@ possibleInteger = .some(100)
```swift
func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
where T.Iterator.Element: Equatable, T.Iterator.Element == U.Iterator.Element {
where T.Element: Equatable, T.Element == U.Element
{
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {

View File

@ -2,7 +2,7 @@
Swift 是一门开发 iOS, macOS, watchOS 和 tvOS 应用的新语言。然而,如果你有 C 或者 Objective-C 开发经验的话,你会发现 Swift 的很多内容都是你熟悉的。
Swift 包含了 C 和 Objective-C 上所有基础数据类型,`Int` 表示整型值; `Double``Float` 表示浮点型值; `Bool` 是布尔型值;`String` 是文本型数据。 Swift 还提供了三个基本的集合类型,`Array``Set``Dictionary` ,详见[集合类型](./04_Collection_Types.md)。
Swift 包含了 C 和 Objective-C 上所有基础数据类型,`Int` 表示整型值; `Double``Float` 表示浮点型值; `Bool` 是布尔型值;`String` 是文本型数据。 Swift 还提供了三个基本的集合类型,`Array``Set``Dictionary` ,详见 [集合类型](./04_Collection_Types.md)。
就像 C 语言一样Swift 使用变量来进行存储并通过变量名来关联值。在 Swift 中,广泛的使用着值不可变的变量,它们就是常量,而且比 C 语言的常量更强大。在 Swift 中,如果你要处理的值不需要改变,那使用常量可以让你的代码更加安全并且更清晰地表达你的意图。
@ -41,11 +41,11 @@ var x = 0.0, y = 0.0, z = 0.0
>
> 如果你的代码中有不需要改变的值,请使用 `let` 关键字将它声明为常量。只将需要改变的值声明为变量。
### 类型注 {#type-annotations}
### 类型注 {#type-annotations}
当你声明常量或者变量的时候可以加上*类型type annotation*,说明常量或者变量中要存储的值的类型。如果要添加类型注,需要在常量或者变量名后面加上一个冒号和空格,然后加上类型名称。
当你声明常量或者变量的时候可以加上*类型注type annotation*,说明常量或者变量中要存储的值的类型。如果要添加类型注,需要在常量或者变量名后面加上一个冒号和空格,然后加上类型名称。
这个例子给 `welcomeMessage` 变量添加了类型注,表示这个变量可以存储 `String` 类型的值:
这个例子给 `welcomeMessage` 变量添加了类型注,表示这个变量可以存储 `String` 类型的值:
```swift
var welcomeMessage: String
@ -63,7 +63,7 @@ var welcomeMessage: String
welcomeMessage = "Hello"
```
你可以在一行中定义多个同样类型的变量,用逗号分割,并在最后一个变量名之后添加类型注:
你可以在一行中定义多个同样类型的变量,用逗号分割,并在最后一个变量名之后添加类型注
```swift
var red, green, blue: Double
@ -71,11 +71,11 @@ var red, green, blue: Double
> 注意
>
> 一般来说你很少需要写类型注。如果你在声明常量或者变量的时候赋了一个初始值Swift 可以推断出这个常量或者变量的类型,请参考[类型安全和类型推断](#type_safety_and_type_inference)。在上面的例子中,没有给 `welcomeMessage` 赋初始值,所以变量 `welcomeMessage` 的类型是通过一个类型注指定的,而不是通过初始值推断的。
> 一般来说你很少需要写类型注。如果你在声明常量或者变量的时候赋了一个初始值Swift 可以推断出这个常量或者变量的类型,请参考 [类型安全和类型推断](#type-safety-and-type-inference)。在上面的例子中,没有给 `welcomeMessage` 赋初始值,所以变量 `welcomeMessage` 的类型是通过一个类型注指定的,而不是通过初始值推断的。
### 常量和变量的命名 {#naming}
常量和变量名可以包含任何字符,包括 Unicode 字符:
常量和变量名可以包含几乎所有的字符,包括 Unicode 字符:
```swift
let π = 3.14159
@ -116,7 +116,7 @@ print(friendlyWelcome)
// 输出“Bonjour!”
```
`print(_:separator:terminator:)` 是一个用来输出一个或多个值到适当输出区的全局函数。如果你用 Xcode`print(_:separator:terminator:)` 将会输出内容到“console”面板上。`separator``terminator` 参数具有默认值,因此你调用这个函数的时候可以忽略它们。默认情况下,该函数通过添加换行符来结束当前行。如果不想换行,可以传递一个空字符串给 `terminator` 参数--例如,`print(someValue, terminator:"")` 。关于参数默认值的更多信息,请参考[默认参数值](./06_Functions.md#default_parameter_values)。
`print(_:separator:terminator:)` 是一个用来输出一个或多个值到适当输出区的全局函数。如果你用 Xcode`print(_:separator:terminator:)` 将会输出内容到“console”面板上。`separator``terminator` 参数具有默认值,因此你调用这个函数的时候可以忽略它们。默认情况下,该函数通过添加换行符来结束当前行。如果不想换行,可以传递一个空字符串给 `terminator` 参数--例如,`print(someValue, terminator:"")` 。关于参数默认值的更多信息,请参考 [默认参数值](./06_Functions.md#default-parameter-values)。
Swift 用*字符串插值string interpolation*的方式把常量名或者变量名当做占位符加入到长字符串中Swift 会用当前常量或变量的值替换这些占位符。将常量或变量名放入圆括号中,并在开括号前使用反斜杠将其转义:
@ -127,7 +127,7 @@ print("The current value of friendlyWelcome is \(friendlyWelcome)")
> 注意
>
> 字符串插值所有可用的选项,请参考[字符串插值](./03_Strings_and_Characters.md#string_interpolation)。
> 字符串插值所有可用的选项,请参考 [字符串插值](./03_Strings_and_Characters.md#string-interpolation)。
## 注释 {#comments}
@ -200,7 +200,7 @@ Swift 也提供了一个特殊的无符号类型 `UInt`,长度与当前平台
> 注意
>
> 尽量不要使用 `UInt`,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用 `Int`,即使你要存储的值已知是非负的。统一使用 `Int` 可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断,请参考[类型安全和类型推断](#type_safety_and_type_inference)。
> 尽量不要使用 `UInt`,除非你真的需要存储一个和当前平台原生字长相同的无符号整数。除了这种情况,最好使用 `Int`,即使你要存储的值已知是非负的。统一使用 `Int` 可以提高代码的可复用性,避免不同类型数字之间的转换,并且匹配数字的类型推断,请参考 [类型安全和类型推断](#type-safety-and-type-inference)。
## 浮点数 {#floating-point-numbers}
@ -213,7 +213,7 @@ Swift 也提供了一个特殊的无符号类型 `UInt`,长度与当前平台
> 注意
>
> `Double` 精确度很高至少有15位数字,而 `Float` 只有6位数字。选择哪个类型取决于你的代码需要处理的值的范围,在两种类型都匹配的情况下,将优先选择 `Double`
> `Double` 精确度很高,至少有 15 位小数,而 `Float` 只有 6 位小数。选择哪个类型取决于你的代码需要处理的值的范围,在两种类型都匹配的情况下,将优先选择 `Double`
## 类型安全和类型推断 {#type-safety-and-type-inference}
@ -327,7 +327,7 @@ let twoThousandAndOne = twoThousand + UInt16(one)
现在两个数字的类型都是 `UInt16`,可以进行相加。目标常量 `twoThousandAndOne` 的类型被推断为 `UInt16`,因为它是两个 `UInt16` 值的和。
`SomeType(ofInitialValue)` 是调用 Swift 构造器并传入一个初始值的默认方法。在语言内部,`UInt16` 有一个构造器,可以接受一个 `UInt8` 类型的值,所以这个构造器可以用现有的 `UInt8` 来创建一个新的 `UInt16`。注意,你并不能传入任意类型的值,只能传入 `UInt16` 内部有对应构造器的值。不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型),请参考[扩展](./20_Extensions.md)。
`SomeType(ofInitialValue)` 是调用 Swift 构造器并传入一个初始值的默认方法。在语言内部,`UInt16` 有一个构造器,可以接受一个 `UInt8` 类型的值,所以这个构造器可以用现有的 `UInt8` 来创建一个新的 `UInt16`。注意,你并不能传入任意类型的值,只能传入 `UInt16` 内部有对应构造器的值。不过你可以扩展现有的类型来让它可以接收其他类型的值(包括自定义类型),请参考 [扩展](./20_Extensions.md)。
### 整数和浮点数转换 {#integer-and-floating-point-conversion}
@ -396,7 +396,7 @@ if turnipsAreDelicious {
// 输出“Eww, turnips are horrible.”
```
条件语句,例如 `if`,请参考[控制流](./05_Control_Flow.md)。
条件语句,例如 `if`,请参考 [控制流](./05_Control_Flow.md)。
如果你在需要使用 `Bool` 类型的地方使用了非布尔值Swift 的类型安全机制会报错。下面的例子会报告一个编译时错误:
@ -416,7 +416,7 @@ if i == 1 {
}
```
`i == 1` 的比较结果是 `Bool` 类型,所以第二个例子可以通过类型检查。类似 `i == 1` 这样的比较,请参考[基本操作符](./05_Control_Flow.md)。
`i == 1` 的比较结果是 `Bool` 类型,所以第二个例子可以通过类型检查。类似 `i == 1` 这样的比较,请参考 [基本操作符](./05_Control_Flow.md)。
和 Swift 中的其他类型安全的例子一样,这个方法可以避免错误并保证这块代码的意图总是清晰的。
@ -477,11 +477,11 @@ print("The status message is \(http200Status.description)")
// 输出“The status message is OK”
```
作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个 `(Int, String)` 元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。请参考[函数参数与返回值](./06_Functions.md#Function_Parameters_and_Return_Values)。
作为函数返回值时,元组非常有用。一个用来获取网页的函数可能会返回一个 `(Int, String)` 元组来描述是否获取成功。和只能返回一个类型的值比较起来,一个包含两个不同类型值的元组可以让函数的返回信息更有用。请参考 [函数参数与返回值](./06_Functions.md#Function-Parameters-and-Return-Values)。
> 注意
>
> 元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或结构体而不是元组。请参考[类和结构体](./09_Classes_and_Structures.md)。
> 当遇到一些相关值的简单分组时,元组是很有用的。元组不适合用来创建复杂的数据结构。如果你的数据结构比较复杂,不要使用元组,用类或结构体去建模。欲获得更多信息,请参考 [结构体和类](./09_Structures_And_Classes.md)。
## 可选类型 {#optionals}
@ -552,7 +552,7 @@ if convertedNumber != nil {
// 输出“convertedNumber has an integer value of 123.”
```
更多关于 `if` 语句的内容,请参考[控制流](./05_Control_Flow.md)。
更多关于 `if` 语句的内容,请参考 [控制流](./05_Control_Flow.md)。
> 注意
>
@ -560,7 +560,7 @@ if convertedNumber != nil {
### 可选绑定 {#optional-binding}
使用*可选绑定optional binding*来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在 `if``while` 语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量。`if``while` 语句,请参考[控制流](./05_Control_Flow.md)。
使用*可选绑定optional binding*来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可选绑定可以用在 `if``while` 语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋给一个常量或者变量。`if``while` 语句,请参考 [控制流](./05_Control_Flow.md)。
像下面这样在 `if` 语句中写一个可选绑定:
@ -570,7 +570,7 @@ if let constantName = someOptional {
}
```
你可以像上面这样使用可选绑定来重写 在[可选类型](./01_The_Basics.md#optionals)举出的 `possibleNumber` 例子:
你可以像上面这样使用可选绑定来重写 在 [可选类型](./01_The_Basics.md#optionals) 举出的 `possibleNumber` 例子:
```swift
if let actualNumber = Int(possibleNumber) {
@ -589,7 +589,7 @@ if let actualNumber = Int(possibleNumber) {
你可以在可选绑定中使用常量和变量。如果你想在 `if` 语句的第一个分支中操作 `actualNumber` 的值,你可以改成 `if var actualNumber`,这样可选类型包含的值就会被赋给一个变量而非常量。
你可以包含多个可选绑定或多个布尔条件在一个 `if` 语句中,只要使用逗号分开就行。只要有任意一个可选绑定的值为 `nil`,或者任意一个布尔条件为 `false`,则整个 `if` 条件判断为 `false`,这时你就需要使用嵌套 `if` 条件语句来处理,如下所示
你可以包含多个可选绑定或多个布尔条件在一个 `if` 语句中,只要使用逗号分开就行。只要有任意一个可选绑定的值为 `nil`,或者任意一个布尔条件为 `false`,则整个 `if` 条件判断为 `false`。下面的两个 `if` 语句是等价的
```swift
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
@ -609,7 +609,7 @@ if let firstNumber = Int("4") {
> 注意
>
> 在 `if` 条件语句中使用常量和变量来创建一个可选绑定,仅在 `if` 语句的句中(`body`)中才能获取到值。相反,在 `guard` 语句中使用常量和变量来创建一个可选绑定,仅在 `guard` 语句外且在语句后才能获取到值,请参考[提前退出](./05_Control_Flow.md#early_exit)。
> 在 `if` 条件语句中使用常量和变量来创建一个可选绑定,仅在 `if` 语句的句中(`body`)中才能获取到值。相反,在 `guard` 语句中使用常量和变量来创建一个可选绑定,仅在 `guard` 语句外且在语句后才能获取到值,请参考 [提前退出](./05_Control_Flow.md#early-exit)。
### 隐式解析可选类型 {#implicityly-unwrapped-optionals}
@ -617,9 +617,9 @@ if let firstNumber = Int("4") {
有时候在程序架构中第一次被赋值之后可以确定一个可选类型_总会_有值。在这种情况下每次都要判断和解析可选值是非常低效的因为可以确定它总会有值。
这种类型的可选状态被定义为隐式解析可选类型implicitly unwrapped optionals。把想要用作可选的类型的后面的问号`String?`)改成感叹号(`String!`)来声明一个隐式解析可选类型。
这种类型的可选状态被定义为隐式解析可选类型implicitly unwrapped optionals。把想要用作可选的类型的后面的问号`String?`)改成感叹号(`String!`)来声明一个隐式解析可选类型。与其在使用时把感叹号放在可选类型的名称的后面,你可以在定义它时,直接把感叹号放在可选类型的后面。
当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考[无主引用以及隐式解析可选属性](./23_Automatic_Reference_Counting.md#unowned_references_and_implicitly_unwrapped_optional_properties)。
当可选类型被第一次赋值之后就可以确定之后一直有值的时候,隐式解析可选类型非常有用。隐式解析可选类型主要被用在 Swift 中类的构造过程中,请参考 [无主引用以及隐式解析可选属性](./24_Automatic_Reference_Counting.md#unowned-references-and-implicitly-unwrapped-optional-properties)。
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。下面的例子展示了可选类型 `String` 和隐式解析可选类型 `String` 之间的区别:
@ -631,13 +631,15 @@ let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要感叹号
```
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。你要做的只是声明的时候把感叹号放到类型的结尾,而不是每次取值的可选名字的结尾
你可以把隐式解析可选类型当做一个可以自动解析的可选类型。当你使用一个隐式解析可选值时Swift 首先会把它当作普通的可选值如果它不能被当成可选类型使用Swift 会强制解析可选值。在以上的代码中,可选值 `assumedString` 在把自己的值赋给 `implicitString` 之前会被强制解析,原因是 `implicitString` 本身的类型是非可选类型的 `String`。在下面的代码中,`optionalString` 并没有显式的数据类型。那么根据类型推断,它就是一个普通的可选类型
```swift
let optionalString = assumedString
// optionalString 的类型是 "String?"assumedString 也没有被强制解析。
```
> 注意
>
> 如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个惊叹号一样。
如果你在隐式解析可选类型没有值的时候尝试取值,会触发运行时错误。和你在没有值的普通可选类型后面加一个感叹号一样。
仍然可以把隐式解析可选类型当做普通可选类型来判断它是否包含值:
你可以把隐式解析可选类型当做普通可选类型来判断它是否包含值:
```swift
if assumedString != nil {
@ -707,7 +709,7 @@ do {
如果没有错误被抛出,`eatASandwich()` 函数会被调用。如果一个匹配 `SandwichError.outOfCleanDishes` 的错误被抛出,`washDishes()` 函数会被调用。如果一个匹配 `SandwichError.missingIngredients` 的错误被抛出,`buyGroceries(_:)` 函数会被调用,并且使用 `catch` 所捕捉到的关联值 `[String]` 作为参数。
抛出,捕捉,以及传播错误会在[错误处理](./17_Error_Handling.md)章节详细说明。
抛出,捕捉,以及传播错误会在 [错误处理](./17_Error_Handling.md) 章节详细说明。
## 断言和先决条件 {#assertions-and-preconditions}
@ -715,7 +717,7 @@ do {
你使用断言和先决条件来表达你所做的假设和你在编码时候的期望。你可以将这些包含在你的代码中。断言帮助你在开发阶段找到错误和不正确的假设,先决条件帮助你在生产环境中探测到存在的问题。
除了在运行时验证你的期望值,断言和先决条件也变成了一个在你的代码中的有用的文档形式。和在上面讨论过的[错误处理](./17_Error_Handling.md)不同,断言和先决条件并不是用来处理可以恢复的或者可预期的错误。因为一个断言失败表明了程序正处于一个无效的状态,没有办法去捕获一个失败的断言。
除了在运行时验证你的期望值,断言和先决条件也变成了一个在你的代码中的有用的文档形式。和在上面讨论过的 [错误处理](./17_Error_Handling.md) 不同,断言和先决条件并不是用来处理可以恢复的或者可预期的错误。因为一个断言失败表明了程序正处于一个无效的状态,没有办法去捕获一个失败的断言。
使用断言和先决条件不是一个能够避免出现程序出现无效状态的编码方法。然而,如果一个无效状态程序产生了,断言和先决条件可以强制检查你的数据和程序状态,使得你的程序可预测的中止(译者:不是系统强制的,被动的中止),并帮助使这个问题更容易调试。一旦探测到无效的状态,执行则被中止,防止无效的状态导致的进一步对于系统的伤害。
@ -762,7 +764,7 @@ if age > 10 {
precondition(index > 0, "Index must be greater than zero.")
```
你可以调用 `precondition(_:_:file:line:)` 方法来表明出现了一个错误例如switch 进入了 default 分支,但是所有的有效值应该被任意一个其他分支(非 default 分支)处理。
你可以调用 `preconditionFailure(_:file:line:)` 方法来表明出现了一个错误例如switch 进入了 default 分支,但是所有的有效值应该被任意一个其他分支(非 default 分支)处理。
> 注意
>

View File

@ -2,11 +2,11 @@
*运算符*是检查、改变、合并值的特殊符号或短语。例如,加号(`+`)将两个数相加(如 `let i = 1 + 2`)。更复杂的运算例子包括逻辑与运算符 `&&`(如 `if enteredDoorCode && passedRetinaScan`)。
Swift 支持大部分标准 C 语言的运算符,且为了减少常见编码错误做了部分改进。如:赋值符(`=`)不再有返回值,这样就消除了手误将判等运算符(`==`)写成赋值符导致代码错误的缺陷。算术运算符(`+``-``*``/``%` 等)的结果会被检测并禁止值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见[溢出运算符](./26_Advanced_Operators.md#overflow_operators)。
Swift 支持运算符你可能在别的语言比如 C 语言里已经认识了,同时为了减少常见编码错误对它们做了部分改进。如:赋值符(`=`)不再有返回值,这样就消除了手误将判等运算符(`==`)写成赋值符导致代码错误的缺陷。算术运算符(`+``-``*``/``%` 等)的结果会被检测并禁止值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见 [溢出运算符](./27_Advanced_Operators.md#overflow-operators)。
Swift 还提供了 C 语言没有的区间运算符,例如 `a..<b``a...b`,这方便我们表达一个区间内的数值。
本章节只描述了 Swift 中的基本运算符,[高级运算符](./26_Advanced_Operators.md)这章会包含 Swift 中的高级运算符,及如何自定义运算符,及如何进行自定义类型的运算符重载。
本章节只描述了 Swift 中的基本运算符,[高级运算符](./27_Advanced_Operators.md) 这章会包含 Swift 中的高级运算符,及如何自定义运算符,及如何进行自定义类型的运算符重载。
## 术语 {#terminology}
@ -62,7 +62,7 @@ Swift 中所有数值类型都支持了基本的四则*算术运算符*
10.0 / 2.5 // 等于 4.0
```
与 C 语言和 Objective-C 不同的是Swift 默认情况下不允许在数值运算中出现溢出情况。但是你可以使用 Swift 的溢出运算符来实现溢出运算(如 `a &+ b`)。详情参见[溢出运算符](./26_Advanced_Operators.md#overflow_operators)。
与 C 语言和 Objective-C 不同的是Swift 默认情况下不允许在数值运算中出现溢出情况。但是你可以使用 Swift 的溢出运算符来实现溢出运算(如 `a &+ b`)。详情参见 [溢出运算符](./27_Advanced_Operators.md#overflow-operators)。
加法运算符也可用于 `String` 的拼接:
@ -153,11 +153,11 @@ a += 2
>
> 复合赋值运算没有返回值,`let b = a += 2` 这类代码是错误。这不同于上面提到的自增和自减运算符。
更多 Swift 标准库运算符的信息,请看[运算符声明](https://developer.apple.com/documentation/swift/operator_declarations)。
更多 Swift 标准库运算符的信息,请看 [运算符声明](https://developer.apple.com/documentation/swift/operator_declarations)。
## 比较运算符Comparison Operators {#comparison-operators}
所有标准 C 语言中的*比较运算符*都可以在 Swift 中使用
Swift 支持以下的比较运算符
- 等于(`a == b`
- 不等于(`a != b`
@ -168,7 +168,7 @@ a += 2
> 注意
>
> Swift 也提供恒等(`===`)和不恒等(`!==`)这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在[类与结构](./09_Classes_and_Structures.md)章节的 **Identity Operators** 部分。
> Swift 也提供恒等(`===`)和不恒等(`!==`)这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在 [类与结构](./09_Structures_And_Classes.md) 章节的 **Identity Operators** 部分。
每个比较运算都返回了一个标识表达式是否成立的布尔值:
@ -193,7 +193,7 @@ if name == "world" {
// 输出“hello, world", 因为 `name` 就是等于 "world”
```
关于 `if` 语句,请看[控制流](./05_Control_Flow.md)。
关于 `if` 语句,请看 [控制流](./05_Control_Flow.md)。
如果两个元组的元素相同,且长度相同的话,元组就可以被比较。比较元组大小会按照从左到右、逐值比较的方式,直到发现有两个值不等时停止。如果所有的值都相等,那么这一对元组我们就称它们是相等的。例如:
@ -267,7 +267,7 @@ if hasHeader {
a != nil ? a! : b
```
上述代码使用了三元运算符。当可选类型 `a` 的值不为空时,进行强制解`a!`),访问 `a` 中的值;反之返回默认值 `b`。无疑空合运算符(`??`)提供了一种更为优雅的方式去封装条件判断和解两种行为,显得简洁以及更具可读性。
上述代码使用了三元运算符。当可选类型 `a` 的值不为空时,进行强制解`a!`),访问 `a` 中的值;反之返回默认值 `b`。无疑空合运算符(`??`)提供了一种更为优雅的方式去封装条件判断和解两种行为,显得简洁以及更具可读性。
> 注意
>
@ -286,7 +286,7 @@ var colorNameToUse = userDefinedColorName ?? defaultColorName
`userDefinedColorName` 变量被定义为一个可选的 `String` 类型,默认值为 `nil`。由于 `userDefinedColorName` 是一个可选类型,我们可以使用空合运算符去判断其值。在上一个例子中,通过空合运算符为一个名为 `colorNameToUse` 的变量赋予一个字符串类型初始值。
由于 `userDefinedColorName` 值为空,因此表达式 `userDefinedColorName ?? defaultColorName` 返回 `defaultColorName` 的值,即 `red`
如果你分配一个非空值(`non-nil`)给 `userDefinedColorName`,再次执行空合运算,运算结果为封包在 `userDefaultColorName` 中的值,而非默认值。
如果你分配一个非空值(`non-nil`)给 `userDefinedColorName`,再次执行空合运算,运算结果为封包在 `userDefinedColorName` 中的值,而非默认值。
```swift
userDefinedColorName = "green"
@ -315,7 +315,7 @@ for index in 1...5 {
// 5 * 5 = 25
```
关于 `for-in` 循环,请看[控制流](./05_Control_Flow.md)。
关于 `for-in` 循环,请看 [控制流](./05_Control_Flow.md)。
### 半开区间运算符 {#half-open-range-operator}
@ -336,7 +336,7 @@ for i in 0..<count {
// 第 4 个人叫 Jack
```
数组有 4 个元素,但 `0..<count` 只数到3最后一个元素的下标因为它是半开区间。关于数组请查阅[数组](./04_Collection_Types.md#arrays)。
数组有 4 个元素,但 `0..<count` 只数到3最后一个元素的下标因为它是半开区间。关于数组请查阅 [数组](./04_Collection_Types.md#arrays)。
### 单侧区间 {#one-sided-ranges}
@ -402,7 +402,7 @@ if !allowedEntry {
在示例代码中,小心地选择布尔常量或变量有助于代码的可读性,并且避免使用双重逻辑非运算,或混乱的逻辑语句。
### 逻辑与运算符 #{logical_and_operator}
### 逻辑与运算符 {#logical-and-operator}
*逻辑与运算符*`a && b`)表达了只有 `a``b` 的值都为 `true` 时,整个表达式的值才会是 `true`
@ -421,7 +421,7 @@ if enteredDoorCode && passedRetinaScan {
// 输出“ACCESS DENIED”
```
### 逻辑或运算符 #{logical_or_operator}
### 逻辑或运算符 {#logical-or-operator}
逻辑或运算符(`a || b`)是一个由两个连续的 `|` 组成的中置运算符。它表示了两个逻辑表达式的其中一个为 `true`,整个表达式就为 `true`

View File

@ -1,8 +1,8 @@
# 字符串和字符
*字符串*是一系列字符的集合,例如 `"hello, world"``"albatross"`。Swift 的字符串通过 `String` 类型来表示。而 `String` 内容的访问方式有多种,例如以 `Character` 值的集合。
*字符串*是一系列字符的集合,例如 `"hello, world"``"albatross"`。Swift 的字符串通过 `String` 类型来表示。而 `String` 内容的访问方式有多种,例如以 `Character` 值的集合。
Swift 的 `String``Character` 类型提供了一种快速且兼容 Unicode 的方式来处理代码中的文本内容。创建和操作字符串的语法与 C 语言中字符串操作相似,轻量并且易读。通过 `+` 符号就可以非常简单的实现两个字符串的拼接操作。与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。你可以在已有字符串中插入常量、变量、字面量和表达式从而形成更长的字符串,这一过程也被为字符串插值。尤其是在为显示、存储和打印创建自定义字符串值时,字符串插值操作尤其有用。
Swift 的 `String``Character` 类型提供了一种快速且兼容 Unicode 的方式来处理代码中的文本内容。创建和操作字符串的语法与 C 语言中字符串操作相似,轻量并且易读。通过 `+` 符号就可以非常简单的实现两个字符串的拼接操作。与 Swift 中其他值一样,能否更改字符串的值,取决于其被定义为常量还是变量。你可以在已有字符串中插入常量、变量、字面量和表达式从而形成更长的字符串,这一过程也被为字符串插值。尤其是在为显示、存储和打印创建自定义字符串值时,字符串插值操作尤其有用。
尽管语法简易,但 Swift 中的 `String` 类型的实现却很快速和现代化。每一个字符串都是由编码无关的 Unicode 字符组成,并支持访问字符的多种 Unicode 表示形式。
@ -106,11 +106,11 @@ Escaping all three quotes \"\"\"
### 扩展字符串分隔符 {#extended-string-delimiters}
您可以将字符串文字放在扩展分隔符中,这样字符串中的特殊字符将会被直接包含而非转义后的效果。将字符串放在引号(**"**)中并用数字符号(****)括起来。例如,打印字符串文字 **"Line 1 \ nLine 2"** 打印换行符转义序列(**\n**)而不是进行换行打印
您可以将字符串文字放在扩展分隔符中,这样字符串中的特殊字符将会被直接包含而非转义后的效果。将字符串放在引号(`"`)中并用数字符号(`#`)括起来。例如,打印字符串文字 `#"Line 1 \nLine 2"#`打印换行符转义序列(`\n`)而不是给文字换行
如果需要字符串文字中字符的特殊效果,请匹配转义字符(**\\**)后面添加与起始位置个数相匹配的 **#** 符。 例如,如果您的字符串是 **"Line 1 \ nLine 2"** 并且您想要换行,则可以使用 **#“Line 1 \ #nLine 2”#** 来代替。 同样,**###"Line1 \ ### nLine2"###** 也可以实现换行效果。
如果需要字符串文字中字符的特殊效果,请匹配转义字符(`\`)后面添加与起始位置个数相匹配的 `#` 符。 例如,如果您的字符串是 `#"Line 1 \nLine 2"#` 并且您想要换行,则可以使用 `#"Line 1 \#nLine 2"#` 来代替。 同样,`###"Line1 \###nLine2"###` 也可以实现换行效果。
扩展分隔符创建的字符串文字也可以是多行字符串文字。 您可以使用扩展分隔符在多行字符串中包含文本 **"""**,覆盖原有的结束文字的默认行为。例如:
扩展分隔符创建的字符串文字也可以是多行字符串文字。 您可以使用扩展分隔符在多行字符串中包含文本 `"""`,覆盖原有的结束文字的默认行为。例如:
```swift
let threeMoreDoubleQuotationMarks = #"""
@ -157,7 +157,7 @@ constantString += " and another Highlander"
## 字符串是值类型 {#strings-are-value-types}
在 Swift 中 `String` 类型是*值类型*。如果你创建了一个新的字符串,那么当其进行常量、变量赋值操作,或在函数/方法中传递时,会进行值拷贝。在前述任一情况下,都会对已有字符串值创建新副本,并对该新副本而非原始字符串进行传递或赋值操作。值类型在 [结构体和枚举是值类型](./09_Classes_and_Structures.md#structures_and_enumerations_are_value_types) 中进行了详细描述。
在 Swift 中 `String` 类型是*值类型*。如果你创建了一个新的字符串,那么当其进行常量、变量赋值操作,或在函数/方法中传递时,会进行值拷贝。在前述任一情况下,都会对已有字符串值创建新副本,并对该新副本而非原始字符串进行传递或赋值操作。值类型在 [结构体和枚举是值类型](./09_Structures_And_Classes.md#structures-and-enumerations-are-value-types) 中进行了详细描述。
Swift 默认拷贝字符串的行为保证了在函数/方法向你传递的字符串所属权属于你,无论该值来自于哪里。你可以确信传递的字符串不会被修改,除非你自己去修改它。
@ -178,7 +178,7 @@ for character in "Dog!🐶" {
// 🐶
```
`for-in` 循环在 [For 循环](./05_Control_Flow.md#for_loops) 中进行了详细描述。
`for-in` 循环在 [For 循环](./05_Control_Flow.md#for-loops) 中进行了详细描述。
另外,通过标明一个 `Character` 类型并用字符字面量进行赋值,可以建立一个独立的字符常量或变量:
@ -269,6 +269,20 @@ let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
`multiplier` 的值也作为字符串中后面表达式的一部分。该表达式计算 `Double(multiplier) * 2.5` 的值并将结果(`7.5`)插入到字符串中。在这个例子中,表达式写为 `\(Double(multiplier) * 2.5)` 并包含在字符串字面量中。
你可以使用扩展字符串分隔符创建字符串,来包含不想作为字符串插值处理的字符。例如:
```swift
print(#"Write an interpolated string in Swift using \(multiplier)."#)
// 打印 "Write an interpolated string in Swift using \(multiplier)."
```
如果要在使用扩展字符串分隔符的字符串中使用字符串插值,需要在反斜杠后面添加与开头和结尾数量相同扩展字符串分隔符。例如:
```swift
print(#"6 times 7 is \#(6 * 7)."#)
// 打印 "6 times 7 is 42."
```
> 注意
>
> 插值字符串中写在括号中的表达式不能包含非转义反斜杠(`\`),并且不能包含回车或换行符。不过,插值字符串可以包含其他字面量。
@ -384,7 +398,7 @@ greeting[index]
```swift
greeting[greeting.endIndex] // error
greeting.index(after: endIndex) // error
greeting.index(after: greeting.endIndex) // error
```
使用 `indices` 属性会创建一个包含全部索引的范围(`Range`),用来在一个字符串中访问单个字符。
@ -430,11 +444,11 @@ welcome.removeSubrange(range)
## 子字符串 {#substrings}
当你从字符串中获取一个子字符串 —— 例如,使用下标或者 `prefix(_:)` 之类的方法 —— 就可以得到一个 `SubString` 的实例,而非另外一个 `String`。Swift 里的 `SubString` 绝大部分函数都跟 `String` 一样,意味着你可以使用同样的方式去操作 `SubString``String`。然而,跟 `String` 不同的是,你只有在短时间内需要操作字符串时,才会使用 `SubString`。当你需要长时间保存结果时,就把 `SubString` 转化为 `String` 的实例:
当你从字符串中获取一个子字符串 —— 例如,使用下标或者 `prefix(_:)` 之类的方法 —— 就可以得到一个 `Substring` 的实例,而非另外一个 `String`。Swift 里的 `Substring` 绝大部分函数都跟 `String` 一样,意味着你可以使用同样的方式去操作 `Substring``String`。然而,跟 `String` 不同的是,你只有在短时间内需要操作字符串时,才会使用 `Substring`。当你需要长时间保存结果时,就把 `Substring` 转化为 `String` 的实例:
```swift
let greeting = "Hello, world!"
let index = greeting.index(of: ",") ?? greeting.endIndex
let index = greeting.firstIndex(of: ",") ?? greeting.endIndex
let beginning = greeting[..<index]
// beginning 的值为 "Hello"
@ -442,15 +456,15 @@ let beginning = greeting[..<index]
let newString = String(beginning)
```
就像 `String`,每一个 `SubString` 都会在内存里保存字符集。而 `String``SubString` 的区别在于性能优化上,`SubString` 可以重用原 `String` 的内存空间,或者另一个 `SubString` 的内存空间(`String` 也有同样的优化,但如果两个 `String` 共享内存的话,它们就会相等)。这一优化意味着你在修改 `String``SubString` 之前都不需要消耗性能去复制内存。就像前面说的那样,`SubString` 不适合长期存储 —— 因为它重用了原 `String` 的内存空间,原 `String` 的内存空间必须保留直到它的 `SubString` 不再被使用为止。
就像 `String`,每一个 `Substring` 都会在内存里保存字符集。而 `String``Substring` 的区别在于性能优化上,`Substring` 可以重用原 `String` 的内存空间,或者另一个 `Substring` 的内存空间(`String` 也有同样的优化,但如果两个 `String` 共享内存的话,它们就会相等)。这一优化意味着你在修改 `String``Substring` 之前都不需要消耗性能去复制内存。就像前面说的那样,`Substring` 不适合长期存储 —— 因为它重用了原 `String` 的内存空间,原 `String` 的内存空间必须保留直到它的 `Substring` 不再被使用为止。
上面的例子,`greeting` 是一个 `String`,意味着它在内存里有一片空间保存字符集。而由于 `beginning``greeting``SubString`,它重用了 `greeting` 的内存空间。相反,`newString` 是一个 `String` —— 它是使用 `SubString` 创建的,拥有一片自己的内存空间。下面的图展示了他们之间的关系:
上面的例子,`greeting` 是一个 `String`,意味着它在内存里有一片空间保存字符集。而由于 `beginning``greeting``Substring`,它重用了 `greeting` 的内存空间。相反,`newString` 是一个 `String` —— 它是使用 `Substring` 创建的,拥有一片自己的内存空间。下面的图展示了他们之间的关系:
![](https://docs.swift.org/swift-book/_images/stringSubstring_2x.png)
> 注意
>
> `String``SubString` 都遵循 `StringProtocol<//apple_ref/swift/intf/s:s14StringProtocolP>` 协议,这意味着操作字符串的函数使用 `StringProtocol` 会更加方便。你可以传入 `String``SubString` 去调用函数。
> `String``Substring` 都遵循 [`StringProtocol`](https://developer.apple.com/documentation/swift/stringprotocol) 协议,这意味着操作字符串的函数使用 `StringProtocol` 会更加方便。你可以传入 `String``Substring` 去调用函数。
## 比较字符串 {#comparing-strings}
@ -458,7 +472,7 @@ Swift 提供了三种方式来比较文本值:字符串字符相等、前缀
### 字符串/字符相等 {#string-and-character-equality}
字符串/字符可以用等于操作符(`==`)和不等于操作符(`!=`),详细描述在[比较运算符](./02_Basic_Operators.md#comparison_operators)
字符串/字符可以用等于操作符(`==`)和不等于操作符(`!=`),详细描述在 [比较运算符](./02_Basic_Operators.md#comparison-operators)
```swift
let quotation = "We're a lot alike, you and I."
@ -556,13 +570,13 @@ print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
> 注意
>
> `hasPrefix(_:)``hasSuffix(_:)` 方法都是在每个字符串中逐字符比较其可扩展的字符群集是否标准相等,详细描述在[字符串/字符相等](#string_and_character_equality)。
> `hasPrefix(_:)``hasSuffix(_:)` 方法都是在每个字符串中逐字符比较其可扩展的字符群集是否标准相等,详细描述在 [字符串/字符相等](#string-and-character-equality)。
## 字符串的 Unicode 表示形式 {#unicode-representations-of-strings}
当一个 Unicode 字符串被写进文本文件或者其他储存时,字符串中的 Unicode 标量会用 Unicode 定义的几种 `编码格式`encoding forms编码。每一个字符串中的小块编码都被称 `代码单元`code units。这些包括 UTF-8 编码格式(编码字符串为 8 位的代码单元), UTF-16 编码格式(编码字符串位 16 位的代码单元),以及 UTF-32 编码格式编码字符串32位的代码单元
Swift 提供了几种不同的方式来访问字符串的 Unicode 表示形式。你可以利用 `for-in` 来对字符串进行遍历,从而以 Unicode 可扩展的字符群集的方式访问每一个 `Character` 值。该过程在 [使用字符](#working_with_characters) 中进行了描述。
Swift 提供了几种不同的方式来访问字符串的 Unicode 表示形式。你可以利用 `for-in` 来对字符串进行遍历,从而以 Unicode 可扩展的字符群集的方式访问每一个 `Character` 值。该过程在 [使用字符](#working-with-characters) 中进行了描述。
另外,能够以其他三种 Unicode 兼容的方式访问字符串的值:

View File

@ -0,0 +1,644 @@
# 集合类型
Swift 语言提供数组Array、集合Set和字典Dictionary三种基本的*集合类型*用来存储集合数据。数组是有序数据的集。集合是无序无重复数据的集。字典是无序的键值对的集。
![](https://docs.swift.org/swift-book/_images/CollectionTypes_intro_2x.png)
Swift 中的数组、集合和字典必须明确其中保存的键和值类型,这样就可以避免插入一个错误数据类型的值。同理,对于获取到的值你也可以放心,其数据类型是确定的。
> 注意
>
> Swift 的数组、集合和字典类型被实现为*泛型集合*。更多关于泛型类型和集合,参见 [泛型](./22_Generics.md) 章节。
## 集合的可变性 {#mutability-of-collections}
如果创建一个数组、集合或字典并且把它分配成一个变量,这个集合将会是*可变的*。这意味着可以在创建之后添加、修改或者删除数据项。如果把数组、集合或字典分配成常量,那么它就是*不可变的*,它的大小和内容都不能被改变。
> 注意
>
> 在不需要改变集合的时候创建不可变集合是很好的实践。这样做便于你理解自己的代码,也能让 Swift 编译器优化集合的性能。
## 数组Arrays {#arrays}
*数组*使用有序列表存储同一类型的多个值。相同的值可以多次出现在一个数组的不同位置中。
> 注意
>
> Swift 的 `Array` 类型被桥接到 Foundation 中的 `NSArray` 类。
>
> 更多关于在 Foundation 和 Cocoa 中使用 `Array` 的信息,参见 [Bridging Between Array and NSArray](https://developer.apple.com/documentation/swift/array#2846730)。
### 数组的简单语法 {#array-type-shorthand-syntax}
Swift 中数组的完整写法为 `Array<Element>`,其中 `Element` 是这个数组中唯一允许存在的数据类型。也可以使用像 `[Element]` 这样的简单语法。尽管两种形式在功能上是一样的,但是推荐较短的那种,而且在本文中都会使用这种形式来使用数组。
### 创建一个空数组 {#creating-an-empty-array}
你可以使用构造语法来创建一个由特定数据类型构成的空数组:
```swift
var someInts: [Int] = []
print("someInts is of type [Int] with \(someInts.count) items.")
// 打印“someInts is of type [Int] with 0 items.”
```
注意,通过构造函数的类型,`someInts` 的值类型被推断为 `[Int]`
或者,如果代码上下文中已经提供了类型信息,例如一个函数参数或者一个已经定义好类型的常量或者变量,你可以使用空数组语句创建一个空数组,它的写法很简单:`[]`(一对空方括号):
```swift
someInts.append(3)
// someInts 现在包含一个 Int 值
someInts = []
// someInts 现在是空数组,但是仍然是 [Int] 类型的。
```
### 创建一个带有默认值的数组 {#creating-an-array-with-a-default-value}
Swift 中的 `Array` 类型还提供一个可以创建特定大小并且所有数据都被默认的构造方法。可以把准备加入新数组的数据项数量(`count`)和适当类型的初始值(`repeating`)传入数组构造函数:
```swift
var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles 是一种 [Double] 数组,等价于 [0.0, 0.0, 0.0]
```
### 通过两个数组相加创建一个数组 {#creating-an-array-by-adding-two-arrays-together}
你可以使用加法操作符(`+`)来组合两个已存在的相同类型数组。新数组的数据类型会从两个数组的数据类型中推断出来:
```swift
var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles 被推断为 [Double],等价于 [2.5, 2.5, 2.5]
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles 被推断为 [Double],等价于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
```
### 用数组字面量构造数组 {#creating-an-array-with-an-array-literals}
你可以使用*数组字面量*来进行数组构造,这是一种用一个或者多个数值构造数组的简单方法。数组字面量是一系列由逗号分割并由方括号包含的数值:
`[value 1, value 2, value 3]`
下面这个例子创建了一个叫做 `shoppingList` 并且存储 `String` 的数组:
```swift
var shoppingList: [String] = ["Eggs", "Milk"]
// shoppingList 已经被构造并且拥有两个初始项。
```
`shoppingList` 变量被声明为“字符串值类型的数组“,记作 `[String]`。因为这个数组被规定只有 `String` 一种数据结构,所以只有 `String` 类型可以在其中被存取。在这里,`shoppingList` 数组由两个 `String` 值(`"Eggs"``"Milk"`)构造,并且由数组字面量定义。
> 注意
>
> `shoppingList` 数组被声明为变量(`var` 关键字创建)而不是常量(`let` 创建)是因为之后会有更多的数据项被插入其中。
在这个例子中,字面量仅仅包含两个 `String` 值。匹配了该数组的声明(只能包含 `String` 的数组),所以可以将这个字面量的赋值过程看作用两个初始项来构造 `shoppingList` 的一种方式。
由于 Swift 的类型推断机制,当你用字面量构造拥有相同类型值数组的时候,不必把数组的类型定义清楚。`shoppingList` 的构造也可以这样写:
```swift
var shoppingList = ["Eggs", "Milk"]
```
因为所有数组字面量中的值都是相同的类型Swift 可以推断出 `[String]``shoppingList` 中变量的正确类型。
### 访问和修改数组 {#accessing-and-modifying-an-array}
你可以通过数组的方法和属性来访问和修改数组,或者使用下标语法。
可以使用数组的只读属性 `count` 来获取数组中的数据项数量:
```swift
print("The shopping list contains \(shoppingList.count) items.")
// 输出“The shopping list contains 2 items.”这个数组有2个项
```
使用布尔属性 `isEmpty` 作为一个缩写形式去检查 `count` 属性是否为 `0`
```swift
if shoppingList.isEmpty {
print("The shopping list is empty.")
} else {
print("The shopping list is not empty.")
}
// 打印“The shopping list is not empty.”shoppinglist 不是空的)
```
也可以使用 `append(_:)` 方法在数组后面添加新的数据项:
```swift
shoppingList.append("Flour")
// shoppingList 现在有3个数据项似乎有人在摊煎饼
```
除此之外,也可以使用加法赋值运算符(`+=`)直接将另一个相同类型数组中的数据添加到该数组后面:
```swift
shoppingList += ["Baking Powder"]
// shoppingList 现在有四项了
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
// shoppingList 现在有七项了
```
可以直接使用*下标语法*来获取数组中的数据项,把所需要数据项的索引值直接放在数组名称之后的方括号中:
```swift
var firstItem = shoppingList[0]
// 第一项是“Eggs”
```
> 注意
>
> 第一项在数组中的索引值是 `0` 而不是 `1`。 Swift 中的数组索引总是从零开始。
你也可以用下标来改变某个有效索引值对应的数据值:
```swift
shoppingList[0] = "Six eggs"
// 其中的第一项现在是“Six eggs”而不是“Eggs”
```
当你使用下标语法,所使用的下标必须是有效的。例如,试图通过 `shoppingList[shoppingList.count] = "Salt"` 在数组的最后添加一项,将产生一个运行时错误。
还可以利用下标来一次改变一系列数据值,即使新数据和原有数据的数量是不一样的。下面的例子把 `"Chocolate Spread"``"Cheese"``"Butter"` 替换为 `"Bananas"``"Apples"`
```swift
shoppingList[4...6] = ["Bananas", "Apples"]
// shoppingList 现在有6项
```
通过调用数组的 `insert(_:at:)` 方法在某个指定索引值之前添加数据项:
```swift
shoppingList.insert("Maple Syrup", at: 0)
// shoppingList 现在有7项
// 现在是这个列表中的第一项是“Maple Syrup”
```
这次 `insert(_:at:)` 方法调用把值为 `"Maple Syrup"` 的新数据项插入列表的最开始位置,并且使用 `0` 作为索引值。
类似的可以使用 `remove(at:)` 方法来移除数组中的某一项。这个方法把数组在特定索引值中存储的数据项移除并且返回这个被移除的数据项(不需要的时候就可以无视它):
```swift
let mapleSyrup = shoppingList.remove(at: 0)
// 索引值为0的数据项被移除
// shoppingList 现在只有6项而且不包括 Maple Syrup
// mapleSyrup 常量的值等于被移除数据项“Maple Syrup”
```
> 注意
>
> 如果你试图通过越界索引来执行访问或者修改数据的操作,会引发一个运行时错误。此时可以使用索引值和数组的 `count` 属性进行比较来在使用该索引之前检验其是否有效。除了当 `count` 等于 0 时(说明这是个空数组),最大索引值一直是 `count - 1`,因为数组都是零起索引。
数据项被移除后数组中的空出项会被自动填补,所以现在索引值为 `0` 的数据项的值再次等于 `"Six eggs"`
```swift
firstItem = shoppingList[0]
// firstItem 现在等于“Six eggs”
```
如果你只想把数组中的最后一项移除,可以使用 `removeLast()` 方法而不是 `remove(at:)` 方法来避免需要获取数组的 `count` 属性。就像后者一样,前者也会返回被移除的数据项:
```swift
let apples = shoppingList.removeLast()
// 数组的最后一项被移除了
// shoppingList 现在只有5项不包括 Apples
// apples 常量的值现在等于字符串“Apples”
```
### 数组的遍历 {#iterating-over-an-array}
你可以使用 `for-in` 循环来遍历数组中所有的数据项:
```swift
for item in shoppingList {
print(item)
}
// Six eggs
// Milk
// Flour
// Baking Powder
// Bananas
```
如果同时需要每个数据项的值和索引值,可以使用 `enumerated()` 方法来进行数组遍历。`enumerated()` 返回一个由索引值和数据值组成的元组数组。索引值从零开始,并且每次增加一;如果枚举一整个数组,索引值将会和数据值一一匹配。你可以把这个元组分解成临时常量或者变量来进行遍历:
```swift
for (index, value) in shoppingList.enumerated() {
print("Item \(String(index + 1)): \(value)")
}
// Item 1: Six eggs
// Item 2: Milk
// Item 3: Flour
// Item 4: Baking Powder
// Item 5: Bananas
```
更多关于 `for-in` 循环的介绍请参见 [For 循环](./05_Control_Flow.md#for-loops)。
## 集合Sets {#sets}
*集合*用来存储相同类型并且没有确定顺序的值。当集合元素顺序不重要时或者希望确保每个元素只出现一次时可以使用集合而不是数组。
> 注意
> Swift 的 `Set` 类型被桥接到 Foundation 中的 `NSSet` 类。
>
> 关于使用 Foundation 和 Cocoa 中 `Set` 的知识,参见 [Bridging Between Set and NSSet](https://developer.apple.com/documentation/swift/set#2845530)
### 集合类型的哈希值 {#hash-values-for-set-types}
一个类型为了存储在集合中,该类型必须是*可哈希化*的——也就是说,该类型必须提供一个方法来计算它的*哈希值*。一个哈希值是 `Int` 类型的,相等的对象哈希值必须相同,比如 `a == b`,因此必须 `a.hashValue == b.hashValue`
Swift 的所有基本类型(比如 `String``Int``Double``Bool`)默认都是可哈希化的,可以作为集合值的类型或者字典键的类型。没有关联值的枚举成员值(在 [枚举](./08_Enumerations.md) 有讲述)默认也是可哈希化的。
> 注意
>
> 你可以使用自定义的类型作为集合值的类型或者是字典键的类型,但需要使自定义类型遵循 Swift 标准库中的 `Hashable` 协议。遵循 `Hashable` 协议的类型需要提供一个类型为 `Int` 的可读属性 `hashValue`。由类型的 `hashValue` 属性返回的值不需要在同一程序的不同执行周期或者不同程序之间保持相同。
>
> 因为 `Hashable` 协议遵循 `Equatable` 协议,所以遵循该协议的类型也必须提供一个“是否相等”运算符(`==`)的实现。这个 `Equatable` 协议要求任何遵循 `==` 实现的实例间都是一种相等的关系。也就是说,对于 `a,b,c` 三个值来说,`==` 的实现必须满足下面三种情况:
> * `a == a`(自反性)
> * `a == b` 意味着 `b == a`(对称性)
> * `a == b && b == c` 意味着 `a == c`(传递性)
>
> 关于遵循协议的更多信息,请看 [协议](./21_Protocols.md)。
### 集合类型语法 {#set-type-syntax}
Swift 中的集合类型被写为 `Set<Element>`,这里的 `Element` 表示集合中允许存储的类型。和数组不同的是,集合没有等价的简化形式。
### 创建和构造一个空的集合 {#creating-and-initalizing-an-empty-set}
你可以通过构造器语法创建一个特定类型的空集合:
```swift
var letters = Set<Character>()
print("letters is of type Set<Character> with \(letters.count) items.")
// 打印“letters is of type Set<Character> with 0 items.”
```
> 注意
>
> 通过构造器,这里 `letters` 变量的类型被推断为 `Set<Character>`。
此外,如果上下文提供了类型信息,比如作为函数的参数或者已知类型的变量或常量,你可以通过一个空的数组字面量创建一个空的集合:
```swift
letters.insert("a")
// letters 现在含有1个 Character 类型的值
letters = []
// letters 现在是一个空的 Set但是它依然是 Set<Character> 类型
```
### 用数组字面量创建集合 {#creating-a-set-with-an-array-literal}
你可以使用数组字面量来构造集合,相当于一种简化的形式将一个或者多个值作为集合元素。
下面的例子创建一个称之为 `favoriteGenres` 的集合来存储 `String` 类型的值:
```swift
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// favoriteGenres 被构造成含有三个初始值的集合
```
这个 `favoriteGenres` 变量被声明为“一个 `String` 值的集合”,写为 `Set<String>`。由于这个特定集合指定了值为 `String` 类型,所以它*只*允许存储 `String` 类型值。这里的 `favoriteGenres` 变量有三个 `String` 类型的初始值(`"Rock"``"Classical"``"Hip hop"`),以数组字面量的形式书写。
> 注意
>
> `favoriteGenres` 被声明为一个变量(拥有 `var` 标示符)而不是一个常量(拥有 `let` 标示符),因为它里面的元素将会在之后的例子中被增加或者移除。
一个集合类型不能从数组字面量中被直接推断出来,因此 `Set` 类型必须显式声明。然而,由于 Swift 的类型推断功能,如果你想使用一个数组字面量构造一个集合并且与该数组字面量中的所有元素类型相同,那么无须写出集合的具体类型。`favoriteGenres` 的构造形式可以采用简化的方式代替:
```swift
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
```
由于数组字面量中的所有元素类型相同Swift 可以推断出 `Set<String>` 作为 `favoriteGenres` 变量的正确类型。
### 访问和修改一个集合 {#accesing-and-modifying-a-set}
你可以通过集合的属性和方法来对其进行访问和修改。
为了获取一个集合中元素的数量,可以使用其只读属性 `count`
```swift
print("I have \(favoriteGenres.count) favorite music genres.")
// 打印“I have 3 favorite music genres.”
```
使用布尔属性 `isEmpty` 作为一个缩写形式去检查 `count` 属性是否为 `0`
```swift
if favoriteGenres.isEmpty {
print("As far as music goes, I'm not picky.")
} else {
print("I have particular music preferences.")
}
// 打印“I have particular music preferences.”
```
你可以通过调用集合的 `insert(_:)` 方法来添加一个新元素:
```swift
favoriteGenres.insert("Jazz")
// favoriteGenres 现在包含4个元素
```
你可以通过调用集合的 `remove(_:)` 方法去删除一个元素,如果它是该集合的一个元素则删除它并且返回它的值,若该集合不包含它,则返回 `nil`。另外,集合可以通过 `removeAll()` 方法删除所有元素。
```swift
if let removedGenre = favoriteGenres.remove("Rock") {
print("\(removedGenre)? I'm over it.")
} else {
print("I never much cared for that.")
}
// 打印“Rock? I'm over it.”
```
使用 `contains(_:)` 方法去检查集合中是否包含一个特定的值:
```swift
if favoriteGenres.contains("Funk") {
print("I get up on the good foot.")
} else {
print("It's too funky in here.")
}
// 打印“It's too funky in here.”
```
### 遍历一个集合 {#iterating-over-a-set}
你可以在一个 `for-in` 循环中遍历一个集合中的所有值。
```swift
for genre in favoriteGenres {
print("\(genre)")
}
// Classical
// Jazz
// Hip hop
```
更多关于 `for-in` 循环的信息,参见 [For 循环](./05_Control_Flow.md#for-loops)。
Swift 的 `Set` 类型没有确定的顺序,为了按照特定顺序来遍历一个集合中的值可以使用 `sorted()` 方法,它将返回一个有序数组,这个数组的元素排列顺序由操作符 `<` 对元素进行比较的结果来确定。
```swift
for genre in favoriteGenres.sorted() {
print("\(genre)")
}
// Classical
// Hip hop
// Jazz
```
## 集合操作 {#performing-set-operations}
你可以高效地完成集合的一些基本操作,比如把两个集合组合到一起,判断两个集合共有元素,或者判断两个集合是否全包含,部分包含或者不相交。
### 基本集合操作 {#fundamental-set-operations}
下面的插图描述了两个集合 `a``b`,以及通过阴影部分的区域显示集合各种操作的结果。
![](https://docs.swift.org/swift-book/_images/setVennDiagram_2x.png)
* 使用 `intersection(_:)` 方法根据两个集合的交集创建一个新的集合。
* 使用 `symmetricDifference(_:)` 方法根据两个集合不相交的值创建一个新的集合。
* 使用 `union(_:)` 方法根据两个集合的所有值创建一个新的集合。
* 使用 `subtracting(_:)` 方法根据不在另一个集合中的值创建一个新的集合。
```swift
let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersection(evenDigits).sorted()
// []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9]
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]
```
### 集合成员关系和相等 {#set-membership-and-equality}
下面的插图描述了三个集合 `a``b``c`,以及通过重叠区域表述集合间共享的元素。集合 `a` 是集合 `b` 的*父集合*,因为 `a` 包含了 `b` 中所有的元素。相反的,集合 `b` 是集合 `a` 的*子集合*,因为属于 `b` 的元素也被 `a` 包含。集合 `b` 和集合 `c` 是*不相交*的,因为它们之间没有共同的元素。
![](https://docs.swift.org/swift-book/_images/setEulerDiagram_2x.png)
* 使用“是否相等”运算符(`==`)来判断两个集合包含的值是否全部相同。
* 使用 `isSubset(of:)` 方法来判断一个集合中的所有值是否也被包含在另外一个集合中。
* 使用 `isSuperset(of:)` 方法来判断一个集合是否包含另一个集合中所有的值。
* 使用 `isStrictSubset(of:)` 或者 `isStrictSuperset(of:)` 方法来判断一个集合是否是另外一个集合的子集合或者父集合并且两个集合并不相等。
* 使用 `isDisjoint(with:)` 方法来判断两个集合是否不含有相同的值(是否没有交集)。
```swift
let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"]
houseAnimals.isSubset(of: farmAnimals)
// true
farmAnimals.isSuperset(of: houseAnimals)
// true
farmAnimals.isDisjoint(with: cityAnimals)
// true
```
## 字典 {#dictionaries}
*字典*是一种无序的集合它存储的是键值对之间的关系其所有键的值需要是相同的类型所有值的类型也需要相同。每个值value都关联唯一的*键*key键作为字典中这个值数据的标识符。和数组中的数据项不同字典中的数据项并没有具体顺序。你在需要通过标识符访问数据的时候使用字典这种方法很大程度上和在现实世界中使用字典查字义的方法一样。
> 注意
>
> Swift 的 `Dictionary` 类型被桥接到 Foundation 的 `NSDictionary` 类。
>
> 更多关于在 Foundation 和 Cocoa 中使用 `Dictionary` 类型的信息,参见 [Bridging Between Dictionary and NSDictionary](https://developer.apple.com/documentation/swift/dictionary#2846239)。
### 字典类型简化语法 {#dictionary-type-shorthand-syntax}
Swift 的字典使用 `Dictionary<Key, Value>` 定义,其中 `Key` 是一种可以在字典中被用作键的类型,`Value` 是字典中对应于这些键所存储值的数据类型。
> 注意
>
> 一个字典的 `Key` 类型必须遵循 `Hashable` 协议,就像 `Set` 的值类型。
你也可以用 `[Key: Value]` 这样简化的形式去表示字典类型。虽然这两种形式功能上相同,但是后者是首选,并且本教程中涉及到字典类型时通篇采用后者。
### 创建一个空字典 {#creating-an-empty-dictionary}
你可以像数组一样使用构造语法创建一个拥有确定类型的空字典:
```swift
var namesOfIntegers: [Int: String] = [:]
// namesOfIntegers 是一个空的 [Int: String] 字典
```
这个例子创建了一个 `[Int: String]` 类型的空字典来储存整数的英语命名。它的键是 `Int` 型,值是 `String` 型。
如果上下文已经提供了类型信息,你可以使用空字典字面量来创建一个空字典,记作 `[:]` (一对方括号中放一个冒号):
```swift
namesOfIntegers[16] = "sixteen"
// namesOfIntegers 现在包含一个键值对
namesOfIntegers = [:]
// namesOfIntegers 又成为了一个 [Int: String] 类型的空字典
```
### 用字典字面量创建字典 {#creating-a-dictionary-with-a-dictionary-literal}
你可以使用*字典字面量*来构造字典,这和刚才介绍过的数组字面量拥有相似语法。字典字面量是一种将一个或多个键值对写作 `Dictionary` 集合的快捷途径。
*一个键值对*是一个键和一个值的结合体。在字典字面量中,每一个键值对的键和值都由冒号分割。这些键值对构成一个列表,其中这些键值对由逗号分割、并整体被包裹在一对方括号中:
```swift
[key 1: value 1, key 2: value 2, key 3: value 3]
```
下面的例子创建了一个存储国际机场名称的字典。在这个字典中键是三个字母的国际航空运输相关代码,值是机场名称:
```swift
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
```
`airports` 字典被声明为一种 `[String: String]` 类型,这意味着这个字典的键和值都是 `String` 类型。
> 注意
>
> `airports` 字典被声明为变量(用 `var` 关键字)而不是常量(用 `let` 关键字)因为后面会有更多的机场信息被添加到这个字典中。
`airports` 字典使用字典字面量初始化,包含两个键值对。第一对的键是 `YYZ`,值是 `Toronto Pearson`。第二对的键是 `DUB`,值是 `Dublin`
这个字典语句包含了两个 `String: String` 类型的键值对。它们对应 `airports` 变量声明的类型(一个只有 `String` 键和 `String` 值的字典),所以这个字典字面量的赋值是一种方式用来构造拥有两个初始数据项的 `airport` 字典。
和数组一样,你在用字典字面量构造字典时,如果它的键和值都有各自一致的类型,那么就不必写出字典的类型。
`airports` 字典也可以用这种简短方式定义:
```swift
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
```
因为这个语句中所有的键和值都各自拥有相同的数据类型Swift 可以推断出 `[String: String]``airports` 字典的正确类型。
### 访问和修改字典 {#accessing-and-modifying-a-dictionary}
你可以通过字典的方法和属性来访问和修改字典,或者通过使用下标语法。
和数组一样,可以通过 `Dictionary` 的只读属性 `count` 来获取字典的数据项数量:
```swift
print("The dictionary of airports contains \(airports.count) items.")
// 打印“The dictionary of airports contains 2 items.”(这个字典有两个数据项)
```
使用布尔属性 `isEmpty` 作为一个缩写形式去检查 `count` 属性是否为 `0`
```swift
if airports.isEmpty {
print("The airports dictionary is empty.")
} else {
print("The airports dictionary is not empty.")
}
// 打印“The airports dictionary is not empty.”
```
你可以通过下标语法来给字典添加新的数据项。可以使用一个恰当类型的键作为下标索引,并且分配恰当类型的新值:
```swift
airports["LHR"] = "London"
// airports 字典现在有三个数据项
```
也可以使用下标语法来改变特定键对应的值:
```swift
airports["LHR"] = "London Heathrow"
// “LHR”对应的值被改为“London Heathrow”
```
作为一种替代下标语法的方式,字典的 `updateValue(_:forKey:)` 方法可以设置或者更新特定键对应的值。就像上面所示的下标示例,`updateValue(_:forKey:)` 方法在这个键不存在对应值的时候会设置新值或者在存在时更新已存在的值。和下标的方式不同,`updateValue(_:forKey:)` 这个方法返回更新值之前的*原值*。这样使得你可以检查更新是否成功。
`updateValue(_:forKey:)` 方法会返回对应值类型的可选类型。举例来说:对于存储 `String` 值的字典,这个函数会返回一个 `String?` 或者“可选 `String`”类型的值。如果有值存在于更新前,则这个可选值包含了旧值,否则它将会是 `nil`
```swift
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
print("The old value for DUB was \(oldValue).")
}
// 输出“The old value for DUB was Dublin.”
```
你也可以使用下标语法来在字典中检索特定键对应的值。因为有可能请求的键没有对应的值存在,字典的下标访问会返回对应值类型的可选类型。如果这个字典包含请求键所对应的值,下标会返回一个包含这个存在值的可选类型,否则将返回 `nil`
```swift
if let airportName = airports["DUB"] {
print("The name of the airport is \(airportName).")
} else {
print("That airport is not in the airports dictionary.")
}
// 打印“The name of the airport is Dublin Airport.”
```
还可以使用下标语法通过将某个键的对应值赋值为 `nil` 来从字典里移除一个键值对:
```swift
airports["APL"] = "Apple Internation"
// “Apple Internation”不是真的 APL 机场,删除它
airports["APL"] = nil
// APL 现在被移除了
```
此外,`removeValue(forKey:)` 方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的值或者在没有对应值的情况下返回 `nil`
```swift
if let removedValue = airports.removeValue(forKey: "DUB") {
print("The removed airport's name is \(removedValue).")
} else {
print("The airports dictionary does not contain a value for DUB.")
}
// 打印“The removed airport's name is Dublin Airport.”
```
### 字典遍历 {#iterating-over-a-dictionary}
你可以使用 `for-in` 循环来遍历某个字典中的键值对。每一个字典中的数据项都以 `(key, value)` 元组形式返回,并且可以使用临时常量或者变量来分解这些元组:
```swift
for (airportCode, airportName) in airports {
print("\(airportCode): \(airportName)")
}
// YYZ: Toronto Pearson
// LHR: London Heathrow
```
更多关于 `for-in` 循环的信息,参见 [For 循环](./05_Control_Flow.md#for-loops)。
通过访问 `keys` 或者 `values` 属性,你也可以遍历字典的键或者值:
```swift
for airportCode in airports.keys {
print("Airport code: \(airportCode)")
}
// Airport code: YYZ
// Airport code: LHR
for airportName in airports.values {
print("Airport name: \(airportName)")
}
// Airport name: Toronto Pearson
// Airport name: London Heathrow
```
如果你需要使用某个字典的键集合或者值集合来作为某个接受 `Array` 实例的 API 的参数,可以直接使用 `keys` 或者 `values` 属性构造一个新数组:
```swift
let airportCodes = [String](airports.keys)
// airportCodes 是 ["YYZ", "LHR"]
let airportNames = [String](airports.values)
// airportNames 是 ["Toronto Pearson", "London Heathrow"]
```
Swift 的 `Dictionary` 是无序集合类型。为了以特定的顺序遍历字典的键或值,可以对字典的 `keys``values` 属性使用 `sorted()` 方法。

View File

@ -23,19 +23,19 @@ for name in names {
// Hello, Jack!
```
你也可以通过遍历一个字典来访问它的键值对。遍历字典时,字典的每项元素会以 `(key, value)` 元组的形式返回,你可以在 `for-in` 循环中使用显式的常量名称来解读 `(key, value)` 元组。下面的例子中,字典的键声明`animalName` 常量,字典的值会声明为 `legCount` 常量:
你也可以通过遍历一个字典来访问它的键值对。遍历字典时,字典的每项元素会以 `(key, value)` 元组的形式返回,你可以在 `for-in` 循环中使用显式的常量名称来解读 `(key, value)` 元组。下面的例子中,字典的键声明为 `animalName` 常量,字典的值会声明为 `legCount` 常量:
```swift
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
print("\(animalName)s have \(legCount) legs")
}
// cats have 4 legs
// ants have 6 legs
// spiders have 8 legs
// cats have 4 legs
```
字典的内容理论上是无序的,遍历元素时的顺序是无法确定的。将元素插入字典的顺序并不会决定它们被遍历的顺序。关于数组和字典的细节,参见[集合类型](./04_Collection_Types.md)。
字典的内容理论上是无序的,遍历元素时的顺序是无法确定的。将元素插入字典的顺序并不会决定它们被遍历的顺序。关于数组和字典的细节,参见 [集合类型](./04_Collection_Types.md)。
`for-in` 循环还可以使用数字范围。下面的例子用来输出乘法表的一部分内容:
@ -69,7 +69,7 @@ print("\(base) to the power of \(power) is \(answer)")
这个例子计算 base 这个数的 power 次幂(本例中,是 `3``10` 次幂),从 `1``3``0` 次幂)开始做 `3` 的乘法, 进行 `10` 次,使用 `1``10` 的闭区间循环。这个计算并不需要知道每一次循环中计数器具体的值,只需要执行了正确的循环次数即可。下划线符号 `_` (替代循环中的变量)能够忽略当前值,并且不提供循环遍历时对值的访问。
在某些情况下,你可能不想使用包括两个端点的闭区间。想象一下,你在一个手表上绘制分钟的刻度线。总共 `60` 个刻度,从 `0` 分开始。使用半开区间运算符(`..<`)来表示一个左闭右开的区间。有关区间的更多信息,请参阅[区间运算符](./02_Basic_Operators.md#range_operators)。
在某些情况下,你可能不想使用包括两个端点的闭区间。想象一下,你在一个手表上绘制分钟的刻度线。总共 `60` 个刻度,从 `0` 分开始。使用半开区间运算符(`..<`)来表示一个左闭右开的区间。有关区间的更多信息,请参阅 [区间运算符](./02_Basic_Operators.md#range-operators)。
```swift
let minutes = 60
@ -97,6 +97,8 @@ for tickMark in stride(from: 3, through: hours, by: hourInterval) {
}
```
以上示例使用 `for-in` 循环来遍历范围、数组、字典和字符串。你可以用它来遍历任何的集合,包括实现了 [Sequence](https://developer.apple.com/documentation/swift/sequence) 协议的自定义类或集合类型。
## While 循环 {#while-loops}
`while` 循环会一直运行一段语句直到条件变成 `false`。这类循环适合使用在第一次迭代前迭代次数未知的情况下。Swift 提供两种 `while` 循环形式:
@ -324,7 +326,7 @@ default:
// 输出“The last letter of the alphabet”
```
在这个例子中,第一个 case 分支用于匹配第一个英文字母 `a`,第二个 case 分支用于匹配最后一个字母 `z`。因为 `switch` 语句必须有一个 case 分支用于覆盖所有可能的字符,而不仅仅是所有的英文字母,所以 switch 语句使用 `default` 分支来匹配除了 `a``z` 外的所有值,这个分支保证了 swith 语句的完备性。
在这个例子中,第一个 case 分支用于匹配第一个英文字母 `a`,第二个 case 分支用于匹配最后一个字母 `z`。因为 `switch` 语句必须有一个 case 分支用于覆盖所有可能的字符,而不仅仅是所有的英文字母,所以 switch 语句使用 `default` 分支来匹配除了 `a``z` 外的所有值,这个分支保证了 switch 语句的完备性。
#### 不存在隐式的贯穿 {#no-implicit-fallthrough}
@ -332,7 +334,7 @@ default:
> 注意
>
> 虽然在 Swift 中 `break` 不是必须的,但你依然可以在 case 分支中的代码执行完毕前使用 `break` 跳出,详情请参见[Switch 语句中的 break](#break_in_a_switch_statement)。
> 虽然在 Swift 中 `break` 不是必须的,但你依然可以在 case 分支中的代码执行完毕前使用 `break` 跳出,详情请参见 [Switch 语句中的 break](#break-in-a-switch-statement)。
每一个 case 分支都*必须*包含至少一条语句。像下面这样书写代码是无效的,因为第一个 case 分支是空的:
@ -363,11 +365,11 @@ default:
// 输出“The letter A”
```
为了可读性,符合匹配可以写成多行形式,详情请参考[复合匹配](#compound_cases)
为了可读性,符合匹配可以写成多行形式,详情请参考 [复合匹配](#compound-cases)
> 注意
>
> 如果想要显式贯穿 case 分支,请使用 `fallthrough` 语句,详情请参考[贯穿](#fallthrough)。
> 如果想要显式贯穿 case 分支,请使用 `fallthrough` 语句,详情请参考 [贯穿](#fallthrough)。
#### 区间匹配 {#interval-matching}
@ -527,7 +529,7 @@ default:
- `return`
- `throw`
我们将会在下面讨论 `continue``break``fallthrough` 语句。`return` 语句将会在[函数](./06_Functions.md)章节讨论,`throw` 语句会在[错误抛出](./18_Error_Handling.md#throwing_errors)章节讨论。
我们将会在下面讨论 `continue``break``fallthrough` 语句。`return` 语句将会在 [函数](./06_Functions.md) 章节讨论,`throw` 语句会在 [错误抛出](./17_Error_Handling.md#throwing-errors) 章节讨论。
### Continue {#continue}
@ -754,7 +756,7 @@ if #available(iOS 10, macOS 10.12, *) {
以上可用性条件指定,`if` 语句的代码块仅仅在 iOS 10 或 macOS 10.12 及更高版本才运行。最后一个参数,`*`是必须的用于指定在所有其它平台中如果版本号高于你的设备指定的最低版本if 语句的代码块将会运行。
在它一般的形式中,可用性条件使用了一个平台名字和版本的列表。平台名字可以是 `iOS``macOS``watchOS``tvOS`——请访问[声明属性](../chapter3/06_Attributes.html)来获取完整列表。除了指定像 iOS 8 或 macOS 10.10 的大版本号,也可以指定像 iOS 11.2.6 以及 macOS 10.13.3 的小版本号。
在它一般的形式中,可用性条件使用了一个平台名字和版本的列表。平台名字可以是 `iOS``macOS``watchOS``tvOS`——请访问 [声明属性](../03_language_reference/07_Attributes.md) 来获取完整列表。除了指定像 iOS 8 或 macOS 10.10 的大版本号,也可以指定像 iOS 11.2.6 以及 macOS 10.13.3 的小版本号。
```swift
if #available(平台名称 版本号, ..., *) {

View File

@ -202,6 +202,30 @@ if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
// 打印“min is -6 and max is 109”
```
### 隐式返回的函数 {#functions-with-an-implicit-return}
如果一个函数的整个函数体是一个单行表达式,这个函数可以隐式地返回这个表达式。举个例子,以下的函数有着同样的作用:
```
func greeting(for person: String) -> String {
"Hello, " + person + "!"
}
print(greeting(for: "Dave"))
// 打印 "Hello, Dave!"
func anotherGreeting(for person: String) -> String {
return "Hello, " + person + "!"
}
print(anotherGreeting(for: "Dave"))
// 打印 "Hello, Dave!"
```
`greeting(for:)` 函数的完整定义是打招呼内容的返回,这就意味着它能使用隐式返回这样更简短的形式。`anothergreeting(for:)` 函数返回同样的内容,却因为 `return` 关键字显得函数更长。任何一个可以被写成一行 `return` 语句的函数都可以忽略 `return`
正如你将会在 [简略的 Getter 声明](./10_Properties.md) 里看到的, 一个属性的 getter 也可以使用隐式返回的形式。
## 函数参数标签和参数名称 {#Function-Argument-Labels-and-Parameter-Names}
每个函数参数都有一个*参数标签argument label*以及一个*参数名称parameter name*。参数标签在调用函数的时候使用;调用的时候需要将函数的参数标签写在对应的参数前面。参数名称在函数的实现中使用。默认情况下,函数参数使用参数名称来作为它们的参数标签。
@ -213,7 +237,7 @@ func someFunction(firstParameterName: Int, secondParameterName: Int) {
someFunction(firstParameterName: 1, secondParameterName: 2)
```
所有的参数都必须有一个独一无二的名字。虽然多个参数拥有同样的参数标签是可能的,但是一个唯一的数标签能够使你的代码更具可读性。
所有的参数都必须有一个独一无二的名字。虽然多个参数拥有同样的参数标签是可能的,但是一个唯一的数标签能够使你的代码更具可读性。
### 指定参数标签 {#specifying-argument-labels}
@ -294,7 +318,7 @@ arithmeticMean(3, 8.25, 18.75)
函数参数默认是常量。试图在函数体中更改参数值将会导致编译错误。这意味着你不能错误地更改参数值。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为*输入输出参数In-Out Parameters*。
定义一个输入输出参数时,在参数定义前加 `inout` 关键字。一个 `输入输出参数`有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。想获取更多的关于输入输出参数的细节和相关的编译器优化,请查看[输入输出参数](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID545)一节。
定义一个输入输出参数时,在参数定义前加 `inout` 关键字。一个 `输入输出参数`有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。想获取更多的关于输入输出参数的细节和相关的编译器优化,请查看 [输入输出参数](../03_language_reference/06_Declarations.md#in-out-parameters) 一节。
你只能传递变量给输入输出参数。你不能传入常量或者字面量,因为这些量是不能被修改的。当传入的参数作为输入输出参数时,需要在参数名前加 `&` 符,表示这个值可以被函数修改。

View File

@ -6,9 +6,9 @@
> 注意
>
> 如果你不熟悉捕获capturing这个概念也不用担心在[值捕获](#capturing_values)章节有它更详细的介绍。
> 如果你不熟悉捕获capturing这个概念也不用担心 [值捕获](#capturing-values) 章节有它更详细的介绍。
在[函数](./06_Functions.md)章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采用如下三种形式之一:
[函数](./06_Functions.md) 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采用如下三种形式之一:
* 全局函数是一个有名字但不会捕获任何值的闭包
* 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
@ -23,7 +23,7 @@ Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进
## 闭包表达式 {#closure-expressions}
[嵌套函数](./06_Functions.md#Nested_Functions)作为复杂函数的一部分时,它自包含代码块式的定义和命名形式在使用上带来了方便。当然,编写未完整声明和没有函数名的类函数结构代码是很有用的,尤其是在编码中涉及到函数作为参数的那些方法时。
[嵌套函数](./06_Functions.md#Nested-Functions) 作为复杂函数的一部分时,它自包含代码块式的定义和命名形式在使用上带来了方便。当然,编写未完整声明和没有函数名的类函数结构代码是很有用的,尤其是在编码中涉及到函数作为参数的那些方法时。
*闭包表达式*是一种构建内联闭包的方式,它的语法简洁。在保证不丢失它语法清晰明了的同时,闭包表达式提供了几种优化的语法简写形式。下面通过对 `sorted(by:)` 这一个案例的多次迭代改进来展示这个过程,每次迭代都使用了更加简明的方式描述了相同功能。。
@ -113,13 +113,13 @@ reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
Swift 自动为内联闭包提供了参数名称缩写功能,你可以直接通过 `$0``$1``$2` 来顺序调用闭包的参数,以此类推。
如果你在闭包表达式中使用参数名称缩写,你可以在闭包定义中省略参数列表,并且对应参数名称缩写的类型会通过函数类型进行推断。`in` 关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
如果你在闭包表达式中使用参数名称缩写,你可以在闭包定义中省略参数列表,并且对应参数名称缩写的类型会通过函数类型进行推断。闭包接受的参数的数量取决于所使用的缩写参数的最大编号。`in` 关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
```swift
reversedNames = names.sorted(by: { $0 > $1 } )
```
在这个例子中,`$0``$1` 表示闭包中第一个和第二个 `String` 类型的参数。
在这个例子中,`$0``$1` 表示闭包中第一个和第二个 `String` 类型的参数。因为 `$1` 是编号最大的缩写参数,所以可以理解为:该闭包需要两个参数。这里的 `sorted(by:)` 函数希望得到一个参数都是字符串的闭包,因此缩写参数 `$0``$1` 的类型均为 `String`
### 运算符方法 {#operator-methods}
@ -129,7 +129,7 @@ reversedNames = names.sorted(by: { $0 > $1 } )
reversedNames = names.sorted(by: >)
```
更多关于运算符方法的内容请查看[运算符方法](./26_Advanced_Operators.md#operator_methods)。
更多关于运算符方法的内容请查看 [运算符方法](./27_Advanced_Operators.md#operator-methods)。
## 尾随闭包 {#trailing-closures}
@ -151,7 +151,7 @@ someFunctionThatTakesAClosure() {
}
```
在[闭包表达式语法](#closure_expression_syntax)上章节中的字符串排序闭包可以作为尾随包的形式改写在 `sorted(by:)` 方法圆括号的外面:
[闭包表达式语法](#closure-expression-syntax) 上章节中的字符串排序闭包可以作为尾随包的形式改写在 `sorted(by:)` 方法圆括号的外面:
```swift
reversedNames = names.sorted() { $0 > $1 }
@ -233,7 +233,7 @@ func makeIncrementer(forIncrement amount: Int) -> () -> Int {
}
```
`makeIncrementer` 返回类型为 `() -> Int`。这意味着其返回的是一个*函数*,而非一个简单类型的值。该函数在每次调用时不接受参数,只返回一个 `Int` 类型的值。关于函数返回其他函数的内容,请查看[函数类型作为返回类型](./06_Functions.md#function_types_as_return_types)。
`makeIncrementer` 返回类型为 `() -> Int`。这意味着其返回的是一个*函数*,而非一个简单类型的值。该函数在每次调用时不接受参数,只返回一个 `Int` 类型的值。关于函数返回其他函数的内容,请查看 [函数类型作为返回类型](./06_Functions.md#function-types-as-return-types)。
`makeIncrementer(forIncrement:)` 函数定义了一个初始值为 `0` 的整型变量 `runningTotal`,用来存储当前总计数值。该值为 `incrementer` 的返回值。
@ -290,7 +290,7 @@ incrementByTen()
> 注意
>
> 如果你将闭包赋值给一个类实例的属性并且该闭包通过访问该实例或其成员而捕获了该实例你将在闭包和该实例间创建一个循环强引用。Swift 使用捕获列表来打破这种循环强引用。更多信息,请参考[闭包引起的循环强引用](./23_Automatic_Reference_Counting.md#strong_reference_cycles_for_closures)。
> 如果你将闭包赋值给一个类实例的属性并且该闭包通过访问该实例或其成员而捕获了该实例你将在闭包和该实例间创建一个循环强引用。Swift 使用捕获列表来打破这种循环强引用。更多信息,请参考 [闭包引起的循环强引用](./24_Automatic_Reference_Counting.md#strong-reference-cycles-for-closures)。
## 闭包是引用类型 {#closures-are-reference-types}
@ -364,7 +364,7 @@ print(customersInLine.count)
// 打印出“5”
print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
// 打印出“Now serving Chris!
print(customersInLine.count)
// 打印出“4”
```
@ -397,7 +397,7 @@ serve(customer: customersInLine.remove(at: 0))
>
> 过度使用 `autoclosures` 会让你的代码变得难以理解。上下文和函数名应该能够清晰地表明求值是被延迟执行的。
如果你想让一个自动闭包可以“逃逸”,则应该同时使用 `@autoclosure``@escaping` 属性。`@escaping` 属性的讲解见上面的[逃逸闭包](#escaping_closures)。
如果你想让一个自动闭包可以“逃逸”,则应该同时使用 `@autoclosure``@escaping` 属性。`@escaping` 属性的讲解见上面的 [逃逸闭包](#escaping-closures)。
```swift
// customersInLine i= ["Barry", "Daniella"]

View File

@ -8,7 +8,7 @@
在 Swift 中枚举类型是一等first-class类型。它们采用了很多在传统上只被类class所支持的特性例如计算属性computed properties用于提供枚举值的附加信息实例方法instance methods用于提供和枚举值相关联的功能。枚举也可以定义构造函数initializers来提供一个初始值可以在原始实现的基础上扩展它们的功能还可以遵循协议protocols来提供标准的功能。
想了解更多相关信息,请参见[属性](./10_Properties.md)[方法](./11_Methods.md)[构造过程](./14_Initialization.md)[扩展](./20_Extensions.md)[协议](./21_Protocols.md)。
想了解更多相关信息,请参见 [属性](./10_Properties.md)[方法](./11_Methods.md)[构造过程](./14_Initialization.md)[扩展](./20_Extensions.md)[协议](./21_Protocols.md)。
## 枚举语法 {#enumeration-syntax}
@ -84,7 +84,7 @@ case .west:
……以此类推。
正如在[控制流](./05_Control_Flow.md)中介绍的那样,在判断一个枚举类型的值时,`switch` 语句必须穷举所有情况。如果忽略了 `.west` 这种情况,上面那段代码将无法通过编译,因为它没有考虑到 `CompassPoint` 的全部成员。强制穷举确保了枚举成员不会被意外遗漏。
正如在 [控制流](./05_Control_Flow.md) 中介绍的那样,在判断一个枚举类型的值时,`switch` 语句必须穷举所有情况。如果忽略了 `.west` 这种情况,上面那段代码将无法通过编译,因为它没有考虑到 `CompassPoint` 的全部成员。强制穷举确保了枚举成员不会被意外遗漏。
当不需要匹配每个枚举成员的时候,你可以提供一个 `default` 分支来涵盖所有未明确处理的枚举成员:
@ -114,7 +114,7 @@ print("\(numberOfChoices) beverages available")
// 打印“3 beverages available”
```
在前面的例子中,通过 `Beverage.allCases` 可以访问到包含 `Beverage` 枚举所有成员的集合。`allCases` 的使用方法和其它一般集合一样——集合中的元素是枚举类型的实例,所以在上面的情况中,这些元素是 `Beverage` 值。在前面的例子中,统计了总共有多少个枚举成员。而在下面的例子中,则使用 `for` 循环来遍历所有枚举成员。
在前面的例子中,通过 `Beverage.allCases` 可以访问到包含 `Beverage` 枚举所有成员的集合。`allCases` 的使用方法和其它一般集合一样——集合中的元素是枚举类型的实例,所以在上面的情况中,这些元素是 `Beverage` 值。在前面的例子中,统计了总共有多少个枚举成员。而在下面的例子中,则使用 `for-in` 循环来遍历所有枚举成员。
```swift
for beverage in Beverage.allCases {
@ -125,7 +125,7 @@ for beverage in Beverage.allCases {
// juice
```
在前面的例子中,使用的语法表明这个枚举遵循 [CaseIterable](https://developer.apple.com/documentation/swift/caseiterable) 协议。想了解 protocols 相关信息,请参见[协议](./21_Protocols.md)。
在前面的例子中,使用的语法表明这个枚举遵循 [CaseIterable](https://developer.apple.com/documentation/swift/caseiterable) 协议。想了解 protocols 相关信息,请参见 [协议](./21_Protocols.md)。
## 关联值 {#associated-values}
@ -200,7 +200,7 @@ case let .qrCode(productCode):
## 原始值 {#raw-values}
在[关联值](#associated_values)小节的条形码例子中,演示了如何声明存储不同类型关联值的枚举成员。作为关联值的替代选择,枚举成员可以被默认值(称为*原始值*)预填充,这些原始值的类型必须相同。
[关联值](#associated-values) 小节的条形码例子中,演示了如何声明存储不同类型关联值的枚举成员。作为关联值的替代选择,枚举成员可以被默认值(称为*原始值*)预填充,这些原始值的类型必须相同。
这是一个使用 ASCII 码作为原始值的枚举:
@ -212,7 +212,7 @@ enum ASCIIControlCharacter: Character {
}
```
枚举类型 `ASCIIControlCharacter` 的原始值类型被定义为 `Character`,并设置了一些比较常见的 ASCII 控制字符。`Character` 的描述详见[字符串和字符](./03_Strings_and_Characters.md)部分。
枚举类型 `ASCIIControlCharacter` 的原始值类型被定义为 `Character`,并设置了一些比较常见的 ASCII 控制字符。`Character` 的描述详见 [字符串和字符](./03_Strings_and_Characters.md) 部分。
原始值可以是字符串、字符,或者任意整型值或浮点型值。每个原始值在枚举声明中必须是唯一的。
@ -273,7 +273,7 @@ let possiblePlanet = Planet(rawValue: 7)
> 注意
>
> 原始值构造器是一个可失败构造器,因为并不是每一个原始值都有与之对应的枚举成员。更多信息请参见[可失败构造器](../chapter3/05_Declarations.html#failable_initializers)
> 原始值构造器是一个可失败构造器,因为并不是每一个原始值都有与之对应的枚举成员。更多信息请参见 [可失败构造器](../03_language_reference/06_Declarations.md#failable-initializers)
如果你试图寻找一个位置为 `11` 的行星,通过原始值构造器返回的可选 `Planet` 值将是 `nil`

View File

@ -28,10 +28,14 @@ Swift 中结构体和类有很多共同点。两者都可以:
* 析构器允许一个类实例释放任何其所被分配的资源
* 引用计数允许对一个类的多次引用
更多信息请参见 [继承](./13_Inheritance.md)、[类型转换](./18_Type_Casting.md)、[析构过程](./15_Deinitialization.md) 和 [自动引用计数](./23_Automatic_Reference_Counting.md)。
更多信息请参见 [继承](./13_Inheritance.md)、[类型转换](./18_Type_Casting.md)、[析构过程](./15_Deinitialization.md) 和 [自动引用计数](./24_Automatic_Reference_Counting.md)。
类支持的附加功能是以增加复杂性为代价的。作为一般准则,优先使用结构体,因为它们更容易理解,仅在适当或必要时才使用类。实际上,这意味着你的大多数自定义数据类型都会是结构体和枚举。更多详细的比较参见 [在结构和类之间进行选择](https://developer.apple.com/documentation/swift/choosing_between_structures_and_classes)。
> 注意
>
> 类和 actors 共享很多特性。更多信息请参见 [并发](./28_Concurrency.md)。
### 类型定义的语法 {#definition-syntax}
结构体和类有着相似的定义方式。你通过 `struct` 关键字引入结构体,通过 `class` 关键字引入类,并将它们的具体定义放在一对大括号中:
@ -47,7 +51,7 @@ class SomeClass {
> 注意
>
> 每当你定义一个新的结构体或者类时,你都是定义了一个新的 Swift 类型。请使用 `UpperCamelCase` 这种方式来命名类型(如这里的 `SomeClass``SomeStructure`),以便符合标准 Swift 类型的大写命名风格(如 `String``Int``Bool`)。请使用 `lowerCamelCase` 这种方式来命名属性和方法(如 `framerate``incrementCount`),以便和类型名区分。
> 每当你定义一个新的结构体或者类时,你都是定义了一个新的 Swift 类型。请使用 `UpperCamelCase` 这种方式来命名类型(如这里的 `SomeClass``SomeStructure`),以便符合标准 Swift 类型的大写命名风格(如 `String``Int``Bool`)。请使用 `lowerCamelCase` 这种方式来命名属性和方法(如 `frameRate``incrementCount`),以便和类型名区分。
以下是定义结构体和定义类的示例:
@ -243,7 +247,7 @@ if tenEighty === alsoTenEighty {
请注意,“相同”(用三个等号表示,`===`)与“等于”(用两个等号表示,`==`的不同。“相同”表示两个类类型class type的常量或者变量引用同一个类实例。“等于”表示两个实例的值“相等”或“等价”判定时要遵照设计者定义的评判标准。
当在定义你的自定义结构体和类的时候,你有义务来决定判定两个实例“相等”的标准。在章节 [等价操作符](./26_Advanced_Operators.md#equivalence_operators) 中将会详细介绍实现自定义 == 和 !== 运算符的流程。
当在定义你的自定义结构体和类的时候,你有义务来决定判定两个实例“相等”的标准。在章节 [等价操作符](./27_Advanced_Operators.md#equivalence-operators) 中将会详细介绍实现自定义 == 和 != 运算符的流程。
### 指针 {#pointers}

View File

@ -6,11 +6,12 @@
另外,还可以定义属性观察器来监控属性值的变化,以此来触发自定义的操作。属性观察器可以添加到类本身定义的存储属性上,也可以添加到从父类继承的属性上。
你也可以利用属性包装器来复用多个属性的 getter 和 setter 中的代码。
## 存储属性 {#stored-properties}
简单来说,一个存储属性就是存储在特定类或结构体实例里的一个常量或变量。存储属性可以是*变量存储属性*(用关键字 `var` 定义),也可以是*常量存储属性*(用关键字 `let` 定义)。
可以在定义存储属性的时候指定默认值,请参考[默认构造器](./14_Initialization.md#default_initializers)一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考[构造过程中常量属性的修改](./14_Initialization.md#assigning_constant_properties_during_initialization)一节。
可以在定义存储属性的时候指定默认值,请参考 [默认构造器](./14_Initialization.md#default-initializers) 一节。也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值,请参考 [构造过程中常量属性的修改](./14_Initialization.md#assigning-constant-properties-during-initialization) 一节。
下面的例子定义了一个名为 `FixedLengthRange` 的结构体,该结构体用于描述整数的区间,且这个范围值在被创建后不能被修改。
@ -98,7 +99,7 @@ print(manager.importer.fileName)
### 存储属性和实例变量 {#stored-properties-and-instance-variables}
如果有过 Objective-C 经验,应该知道 Objective-C 为类实例存储值和引用提供两种方法。除了属性之外,还可以使用实例变量作为一个备份存储将变量值赋值给属性。
如果有过 Objective-C 经验,应该知道 Objective-C 为类实例存储值和引用提供两种方法。除了属性之外,还可以使用实例变量作为一个备份存储将变量值赋值给属性。
Swift 编程语言中把这些理论统一用属性来实现。Swift 中的属性没有对应的实例变量,属性的备份存储也无法直接访问。这就避免了不同场景下访问方式的困扰,同时也将属性的定义简化成一个语句。属性的全部信息——包括命名、类型和内存管理特征——作为类型定义的一部分,都定义在一个地方。
@ -174,6 +175,28 @@ struct AlternativeRect {
}
```
### 简化 Getter 声明 {#shorthand-getter-declaration}
如果整个 getter 是单一表达式getter 会隐式地返回这个表达式结果。下面是另一个版本的 `Rect` 结构体,用到了简化的 getter 和 setter 声明:
```swift
struct CompactRect {
var origin = Point()
var size = Size()
var center: Point {
get {
Point(x: origin.x + (size.width / 2),
y: origin.y + (size.height / 2))
}
set {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}
}
}
```
在 getter 中忽略 `return` 与在函数中忽略 `return` 的规则相同,请参考 [隐式返回的函数](./06_Functions.md/#functions-with-an-implicit-return)。
### 只读计算属性 {#readonly-computed-properties}
只有 getter 没有 setter 的计算属性叫*只读计算属性*。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。
@ -202,7 +225,12 @@ print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,即使新值和当前值相同的时候也不例外。
你可以为除了延时加载存储属性之外的其他存储属性添加属性观察器,你也可以在子类中通过重写属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。你不必为非重写的计算属性添加属性观察器,因为你可以直接通过它的 setter 监控和响应值的变化。属性重写请参考[重写](./13_Inheritance.md#overriding)。
你可以在以下位置添加属性观察器:
* 自定义的存储属性
* 继承的存储属性
* 继承的计算属性
对于继承的属性,你可以在子类中通过重写属性的方式为它添加属性观察器。对于自定义的计算属性来说,使用它的 setter 监控和响应值的变化,而不是尝试创建观察器。属性重写请参考 [重写](./13_Inheritance.md#overriding)。
可以为属性添加其中一个或两个观察器:
@ -217,7 +245,7 @@ print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
>
> 在父类初始化方法调用之后,在子类构造器中给父类的属性赋值时,会调用父类属性的 `willSet``didSet` 观察器。而在父类初始化方法调用之前,给子类的属性赋值时不会调用子类属性的观察器。
>
> 有关构造器代理的更多信息,请参考[值类型的构造器代理](./14_Initialization.md#initializer_delegation_for_value_types)[类的构造器代理](./14_Initialization.md#initializer_delegation_for_class_types)。
> 有关构造器代理的更多信息,请参考 [值类型的构造器代理](./14_Initialization.md#initializer-delegation-for-value-types)[类的构造器代理](./14_Initialization.md#initializer-delegation-for-class-types)。
下面是一个 `willSet``didSet` 实际运用的例子,其中定义了一个名为 `StepCounter` 的类,用来统计一个人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。
@ -256,7 +284,247 @@ stepCounter.totalSteps = 896
> 注意
>
> 如果将带有观察器的属性通过 in-out 方式传入函数,`willSet``didSet` 也会调用。这是因为 in-out 参数采用了拷入拷出内存模式:即在函数内部使用的是参数的 copy函数结束后又对参数重新赋值。关于 in-out 参数详细的介绍,请参考[输入输出参数](../chapter3/05_Declarations.html#in-out_parameters)
> 如果将带有观察器的属性通过 in-out 方式传入函数,`willSet``didSet` 也会调用。这是因为 in-out 参数采用了拷入拷出内存模式:即在函数内部使用的是参数的 copy函数结束后又对参数重新赋值。关于 in-out 参数详细的介绍,请参考 [输入输出参数](../03_language_reference/06_Declarations.md#in-out-parameters)
## 属性包装器 {#property-wrappers}
属性包装器在管理属性如何存储和定义属性的代码之间添加了一个分隔层。举例来说,如果你的属性需要线程安全性检查或者需要在数据库中存储它们的基本数据,那么必须给每个属性添加同样的逻辑代码。当使用属性包装器时,你只需在定义属性包装器时编写一次管理代码,然后应用到多个属性上来进行复用。
定义一个属性包装器,你需要创建一个定义 `wrappedValue` 属性的结构体、枚举或者类。在下面的代码中,`TwelveOrLess` 结构体确保它包装的值始终是小于等于 12 的数字。如果要求它存储一个更大的数字,它则会存储 12 这个数字。
```swift
@propertyWrapper
struct TwelveOrLess {
private var number: Int
init() { self.number = 0 }
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
```
这个 setter 确保新值小于 12而且返回被存储的值。
> 注意
>
> 上面例子以 `private` 的方式声明 `number` 变量,这使得 `number` 仅在 `TwelveOrLess` 的实现中使用。写在其他地方的代码通过使用 `wrappedValue` 的 getter 和 setter 来获取这个值,但不能直接使用 `number`。有关 `private` 的更多信息,请参考 [访问控制](./26_Access_Control.md)
通过在属性之前写上包装器名称作为特性的方式,你可以把一个包装器应用到一个属性上去。这里有个存储小矩形的结构体。通过 `TwelveOrLess` 属性包装器实现类似(挺随意的)对“小”的定义。
```swift
struct SmallRectangle {
@TwelveOrLess var height: Int
@TwelveOrLess var width: Int
}
var rectangle = SmallRectangle()
print(rectangle.height)
// 打印 "0"
rectangle.height = 10
print(rectangle.height)
// 打印 "10"
rectangle.height = 24
print(rectangle.height)
// 打印 "12"
```
`height``width` 属性从 `TwelveOrLess` 的定义中获取它们的初始值。该定义把 `TwelveOrLess.number` 设置为 0。把数字 10 存进 `rectangle.height` 中的操作能成功,是因为数字 10 很小。尝试存储 24 的操作实际上存储的值为 12这是因为对于这个属性的 setter 的规则来说24 太大了。
当你把一个包装器应用到一个属性上时,编译器将合成提供包装器存储空间和通过包装器访问属性的代码。(属性包装器只负责存储被包装值,所以没有合成这些代码。)不利用这个特性语法的情况下,你可以写出使用属性包装器行为的代码。举例来说,这是先前代码清单中的 `SmallRectangle` 的另一个版本。这个版本将其属性明确地包装在 `TwelveOrLess` 结构体中,而不是把 `@TwelveOrLess` 作为特性写下来:
```swift
struct SmallRectangle {
private var _height = TwelveOrLess()
private var _width = TwelveOrLess()
var height: Int {
get { return _height.wrappedValue }
set { _height.wrappedValue = newValue }
}
var width: Int {
get { return _width.wrappedValue }
set { _width.wrappedValue = newValue }
}
}
```
`_height``_width` 属性存着这个属性包装器的一个实例,即 `TwelveOrLess``height``width` 的 getter 和 setter 把对 `wrappedValue` 属性的访问包装起来。
### 设置被包装属性的初始值 {#setting-initial-values-for-wrapped-properties}
上面例子中的代码通过在 `TwelveOrLess` 的定义中赋予 `number` 一个初始值来设置被包装属性的初始值。使用这个属性包装器的代码没法为被 `TwelveOrLess` 包装的属性指定其他初始值。举例来说,`SmallRectangle` 的定义没法给 `height` 或者 `width` 一个初始值。为了支持设定一个初始值或者其他自定义操作,属性包装器需要添加一个构造器。这是 `TwelveOrLess` 的扩展版本,称为 `SmallNumber``SmallNumber` 定义了能设置被包装值和最大值的构造器:
```swift
@propertyWrapper
struct SmallNumber {
private var maximum: Int
private var number: Int
var wrappedValue: Int {
get { return number }
set { number = min(newValue, maximum) }
}
init() {
maximum = 12
number = 0
}
init(wrappedValue: Int) {
maximum = 12
number = min(wrappedValue, maximum)
}
init(wrappedValue: Int, maximum: Int) {
self.maximum = maximum
number = min(wrappedValue, maximum)
}
}
```
`SmallNumber` 的定义包括三个构造器——`init()``init(wrappedValue:)``init(wrappedValue:maximum:)`——下面的示例使用这三个构造器来设置被包装值和最大值。有关构造过程和构造器语法的更多信息,请参考 [构造过程](./14_Initialization.md)。
当你把包装器应用于属性且没有设定初始值时Swift 使用 `init()` 构造器来设置包装器。举个例子:
```swift
struct ZeroRectangle {
@SmallNumber var height: Int
@SmallNumber var width: Int
}
var zeroRectangle = ZeroRectangle()
print(zeroRectangle.height, zeroRectangle.width)
// 打印 "0 0"
```
调用 `SmallNumber()` 来创建包装 `height``width``SmallNumber` 的实例。构造器内部的代码使用默认值 0 和 12 设置初始的被包装值和初始的最大值。像之前使用在 `SmallRectangle` 中使用 `TwelveOrLess` 的例子,这个属性包装器仍然提供所有的初始值。与这个例子不同的是,`SmallNumber` 也支持把编写这些初始值作为声明属性的一部分。
当你为属性指定初始值时Swift 使用 `init(wrappedValue:)` 构造器来设置包装器。举个例子:
```swift
struct UnitRectangle {
@SmallNumber var height: Int = 1
@SmallNumber var width: Int = 1
}
var unitRectangle = UnitRectangle()
print(unitRectangle.height, unitRectangle.width)
// 打印 "1 1"
```
当你对一个被包装的属性写下 `= 1` 时,这被转换为调用 `init(wrappedValue:)` 构造器。调用 `SmallNumber(wrappedValue: 1)`来创建包装 `height``width``SmallNumber` 的实例。构造器使用此处指定的被包装值,且使用的默认最大值为 12。
当你在自定义特性后面把实参写在括号里时Swift 使用接受这些实参的构造器来设置包装器。举例来说如果你提供初始值和最大值Swift 使用 `init(wrappedValue:maximum:)` 构造器:
```swift
struct NarrowRectangle {
@SmallNumber(wrappedValue: 2, maximum: 5) var height: Int
@SmallNumber(wrappedValue: 3, maximum: 4) var width: Int
}
var narrowRectangle = NarrowRectangle()
print(narrowRectangle.height, narrowRectangle.width)
// 打印 "2 3"
narrowRectangle.height = 100
narrowRectangle.width = 100
print(narrowRectangle.height, narrowRectangle.width)
// 打印 "5 4"
```
调用 `SmallNumber(wrappedValue: 2, maximum: 5)` 来创建包装 `height``SmallNumber` 的一个实例。调用 `SmallNumber(wrappedValue: 3, maximum: 4)` 来创建包装 `width``SmallNumber` 的一个实例。
通过将实参包含到属性包装器中,你可以设置包装器的初始状态,或者在创建包装器时传递其他的选项。这种语法是使用属性包装器最通用的方法。你可以为这个属性提供任何所需的实参,且它们将被传递给构造器。
当包含属性包装器实参时你也可以使用赋值来指定初始值。Swift 将赋值视为 `wrappedValue` 参数,且使用接受被包含的实参的构造器。举个例子:
```swift
struct MixedRectangle {
@SmallNumber var height: Int = 1
@SmallNumber(maximum: 9) var width: Int = 2
}
var mixedRectangle = MixedRectangle()
print(mixedRectangle.height)
// 打印 "1"
mixedRectangle.height = 20
print(mixedRectangle.height)
// 打印 "12"
```
调用 `SmallNumber(wrappedValue: 1)` 来创建包装 `height``SmallNumber` 的一个实例,这个实例使用默认最大值 12。调用 `SmallNumber(wrappedValue: 2, maximum: 9)` 来创建包装 `width``SmallNumber` 的一个实例。
### 从属性包装器中呈现一个值 {#projecting-a-value-from-a-property-wrapper}
除了被包装值,属性包装器可以通过定义被呈现值暴露出其他功能。举个例子,管理对数据库的访问的属性包装器可以在它的被呈现值上暴露出 `flushDatabaseConnection()` 方法。除了以货币符号(\$)开头,被呈现值的名称和被包装值是一样的。因为你的代码不能够定义以 $ 开头的属性,所以被呈现值永远不会与你定义的属性有冲突。
在之前 `SmallNumber` 的例子中,如果你尝试把这个属性设置为一个很大的数值,属性包装器会在存储这个数值之前调整这个数值。以下的代码把被呈现值添加到 `SmallNumber` 结构体中来追踪在存储新值之前属性包装器是否为这个属性调整了新值。
```swift
@propertyWrapper
struct SmallNumber {
private var number: Int
var projectedValue: Bool
init() {
self.number = 0
self.projectedValue = false
}
var wrappedValue: Int {
get { return number }
set {
if newValue > 12 {
number = 12
projectedValue = true
} else {
number = newValue
projectedValue = false
}
}
}
}
struct SomeStructure {
@SmallNumber var someNumber: Int
}
var someStructure = SomeStructure()
someStructure.someNumber = 4
print(someStructure.$someNumber)
// 打印 "false"
someStructure.someNumber = 55
print(someStructure.$someNumber)
// 打印 "true"
```
写下 `someStructure.$someNumber` 即可访问包装器的被呈现值。在存储一个比较小的数值时,如 4 `someStructure.$someNumber` 的值为 `false`。但是,在尝试存储一个较大的数值时,如 55 ,被呈现值变为 `true`
属性包装器可以返回任何类型的值作为它的被呈现值。在这个例子里,属性包装器要暴露的信息是:那个数值是否被调整过,所以它暴露出布尔型值来作为它的被呈现值。需要暴露出更多信息的包装器可以返回其他数据类型的实例,或者可以返回自身来暴露出包装器的实例,并把其作为它的被呈现值。
当从类型的一部分代码中访问被呈现值,例如属性 getter 或实例方法,你可以在属性名称之前省略 `self.`,就像访问其他属性一样。以下示例中的代码用 `$height``$width` 引用包装器 `height``width` 的被呈现值:
```swift
enum Size {
case small, large
}
struct SizedRectangle {
@SmallNumber var height: Int
@SmallNumber var width: Int
mutating func resize(to size: Size) -> Bool {
switch size {
case .small:
height = 10
width = 20
case .large:
height = 100
width = 100
}
return $height || $width
}
}
```
因为属性包装器语法只是具有 getter 和 setter 的属性的语法糖,所以访问 `height``width` 的行为与访问任何其他属性的行为相同。举个例子,`resize(to:)` 中的代码使用它们的属性包装器来访问 `height``width`。如果调用 `resize(to: .large)``.large` 的 switch case 分支语句把矩形的高度和宽度设置为 100。属性包装器防止这些属性的值大于 12且把被呈现值设置成为 `true` 来记下它调整过这些值的事实。在 `resize(to:)` 的最后,返回语句检查 `$height``$width` 来确认是否属性包装器调整过 `height``width`
## 全局变量和局部变量 {#global-and-local-variables}
@ -268,7 +536,7 @@ stepCounter.totalSteps = 896
> 注意
>
> 全局的常量或变量都是延迟计算的,跟[延时加载存储属性](#lazy_stored_properties)相似,不同的地方在于,全局的常量或变量不需要标记 `lazy` 修饰符。
> 全局的常量或变量都是延迟计算的,跟 [延时加载存储属性](#lazy-stored-properties) 相似,不同的地方在于,全局的常量或变量不需要标记 `lazy` 修饰符。
>
> 局部范围的常量和变量从不延迟计算。

View File

@ -6,7 +6,7 @@
## 实例方法Instance Methods {#instance-methods}
*实例方法*是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见[函数](./06_Functions.md)。
*实例方法*是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见 [函数](./06_Functions.md)。
实例方法要写在它所属的类型的前后大括号之间。实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的某个特定实例调用。实例方法不能脱离于现存的实例而被调用。
@ -47,7 +47,7 @@ counter.reset()
// 计数值现在是0
```
函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见[指定外部参数名](./06_Functions.md#specifying_external_parameter_names)。方法参数也一样,因为方法就是函数,只是这个函数与某个类型相关联了。
函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见 [指定外部参数名](./06_Functions.md#specifying-external-parameter-names)。方法参数也一样,因为方法就是函数,只是这个函数与某个类型相关联了。
### self 属性 {#the-self-property}
@ -107,7 +107,7 @@ print("The point is now at (\(somePoint.x), \(somePoint.y))")
上面的 `Point` 结构体定义了一个可变方法 `moveByxy :)` 来移动 `Point` 实例到给定的位置。该方法被调用时修改了这个点,而不是返回一个新的点。方法定义时加上了 `mutating` 关键字,从而允许修改属性。
注意不能在结构体类型的常量a constant of structure type上调用可变方法因为其属性不能被改变即使属性是变量属性详情参见[常量结构体的存储属性](./10_Properties.md#stored_properties_of_constant_structure_instances)
注意不能在结构体类型的常量a constant of structure type上调用可变方法因为其属性不能被改变即使属性是变量属性详情参见 [常量结构体的存储属性](./10_Properties.md#stored-properties-of-constant-structure-instances)
```swift
let fixedPoint = Point(x: 3.0, y: 3.0)
@ -157,7 +157,7 @@ ovenLight.next()
## 类型方法 {#type-methods}
实例方法是被某个类型的实例调用的方法。你也可以定义在类型本身上调用的方法,这种方法就叫做*类型方法*。在方法的 `func` 关键字之前加上关键字 `static`,来指定类型方法。类还可以用关键字 `class` 来允许子类重写父类方法实现。
实例方法是被某个类型的实例调用的方法。你也可以定义在类型本身上调用的方法,这种方法就叫做*类型方法*。在方法的 `func` 关键字之前加上关键字 `static`,来指定类型方法。类还可以用关键字 `class`指定,从而允许子类重写父类方法实现。
> 注意
>
@ -213,7 +213,7 @@ struct LevelTracker {
除了类型属性和类型方法,`LevelTracker` 还监测每个玩家的进度。它用实例属性 `currentLevel` 来监测每个玩家当前的等级。
为了便于管理 `currentLevel` 属性,`LevelTracker` 定义了实例方法 `advance(to:)`。这个方法会在更新 `currentLevel` 之前检查所请求的新等级是否已经解锁。`advance(to:)` 方法返回布尔值以指示是否能够设置 `currentLevel`。因为允许在调用 `advance(to:)` 时候忽略返回值,不会产生编译警告,所以函数被标注为 `@discardableResult` 属性,更多关于属性信息,请参考[特性](../chapter3/07_Attributes.html)章节。
为了便于管理 `currentLevel` 属性,`LevelTracker` 定义了实例方法 `advance(to:)`。这个方法会在更新 `currentLevel` 之前检查所请求的新等级是否已经解锁。`advance(to:)` 方法返回布尔值以指示是否能够设置 `currentLevel`。因为允许在调用 `advance(to:)` 时候忽略返回值,不会产生编译警告,所以函数被标注为 `@discardableResult` 属性,更多关于属性信息,请参考 [特性](../03_language_reference/07_Attributes.md) 章节。
下面,`Player` 类使用 `LevelTracker` 来监测和更新每个玩家的发展进度:

View File

@ -2,11 +2,11 @@
*下标*可以定义在类、结构体和枚举中,是访问集合、列表或序列中元素的快捷方式。可以使用下标的索引,设置和获取值,而不需要再调用对应的存取方法。举例来说,用下标访问一个 `Array` 实例中的元素可以写作 `someArray[index]`,访问 `Dictionary` 实例中的元素可以写作 `someDictionary[key]`
一个类型可以定义多个下标,通过不同索引类型进行重载。下标不限于一维,你可以定义具有多个入参的下标满足自定义类型的需求。
一个类型可以定义多个下标,通过不同索引类型进行对应的重载。下标不限于一维,你可以定义具有多个入参的下标满足自定义类型的需求。
## 下标语法 {#subscript-syntax}
下标允许你通过在实例名称后面的方括号中传入一个或者多个索引值来对实例进行存取。语法类似于实例方法语法和计算型属性语法的混合。与定义实例方法类似,定义下标使用 `subscript` 关键字,指定一个或多个输入参数和返回类型与实例方法不同的是,下标可以设定为读写或只读。这种行为由 getter 和 setter 实现,有点类似计算型属性:
下标允许你通过在实例名称后面的方括号中传入一个或者多个索引值来对实例进行查询。它的语法类似于实例方法语法和计算型属性语法定义下标使用 `subscript` 关键字,与定义实例方法类似,都是指定一个或多个输入参数和一个返回类型与实例方法不同的是,下标可以设定为读写或只读。这种行为由 getter 和 setter 实现,类似计算型属性:
```swift
subscript(index: Int) -> Int {
@ -19,9 +19,9 @@ subscript(index: Int) -> Int {
}
```
`newValue` 的类型和下标的返回类型相同。如同计算型属性,可以不指定 setter 的参数(`newValue`。如果不指定参数setter 会提供一个名为 `newValue` 的默认参数。
`newValue` 的类型和下标操作的返回类型相同。如同计算型属性,可以不指定 setter 的参数(`newValue`。如果不指定参数setter 会提供一个名为 `newValue` 的默认参数。
如同只读计算型属性,可以省略只读下标的 `get` 关键字:
如同只读计算型属性,对于只读下标的声明,你可以通过省略 `get` 关键字和对应的大括号组来进行简写
```swift
subscript(index: Int) -> Int {
@ -29,7 +29,7 @@ subscript(index: Int) -> Int {
}
```
下面代码演示了只读下标的实现,这里定义了一个 `TimesTable` 结构体,用来表示传入整数的乘法表:
下面代码演示了只读下标的实现,这里定义了一个 `TimesTable` 结构体,用来表示对应整数的乘法表:
```swift
struct TimesTable {
@ -45,7 +45,7 @@ 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`
> 注意
>
@ -53,9 +53,9 @@ print("six times three is \(threeTimesTable[6])")
## 下标用法 {#subscript-usage}
下标的确切含义取决于使用场景。下标通常作为访问集合,列表或序列中元素的快捷方式。你可以针对自己特定的类或结构体功能来自由地以最恰当的方式实现下标。
下标的确切含义取决于使用场景。下标通常作为访问集合,列表或序列中元素的快捷方式。你可以针对自己特定的类或结构体功能来以最恰当的方式实现下标。
例如Swift 的 `Dictionary` 类型实现下标用于对实例中储存的值进行存取操作。为字典设值时,在下标中使用和字典的键类型相同的键,并把一个和字典的值类型相同的值赋给这个下标:
例如Swift 的 `Dictionary` 类型实现下标用于对实例中储存的值进行存取操作。为字典设值时,在下标中使用和字典的键类型相同的键,并把一个和字典的值类型相同的值赋给这个下标:
```swift
var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
@ -64,17 +64,19 @@ numberOfLegs["bird"] = 2
上例定义一个名为 `numberOfLegs` 的变量,并用一个包含三对键值的字典字面量初始化它。`numberOfLegs` 字典的类型被推断为 `[String: Int]`。字典创建完成后,该例子通过下标将 `String` 类型的键 `bird``Int` 类型的值 `2` 添加到字典中。
更多关于 `Dictionary` 下标的信息请参考 [读取和修改字典](./04_Collection_Types.md#accessing_and_modifying_a_dictionary)。
更多关于 `Dictionary` 下标的信息请参考 [读取和修改字典](./04_Collection_Types.md#accessing-and-modifying-a-dictionary)。
> 注意
>
> Swift 的 `Dictionary` 类型的下标接受并返回可选类型的值。上例中的 `numberOfLegs` 字典通过下标返回的是一个 `Int?` 或者说“可选的 int”。`Dictionary` 类型之所以如此实现下标,是因为不是每个键都有对应的值,同时这也提供了一种通过键删除对应值的方式,只需将键对应的值赋值为 `nil` 即可。
> Swift 的 `Dictionary` 类型的下标接受并返回_可选_类型的值。上例中的 `numberOfLegs` 字典通过下标返回的是一个 `Int?` 或者说“可选的 int”。`Dictionary` 类型之所以如此实现下标,是因为不是每个键都有对应的值,同时这也提供了一种通过键删除对应值的方式,只需将键对应的值赋值为 `nil` 即可。
## 下标选项 {#subscript-options}
下标可以接受任意数量的入参,并且这些入参可以是任类型。下标的返回值也可以是任意类型。下标可以使用变量参数和可变参数,但不能使用输入输出参数,也不能给参数设置默认值。
下标可以接受任意数量的入参,并且这些入参可以是任类型。下标的返回值也可以是任意类型。
一个类或结构体可以根据自身需要提供多个下标实现,使用下标时将通过入参的数量和类型进行区分,自动匹配合适的下标,这就是*下标的重载*
与函数一样,下标可以接受不同数量的参数,并且为这些参数提供默认值,如在[可变参数](./06_Functions.md#variadic-parameters) 和 [默认参数值](./06_Functions.md#default-parameter-values) 中所述。但是,与函数不同的是,下标不能使用 in-out 参数
一个类或结构体可以根据自身需要提供多个下标实现,使用下标时将通过入参的数量和类型进行区分,自动匹配合适的下标。它通常被称为*下标的重载*。
虽然接受单一入参的下标是最常见的,但也可以根据情况定义接受多个入参的下标。例如下例定义了一个 `Matrix` 结构体,用于表示一个 `Double` 类型的二维矩阵。`Matrix` 结构体的下标接受两个整型参数:
@ -103,15 +105,15 @@ struct Matrix {
}
```
`Matrix` 提供了一个接受两个入参的构造方法,入参分别是 `rows``columns`,创建了一个足够容纳 `rows * columns``Double` 类型的值的数组。通过传入数组长度和初始值 `0.0` 到数组的构造器,将矩阵中每个位置的值初始化为 `0.0`。关于数组的这种构造方法请参考 [创建一个带有默认值的数组](./04_Collection_Types.md#creating_an_array_with_a_default_value)。
`Matrix` 提供了一个接受两个入参的构造方法,入参分别是 `rows``columns`,创建了一个足够容纳 `rows * columns``Double` 类型的值的数组。通过传入数组长度和初始值 `0.0` 到数组的构造器,将矩阵中每个位置的值初始化为 `0.0`。关于数组的这种构造方法请参考 [创建一个带有默认值的数组](./04_Collection_Types.md#creating-an-array-with-a-default-value)。
你可以通过传入合适的 `row``column` 的数量来构造一个新的 `Matrix` 实例:
你可以通过传入合适的 `row``column` 数值来构造一个新的 `Matrix` 实例:
```swift
var matrix = Matrix(rows: 2, columns: 2)
```
上例中创建了一个 `Matrix` 实例来表示两行两列的矩阵。该 `Matrix` 实例的 `grid` 数组按照从左上到右下的阅读顺序将矩阵扁平化存储:
上例中创建了一个两行两列的 `Matrix` 实例。该 `Matrix` 实例的 `grid` 数组按照从左上到右下的阅读顺序将矩阵扁平化存储:
![](https://docs.swift.org/swift-book/_images/subscriptMatrix01_2x.png)
@ -140,3 +142,17 @@ func indexIsValid(row: Int, column: Int) -> Bool {
let someValue = matrix[2, 2]
// 断言将会触发,因为 [2, 2] 已经超过了 matrix 的范围
```
## 类型下标{#type-subscripts}
正如上节所述实例下标是在特定类型的一个实例上调用的下标。你也可以定义一种在这个类型自身上调用的下标。这种下标被称作_类型下标_。你可以通过在 `subscript` 关键字之前写下 `static` 关键字的方式来表示一个类型下标。类类型可以使用 `class` 关键字来代替 `static`,它允许子类重写父类中对那个下标的实现。下面的例子展示了如何定义和调用一个类型下标:
```
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
static subscript(n: Int) -> Planet {
return Planet(rawValue: n)!
}
}
let mars = Planet[4]
print(mars)
```

View File

@ -57,7 +57,7 @@ class SomeClass: SomeSuperclass {
}
```
下一个例子,定义了一个叫 `Bicycle` 的子类,继承自`Vehicle`
下一个例子,定义了一个叫 `Bicycle` 的子类,继承自`Vehicle`
```swift
class Bicycle: Vehicle {
@ -111,7 +111,7 @@ print("Tandem: \(tandem.description)")
如果要重写某个特性,你需要在重写定义的前面加上 `override` 关键字。这么做,就表明了你是想提供一个重写版本,而非错误地提供了一个相同的定义。意外的重写行为可能会导致不可预知的错误,任何缺少 `override` 关键字的重写都会在编译时被认定为错误。
`override` 关键字会提醒 Swift 编译器去检查该类的超类(或其中一个类)是否有匹配重写版本的声明。这个检查可以确保你的重写定义是正确的。
`override` 关键字会提醒 Swift 编译器去检查该类的超类(或其中一个类)是否有匹配重写版本的声明。这个检查可以确保你的重写定义是正确的。
### 访问超类的方法,属性及下标 {#accessing-superclass-methods-properties-and-subscripts}
@ -184,7 +184,7 @@ print("Car: \(car.description)")
#### 重写属性观察器 {#overriding-property-observers}
你可以通过重写属性为一个继承来的属性添加属性观察器。这样一来,无论被继承属性原本是如何实现的,当其属性值发生改变时,你就会被通知到。关于属性观察器的更多内容,请看[属性观察器](../chapter2/10_Properties.html#property_observers)。
你可以通过重写属性为一个继承来的属性添加属性观察器。这样一来,无论被继承属性原本是如何实现的,当其属性值发生改变时,你就会被通知到。关于属性观察器的更多内容,请看 [属性观察器](../02_language_guide/10_Properties.md#property-observers)。
> 注意
>

View File

@ -4,7 +4,7 @@
你要通过定义*构造器*来实现构造过程,它就像用来创建特定类型新实例的特殊方法。与 Objective-C 中的构造器不同Swift 的构造器没有返回值。它们的主要任务是保证某种类型的新实例在第一次使用前完成正确的初始化。
类的实例也可以通过实现*析构器*来执行它释放之前自定义的清理工作。想了解更多关于析构器的内容,请参[析构过程](./15_Deinitialization.md)。
类的实例也可以通过实现*析构器*来执行它释放之前自定义的清理工作。想了解更多关于析构器的内容,请参[析构过程](./15_Deinitialization.md)。
## 存储属性的初始赋值 {#setting-initial-values-for-stored-properties}
@ -14,7 +14,7 @@
> 注意
>
> 当你为存储型属性分配默认值或者在构造器中设置初始值时,它们的值是被直接设置的,不会触发任何属性观察者。
> 当你为存储型属性分配默认值或者在构造器中设置初始值时,它们的值是被直接设置的,不会触发任何属性观察者。
### 构造器 {#initializers}
@ -131,7 +131,7 @@ let veryGreen = Color(0.0, 1.0, 0.0)
如果你不希望构造器的某个形参使用实参标签,可以使用下划线(`_`)来代替显式的实参标签来重写默认行为。
下面是之前[形参的构造过程](#initialization_parameters)中 `Celsius` 例子的扩展,多了一个用已经的摄氏表示的 `Double` 类型值来创建新的 `Celsius` 实例的额外构造器:
下面是之前 [形参的构造过程](#initialization-parameters) `Celsius` 例子的扩展,多了一个用已经的摄氏表示的 `Double` 类型值来创建新的 `Celsius` 实例的额外构造器:
```swift
struct Celsius {
@ -240,11 +240,25 @@ struct Size {
let twoByTwo = Size(width: 2.0, height: 2.0)
```
当你调用一个逐一成员构造器memberwise initializer可以省略任何一个有默认值的属性。在上面这个例子中`Size` 结构体的 `height``width` 属性各有一个默认值。你可以省略两者或两者之一,对于被省略的属性,构造器会使用默认值。举个例子:
```
let zeroByTwo = Size(height: 2.0)
print(zeroByTwo.width, zeroByTwo.height)
// 打印 "0.0 2.0"
let zeroByZero = Size()
print(zeroByZero.width, zeroByZero.height)
// 打印 "0.0 0.0"
```
## 值类型的构造器代理 {#initializer-delegation-for-value-types}
构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为*构造器代理*,它能避免多个构造器间的代码重复。
构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给自己的其它构造器。类则不同,它可以继承自其它类(请参考[继承](./13_Inheritance.md))。这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。这些责任将在后续章节[类的继承和构造过程](#class_inheritance_and_initialization)中介绍。
构造器代理的实现规则和形式在值类型和类类型中有所不同。值类型(结构体和枚举类型)不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给自己的其它构造器。类则不同,它可以继承自其它类(请参考 [继承](./13_Inheritance.md))。这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。这些责任将在后续章节 [类的继承和构造过程](#class-inheritance-and-initialization) 中介绍。
对于值类型,你可以使用 `self.init` 在自定义的构造器中引用相同类型中的其它构造器。并且你只能在构造器内部调用 `self.init`
@ -252,7 +266,7 @@ let twoByTwo = Size(width: 2.0, height: 2.0)
> 注意
>
> 假如你希望默认构造器、逐一成员构造器以及你自己的自定义构造器都能用来创建实例,可以将自定义的构造器写到扩展(`extension`)中,而不是写在值类型的原始定义中。想查看更多内容,请查看[扩展](./20_Extensions.md)章节。
> 假如你希望默认构造器、逐一成员构造器以及你自己的自定义构造器都能用来创建实例,可以将自定义的构造器写到扩展(`extension`)中,而不是写在值类型的原始定义中。想查看更多内容,请查看 [扩展](./20_Extensions.md) 章节。
下面例子定义一个自定义结构体 `Rect`,用来代表几何矩形。这个例子需要两个辅助的结构体 `Size``Point`,它们各自为其所有的属性提供了默认初始值 `0.0`
@ -314,7 +328,7 @@ let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
> 注意
>
> 如果你想用另外一种不需要自己定义 `init()``init(origin:size:)` 的方式来实现这个例子,请参考[扩展](./21_Extensions.md)。
> 如果你想用另外一种不需要自己定义 `init()``init(origin:size:)` 的方式来实现这个例子,请参考 [扩展](./20_Extensions.md)。
## 类的继承和构造过程 {#class-inheritance-and-initialization}
@ -328,7 +342,7 @@ Swift 为类类型提供了两种构造器来确保实例中所有存储型属
类倾向于拥有极少的指定构造器,普遍的是一个类只拥有一个指定构造器。指定构造器像一个个“漏斗”放在构造过程发生的地方,让构造过程沿着父类链继续往上进行。
每一个类都必须至少拥有一个指定构造器。在某些情况下,许多类通过继承了父类中的指定构造器而满足了这个条件。具体内容请参考后续章节[构造器的自动继承](#automatic_initializer_inheritance)。
每一个类都必须至少拥有一个指定构造器。在某些情况下,许多类通过继承了父类中的指定构造器而满足了这个条件。具体内容请参考后续章节 [构造器的自动继承](#automatic-initializer-inheritance)。
*便利构造器*是类中比较次要的、辅助型的构造器。你可以定义便利构造器来调用同一个类中的指定构造器,并为部分形参提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入值的实例。
@ -465,11 +479,11 @@ Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造
> 注意
>
> 父类的构造器仅会在安全和适当的某些情况下被继承。具体内容请参考后续章节[构造器的自动继承](#automatic_initializer_inheritance)。
> 父类的构造器仅会在安全和适当的某些情况下被继承。具体内容请参考后续章节 [构造器的自动继承](#automatic-initializer-inheritance)。
假如你希望自定义的子类中能提供一个或多个跟父类相同的构造器,你可以在子类中提供这些构造器的自定义实现。
当你在编写一个和父类中指定构造器相匹配的子类构造器时,你实际上是在重写父类的这个指定构造器。因此,你必须在定义子类构造器时带上 `override` 修饰符。即使你重写的是系统自动提供的默认构造器,也需要带上 `override` 修饰符,具体内容请参考[默认构造器](#default_initializers)。
当你在编写一个和父类中指定构造器相匹配的子类构造器时,你实际上是在重写父类的这个指定构造器。因此,你必须在定义子类构造器时带上 `override` 修饰符。即使你重写的是系统自动提供的默认构造器,也需要带上 `override` 修饰符,具体内容请参考 [默认构造器](#default-initializers)。
正如重写属性,方法或者是下标,`override` 修饰符会让编译器去检查父类中是否有相匹配的指定构造器,并验证构造器参数是否被按预想中被指定。
@ -477,7 +491,7 @@ Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造
>
> 当你重写一个父类的指定构造器时,你总是需要写 `override` 修饰符,即使是为了实现子类的便利构造器。
相反,如果你编写了一个和父类便利构造器相匹配的子类构造器,由于子类不能直接调用父类的便利构造器(每个规则都在上文[类的构造器代理规则](#initializer_delegation_for_class_types)有所描述),因此,严格意义上来讲,你的子类并未对一个父类构造器提供重写。最后的结果就是,你在子类中“重写”一个父类便利构造器时,不需要加 `override` 修饰符。
相反,如果你编写了一个和父类便利构造器相匹配的子类构造器,由于子类不能直接调用父类的便利构造器(每个规则都在上文 [类的构造器代理规则](#initializer-delegation-for-class-types) 有所描述),因此,严格意义上来讲,你的子类并未对一个父类构造器提供重写。最后的结果就是,你在子类中“重写”一个父类便利构造器时,不需要加 `override` 修饰符。
在下面的例子中定义了一个叫 `Vehicle` 的基类。基类中声明了一个存储型属性 `numberOfWheels`,它是默认值为 `Int` 类型的 `0``numberOfWheels` 属性用在一个描述车辆特征 `String` 类型为 `descrpiption` 的计算型属性中:
@ -490,7 +504,7 @@ class Vehicle {
}
```
`Vehicle` 类只为存储型属性提供默认值,也没有提供自定义构造器。因此,它会自动获得一个默认构造器,具体内容请参考[默认构造器](#default_initializers)。默认构造器(如果有的话)总是类中的指定构造器,可以用于创建 `numberOfWheels``0``Vehicle` 实例:
`Vehicle` 类只为存储型属性提供默认值,也没有提供自定义构造器。因此,它会自动获得一个默认构造器,具体内容请参考 [默认构造器](#default-initializers)。默认构造器(如果有的话)总是类中的指定构造器,可以用于创建 `numberOfWheels``0``Vehicle` 实例:
```swift
let vehicle = Vehicle()
@ -521,7 +535,7 @@ print("Bicycle: \(bicycle.description)")
// 打印“Bicycle: 2 wheel(s)”
```
如果类的构造器没有在阶段 2 过程中做自定义操作,并且父类有一个无参数的自定义构造器你可以在所有类的存储属性赋值之后省略 `super.init()` 的调用。
如果类的构造器没有在阶段 2 过程中做自定义操作,并且父类有一个无参数的指定构造器你可以在所有类的存储属性赋值之后省略 `super.init()` 的调用。
这个例子定义了另一个 `Vehicle` 的子类 `Hoverboard` ,只设置它的 `color` 属性。这个构造器依赖隐式调用父类的构造器来完成,而不是显示调用 `super.init()`
@ -628,11 +642,11 @@ class RecipeIngredient: Food {
![RecipeIngredient 构造器](https://docs.swift.org/swift-book/_images/initializersExample02_2x.png)
`RecipeIngredient` 类拥有一个指定构造器 `init(name: String, quantity: Int)`,它可以用来填充 `RecipeIngredient` 实例的所有属性值。这个构造器一开始先将传入的 `quantity` 实参赋值给 `quantity` 属性,这个属性也是唯一在 `RecipeIngredient` 中新引入的属性。随后,构造器向上代理到父类 `Food``init(name: String)`。这个过程满足[两段式构造过程](#two_phase_initialization)中的安全检查 1。
`RecipeIngredient` 类拥有一个指定构造器 `init(name: String, quantity: Int)`,它可以用来填充 `RecipeIngredient` 实例的所有属性值。这个构造器一开始先将传入的 `quantity` 实参赋值给 `quantity` 属性,这个属性也是唯一在 `RecipeIngredient` 中新引入的属性。随后,构造器向上代理到父类 `Food``init(name: String)`。这个过程满足 [两段式构造过程](#two-phase-initialization) 中的安全检查 1。
`RecipeIngredient` 也定义了一个便利构造器 `init(name: String)`,它只通过 `name` 来创建 `RecipeIngredient` 的实例。这个便利构造器假设任意 `RecipeIngredient` 实例的 `quantity``1`,所以不需要显式的质量即可创建出实例。这个便利构造器的定义可以更加方便和快捷地创建实例,并且避免了创建多个 `quantity``1``RecipeIngredient` 实例时的代码重复。这个便利构造器只是简单地横向代理到类中的指定构造器,并为 `quantity` 参数传递 `1`
`RecipeIngredient` 的便利构造器 `init(name: String)` 使用了跟 `Food` 中指定构造器 `init(name: String)` 相同的形参。由于这个便利构造器重写了父类的指定构造器 `init(name: String)`,因此必须在前面使用 `override` 修饰符(参见[构造器的继承和重写](#initializer_inheritance_and_overriding))。
`RecipeIngredient` 的便利构造器 `init(name: String)` 使用了跟 `Food` 中指定构造器 `init(name: String)` 相同的形参。由于这个便利构造器重写了父类的指定构造器 `init(name: String)`,因此必须在前面使用 `override` 修饰符(参见 [构造器的继承和重写](#initializer-inheritance-and-overriding))。
尽管 `RecipeIngredient` 将父类的指定构造器重写为了便利构造器,但是它依然提供了父类的所有指定构造器的实现。因此,`RecipeIngredient` 会自动继承父类的所有便利构造器。
@ -1022,7 +1036,7 @@ class SomeClass {
```swift
struct Chessboard {
let boardColors: [Bool] = {
var temporaryBoard = [Bool]()
var temporaryBoard: [Bool] = []
var isBlack = false
for i in 1...8 {
for j in 1...8 {

View File

@ -4,7 +4,7 @@
## 析构过程原理 {#how-deinitialization-works}
Swift 会自动释放不再需要的实例以释放资源。如[自动引用计数](./23_Automatic_Reference_Counting.md)章节中所讲述Swift 通过*自动引用计数ARC)* 处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前手动去关闭该文件。
Swift 会自动释放不再需要的实例以释放资源。如 [自动引用计数](./24_Automatic_Reference_Counting.md) 章节中所讲述Swift 通过*自动引用计数ARC)* 处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前手动去关闭该文件。
在类的定义中,每个类最多只能有一个析构器,而且析构器不带任何参数和圆括号,如下所示:

View File

@ -6,15 +6,15 @@
>
> Swift 的可选链式调用和 Objective-C 中向 `nil` 发送消息有些相像,但是 Swift 的可选链式调用可以应用于任意类型,并且能检查调用是否成功。
## 使用可选链式调用代替强制展开 {#optional-chaining-as-an-alternative-to-forced-unwrapping}
## 使用可选链式调用代替强制解包 {#optional-chaining-as-an-alternative-to-forced-unwrapping}
通过在想调用的属性、方法,或下标的可选值后面放一个问号(`?`),可以定义一个可选链。这一点很像在可选值后面放一个叹号(`!`)来强制展开它的值。它们的主要区别在于当可选值为空时可选链式调用只会调用失败,然而强制展开将会触发运行时错误。
通过在想调用的属性、方法,或下标的可选值后面放一个问号(`?`),可以定义一个可选链。这一点很像在可选值后面放一个叹号(`!`)来强制解包它的值。它们的主要区别在于当可选值为空时可选链式调用只会调用失败,然而强制解包将会触发运行时错误。
为了反映可选链式调用可以在空值(`nil`)上调用的事实,不论这个调用的属性、方法及下标返回的值是不是可选值,它的返回结果都是一个可选值。你可以利用这个返回值来判断你的可选链式调用是否调用成功,如果调用有返回值则说明调用成功,返回 `nil` 则说明调用失败。
这里需要特别指出,可选链式调用的返回结果与原本的返回结果具有相同的类型,但是被包装成了一个可选值。例如,使用可选链式调用访问属性,当可选链式调用成功时,如果属性原本的返回结果是 `Int` 类型,则会变为 `Int?` 类型。
下面几段代码将解释可选链式调用和强制展开的不同。
下面几段代码将解释可选链式调用和强制解包的不同。
首先定义两个类 `Person``Residence`
@ -36,7 +36,7 @@ class Residence {
let john = Person()
```
如果使用叹号(`!`)强制展开获得这个 `john``residence` 属性中的 `numberOfRooms` 值,会触发运行时错误,因为这时 `residence` 没有可以展开的值:
如果使用叹号(`!`)强制解包获得这个 `john``residence` 属性中的 `numberOfRooms` 值,会触发运行时错误,因为这时 `residence` 没有可以解包的值:
```swift
let roomCount = john.residence!.numberOfRooms
@ -58,7 +58,7 @@ if let roomCount = john.residence?.numberOfRooms {
`residence` 后面添加问号之后Swift 就会在 `residence` 不为 `nil` 的情况下访问 `numberOfRooms`
因为访问 `numberOfRooms` 有可能失败,可选链式调用会返回 `Int?` 类型,或称为“可选的 `Int`”。如上例所示,当 `residence``nil` 的时候,可选的 `Int` 将会为 `nil`,表明无法访问 `numberOfRooms`。访问成功时,可选的 `Int` 值会通过可选绑定展开,并赋值给非可选类型的 `roomCount` 常量。
因为访问 `numberOfRooms` 有可能失败,可选链式调用会返回 `Int?` 类型,或称为“可选的 `Int`”。如上例所示,当 `residence``nil` 的时候,可选的 `Int` 将会为 `nil`,表明无法访问 `numberOfRooms`。访问成功时,可选的 `Int` 值会通过可选绑定解包,并赋值给非可选类型的 `roomCount` 常量。
要注意的是,即使 `numberOfRooms` 是非可选的 `Int` 时,这一点也成立。只要使用可选链式调用就意味着 `numberOfRooms` 会返回一个 `Int?` 而不是 `Int`
@ -156,7 +156,7 @@ class Address {
## 通过可选链式调用访问属性 {#accessing-properties-through-optional-chaining}
正如[使用可选链式调用代替强制展开](#optional_chaining_as_an_alternative_to_forced_unwrapping)中所述,可以通过可选链式调用在一个可选值上访问它的属性,并判断访问是否成功。
正如 [使用可选链式调用代替强制解包](#optional-chaining-as-an-alternative-to-forced-unwrapping) 中所述,可以通过可选链式调用在一个可选值上访问它的属性,并判断访问是否成功。
使用前面定义过的类,创建一个 `Person` 实例,然后像之前一样,尝试访问 `numberOfRooms` 属性:
@ -212,7 +212,7 @@ func printNumberOfRooms() {
}
```
这个方法没有返回值。然而,没有返回值的方法具有隐式的返回类型 `Void`,如[无返回值函数](./06_Functions.md#functions_without_return_values)中所述。这意味着没有返回值的方法也会返回 `()`,或者说空的元组。
这个方法没有返回值。然而,没有返回值的方法具有隐式的返回类型 `Void`,如 [无返回值函数](./06_Functions.md#functions-without-return-values) 中所述。这意味着没有返回值的方法也会返回 `()`,或者说空的元组。
如果在可选值上通过可选链式调用来调用这个方法,该方法的返回类型会是 `Void?`,而不是 `Void`,因为通过可选链式调用得到的返回值都是可选的。这样我们就可以使用 `if` 语句来判断能否成功调用 `printNumberOfRooms()` 方法,即使方法本身没有定义返回值。通过判断返回值是否为 `nil` 可以判断调用是否成功:
@ -225,7 +225,7 @@ if john.residence?.printNumberOfRooms() != nil {
// 打印“It was not possible to print the number of rooms.”
```
同样的,可以据此判断通过可选链式调用为属性赋值是否成功。在上面的[通过可选链式调用访问属性](#accessing_properties_through_optional_chaining)的例子中,我们尝试给 `john.residence` 中的 `address` 属性赋值,即使 `residence``nil`。通过可选链式调用给属性赋值会返回 `Void?`,通过判断返回值是否为 `nil` 就可以知道赋值是否成功:
同样的,可以据此判断通过可选链式调用为属性赋值是否成功。在上面的 [通过可选链式调用访问属性](#accessing-properties-through-optional-chaining) 的例子中,我们尝试给 `john.residence` 中的 `address` 属性赋值,即使 `residence``nil`。通过可选链式调用给属性赋值会返回 `Void?`,通过判断返回值是否为 `nil` 就可以知道赋值是否成功:
```swift
if (john.residence?.address = someAddress) != nil {

View File

@ -114,7 +114,7 @@ func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
}
```
上例中,`buyFavoriteSnack(person:vendingMachine:)` 函数会查找某人最喜欢的零食,并通过调用 `vend(itemNamed:)` 方法来尝试为他们购买。因为 `vend(itemNamed:)` 方法能抛出错误,所以在调用它时候在它前面加了 `try` 关键字。
上例中,`buyFavoriteSnack(person:vendingMachine:)` 函数会查找某人最喜欢的零食,并通过调用 `vend(itemNamed:)` 方法来尝试为他们购买。因为 `vend(itemNamed:)` 方法能抛出错误,所以在调用它时候在它前面加了 `try` 关键字。
`throwing` 构造器能像 `throwing` 函数一样传递错误。例如下面代码中的 `PurchasedSnack` 构造器在构造过程中调用了 throwing 函数,并且通过传递到它的调用者来处理这些错误。
@ -142,12 +142,14 @@ do {
statements
} catch pattern 2 where condition {
statements
} catch pattern 3, pattern 4 where condition {
statements
} catch {
statements
}
```
`catch` 后面写一个匹配模式来表明这个子句能处理什么样的错误。如果一条 `catch` 子句没有指定匹配模式,那么这条子句可以匹配任何错误,并且把错误绑定到一个名字为 `error` 的局部常量。关于模式匹配的更多信息请参考 [模式](../chapter3/07_Patterns.html)。
`catch` 后面写一个匹配模式来表明这个子句能处理什么样的错误。如果一条 `catch` 子句没有指定匹配模式,那么这条子句可以匹配任何错误,并且把错误绑定到一个名字为 `error` 的局部常量。关于模式匹配的更多信息请参考 [模式](../03_language_reference/08_Patterns.md)。
举例来说,下面的代码处理了 `VendingMachineError` 枚举类型的全部三种情况:
@ -173,14 +175,14 @@ do {
`catch` 子句不必将 `do` 子句中的代码所抛出的每一个可能的错误都作处理。如果所有 `catch` 子句都未处理错误,错误就会传递到周围的作用域。然而,错误还是必须要被某个周围的作用域处理的。在不会抛出错误的函数中,必须用 `do-catch` 语句处理错误。而能够抛出错误的函数既可以使用 `do-catch` 语句处理,也可以让调用方来处理错误。如果错误传递到了顶层作用域却依然没有被处理,你会得到一个运行时错误。
以下面的代码为例,不是 `VendingMachineError`明的错误会在调用函数的地方被捕获:
以下面的代码为例,不是 `VendingMachineError`明的错误会在调用函数的地方被捕获:
```swift
func nourish(with item: String) throws {
do {
try vendingMachine.vend(itemNamed: item)
} catch is VendingMachineError {
print("Invalid selection, out of stock, or not enough money.")
print("Couldn't buy that from the vending machine.")
}
}
@ -189,12 +191,28 @@ do {
} catch {
print("Unexpected non-vending-machine-related error: \(error)")
}
// 打印“Invalid selection, out of stock, or not enough money.”
// 打印“Couldn't buy that from the vending machine.”
```
如果 `vend(itemNamed:)` 抛出的是一个 `VendingMachineError` 类型的错误,`nourish(with:)` 会打印一条消息,否则 `nourish(with:)` 会将错误抛给它的调用方。这个错误之后会被通用的 `catch` 语句捕获。
### 将错误转换成可选值 {#converting_errors_to_optional_values}
另一种捕获多个相关错误的方式是将它们放在 `catch` 后,通过逗号分隔。
例如:
```swift
func eat(item: String) throws {
do {
try vendingMachine.vend(itemNamed: item)
} catch VendingMachineError.invalidSelection, VendingMachineError.insufficientFunds, VendingMachineError.outOfStock {
print("Invalid selection, out of stock, or not enough money.")
}
}
```
`eat(item:)` 函数捕获了列出来的 `VendingMachine` 错误,且它的错误文本和列表的错误相关。如果列出来的三个错误中任意一个抛出,这个 `catch` 代码块就会打印信息。其他错误会传递到外面的作用域,包括以后可能添加的其他 `VendingMachine` 错误。
### 将错误转换成可选值 {#converting-errors-to-optional-values}
可以使用 `try?` 通过将错误转换成一个可选值来处理错误。如果是在计算 `try?` 表达式时抛出错误,该表达式的结果就为 `nil`。例如,在下面的代码中,`x``y` 有着相同的数值和等价的含义:
@ -225,7 +243,7 @@ func fetchData() -> Data? {
}
```
### 禁用错误传递 {#disabling_error_propagation}
### 禁用错误传递 {#disabling-error-propagation}
有时你知道某个 `throwing` 函数实际上在运行时是不会抛出错误的,在这种情况下,你可以在表达式前面写 `try!` 来禁用错误传递,这会把调用包装在一个不会有错误抛出的运行时断言中。如果真的抛出了错误,你会得到一个运行时错误。

View File

@ -4,7 +4,7 @@
类型转换在 Swift 中使用 `is``as` 操作符实现。这两个操作符分别提供了一种简单达意的方式去检查值的类型或者转换它的类型。
你也可以用它来检查一个类型是否遵循了某个协议,就像在[检验协议遵循](./21_Protocols.md#checking_for_protocol_conformance)部分讲述的一样。
你也可以用它来检查一个类型是否遵循了某个协议,就像在 [检验协议遵循](./21_Protocols.md#checking-for-protocol-conformance) 部分讲述的一样。
## 为类型转换定义类层次 {#defining-a-class-hierarchy-for-type-casting}

View File

@ -63,7 +63,7 @@ struct BlackjackCard {
`BlackjackCard` 结构体拥有两个属性——`rank``suit`。它也同样定义了一个计算型属性 `description``description` 属性用 `rank``suit` 中的内容来构建对扑克牌名字和数值的描述。该属性使用可选绑定来检查可选类型 `second` 是否有值,若有值,则在原有的描述中增加对 `second` 的描述。
因为 `BlackjackCard` 是一个没有自定义构造器的结构体,在[结构体的逐一成员构造器](./14_Initialization.md#memberwise_initializers_for_structure_types)中可知,结构体有默认的成员构造器,所以你可以用默认的构造器去初始化新常量 `theAceOfSpades`
因为 `BlackjackCard` 是一个没有自定义构造器的结构体,在 [结构体的逐一成员构造器](./14_Initialization.md#memberwise-initializers-for-structure-types) 中可知,结构体有默认的成员构造器,所以你可以用默认的构造器去初始化新常量 `theAceOfSpades`
```swift
let theAceOfSpades = BlackjackCard(rank: .ace, suit: .spades)

View File

@ -87,7 +87,7 @@ print("A marathon is \(aMarathon) meters long")
扩展可以给一个类添加新的便利构造器,但是它们不能给类添加新的指定构造器或者析构器。指定构造器和析构器必须始终由类的原始实现提供。
如果你使用扩展给一个值类型添加构造器只是用于给所有存储属性提供默认值,且没有定义任何自定义构造器,那么你可以在该值类型扩展的构造器中使用默认构造器和成员构造器。如果你构造器写到了值类型的原始实现中,就像 [值类型的构造器委托](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID215) 中所描述的,那么就不属于在扩展中添加构造器
如果你使用扩展给一个值类型添加构造器,而这个值类型已经为所有存储属性提供默认值,且没有定义任何自定义构造器,那么你可以在该值类型扩展的构造器中使用默认构造器和成员构造器。如果你已经将构造器写值类型的原始实现中,则不适用于这种情况,如同 [值类型的构造器委托](https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID215) 中所描述的那样
如果你使用扩展给另一个模块中定义的结构体添加构造器,那么新的构造器直到定义模块中使用一个构造器之前,不能访问 `self`

View File

@ -22,7 +22,7 @@ struct SomeStructure: FirstProtocol, AnotherProtocol {
}
```
若一个拥有父类的类在遵循协议时,应该将父类名放在协议名之前,以逗号分隔:
一个拥有父类,应该将父类名放在遵循的协议名之前,以逗号分隔:
```swift
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
@ -75,7 +75,7 @@ let john = Person(fullName: "John Appleseed")
这个例子中定义了一个叫做 `Person` 的结构体,用来表示一个具有名字的人。从第一行代码可以看出,它遵循了 `FullyNamed` 协议。
`Person` 结构体的每一个实例都有一个 `String` 类型的存储型属性 `fullName`。这正好满足了 `FullyNamed` 协议的要求,也就意味着 `Person` 结构体正确地符合了协议。(如果协议要求未被完全满足,在编译时会报错。)
`Person` 结构体的每一个实例都有一个 `String` 类型的存储型属性 `fullName`。这正好满足了 `FullyNamed` 协议的要求,也就意味着 `Person` 结构体正确地遵循了协议。(如果协议要求未被完全满足,在编译时会报错。)
下面是一个更为复杂的类,它采纳并遵循了 `FullyNamed` 协议:
@ -95,7 +95,7 @@ var ncc1701 = Starship(name: "Enterprise", prefix: "USS")
// ncc1701.fullName 为 "USS Enterprise"
```
`Starship`只能`fullName` 作为只读的计算属性来实现。每一个 `Starship` 类的实例都有一个名为 `name` 的非可选属性和一个名为 `prefix` 的可选属性。 当 `prefix` 存在时,计算属性 `fullName` 会将 `prefix` 插入到 `name` 之前,从而得到一个带有 `prefix``fullName`
`Starship` 类把 `fullName` 作为只读的计算属性来实现。每一个 `Starship` 类的实例都有一个名为 `name` 的非可选属性和一个名为 `prefix` 的可选属性。 当 `prefix` 存在时,计算属性 `fullName` 会将 `prefix` 插入到 `name` 之前,从而得到一个带有 `prefix``fullName`
## 方法要求 {#method-requirements}
@ -143,7 +143,7 @@ print("And another one: \(generator.random())")
## 异变方法要求 {#mutating-method-requirements}
有时需要在方法中改变(或*异变*)方法所属的实例。例如,在值类型(即结构体和枚举)的实例方法中,将 `mutating` 关键字作为方法的前缀,写在 `func` 关键字之前,表示可以在该方法中修改它所属的实例以及实例的任意属性的值。这一过程在 [在实例方法中修改值类型](./11_Methods.md#modifying_value_types_from_within_instance_methods) 章节中有详细描述。
有时需要在方法中改变(或*异变*)方法所属的实例。例如,在值类型(即结构体和枚举)的实例方法中,将 `mutating` 关键字作为方法的前缀,写在 `func` 关键字之前,表示可以在该方法中修改它所属的实例以及实例的任意属性的值。这一过程在 [在实例方法中修改值类型](./11_Methods.md#modifying-value-types-from-within-instance-methods) 章节中有详细描述。
如果你在协议中定义了一个实例方法,该方法会改变遵循该协议的类型的实例,那么在定义协议时需要在方法前加 `mutating` 关键字。这使得结构体和枚举能够遵循此协议并满足此方法要求。
@ -204,13 +204,13 @@ class SomeClass: SomeProtocol {
}
```
使用 `required` 修饰符可以确保所有子类也必须提供此构造器实现,从而也能符合协议。
使用 `required` 修饰符可以确保所有子类也必须提供此构造器实现,从而也能遵循协议。
关于 `required` 构造器的更多内容,请参考 [必要构造器](./14_Initialization.md#required_initializers)。
关于 `required` 构造器的更多内容,请参考 [必要构造器](./14_Initialization.md#required-initializers)。
> 注意
>
> 如果类已经被标记为 `final`,那么不需要在协议构造器的实现中使用 `required` 修饰符,因为 `final` 类不能有子类。关于 `final` 修饰符的更多内容,请参见 [防止重写](./13_Inheritance.md#preventing_overrides)。
> 如果类已经被标记为 `final`,那么不需要在协议构造器的实现中使用 `required` 修饰符,因为 `final` 类不能有子类。关于 `final` 修饰符的更多内容,请参见 [防止重写](./13_Inheritance.md#preventing-overrides)。
如果一个子类重写了父类的指定构造器,并且该构造器满足了某个协议的要求,那么该构造器的实现需要同时标注 `required``override` 修饰符:
@ -236,13 +236,13 @@ class SomeSubClass: SomeSuperClass, SomeProtocol {
### 可失败构造器要求 {#failable-initializer-requirements}
协议还可以为遵循协议的类型定义可失败构造器要求,详见 [可失败构造器](./14_Initialization.md#failable_initializers)。
协议还可以为遵循协议的类型定义可失败构造器要求,详见 [可失败构造器](./14_Initialization.md#failable-initializers)。
遵循协议的类型可以通过可失败构造器(`init?`)或非可失败构造器(`init`)来满足协议中定义的可失败构造器要求。协议中定义的非可失败构造器要求可以通过非可失败构造器(`init`)或隐式解包可失败构造器(`init!`)来满足。
## 协议作为类型 {#protocols-as-types}
尽管协议本身并未实现任何功能,但是协议可以被当做一个功能完备的类型来使用。
尽管协议本身并未实现任何功能,但是协议可以被当做一个功能完备的类型来使用。协议作为类型使用,有时被称作「存在类型」,这个名词来自「存在着一个类型 T该类型遵循协议 T」。
协议可以像其他普通类型一样使用,使用场景如下:
@ -272,7 +272,7 @@ class Dice {
例子中定义了一个 `Dice` 类,用来代表桌游中拥有 N 个面的骰子。`Dice` 的实例含有 `sides``generator` 两个属性,前者是整型,用来表示骰子有几个面,后者为骰子提供一个随机数生成器,从而生成随机点数。
`generator` 属性的类型为 `RandomNumberGenerator`,因此任何遵循了 `RandomNumberGenerator` 协议的类型的实例都可以赋值给 `generator`,除此之外并无其他要求。
`generator` 属性的类型为 `RandomNumberGenerator`,因此任何遵循了 `RandomNumberGenerator` 协议的类型的实例都可以赋值给 `generator`,除此之外并无其他要求。并且由于其类型是 `RandomNumberGenerator`,在 `Dice` 类中与 `generator` 交互的代码,必须适用于所有 `generator` 实例都遵循的方法。这句话的意思是不能使用由 `generator` 底层类型提供的任何方法或属性。但是你可以通过向下转型,从协议类型转换成底层实现类型,比如从父类向下转型为子类。请参考 [向下转型](./18_Type_Casting.md#downcasting)。
`Dice` 类还有一个构造器,用来设置初始状态。构造器有一个名为 `generator`,类型为 `RandomNumberGenerator` 的形参。在调用构造方法创建 `Dice` 的实例时,可以传入任何遵循 `RandomNumberGenerator` 协议的实例给 `generator`
@ -312,7 +312,7 @@ protocol DiceGameDelegate {
`DiceGame` 协议可以被任意涉及骰子的游戏遵循。
`DiceGameDelegate` 协议可以被任意类型遵循,用来追踪 `DiceGame` 的游戏过程。为了防止强引用导致的循环引用问题,可以把协议声明为弱引用,更多相关的知识请看 [类实例之间的循环强引用](./23_Automatic_Reference_Counting.md#strong_reference_cycles_between_class_instances),当协议标记为类专属可以使 `SnakesAndLadders` 类在声明协议时强制要使用弱引用。若要声明类专属的协议就必须继承于 `AnyObject` ,更多请看 [类专属的协议](#class_only_protocol)。
`DiceGameDelegate` 协议可以被任意类型遵循,用来追踪 `DiceGame` 的游戏过程。为了防止强引用导致的循环引用问题,可以把协议声明为弱引用,更多相关的知识请看 [类实例之间的循环强引用](./24_Automatic_Reference_Counting.md#strong-reference-cycles-between-class-instances),当协议标记为类专属可以使 `SnakesAndLadders` 类在声明协议时强制要使用弱引用。若要声明类专属的协议就必须继承于 `AnyObject` ,更多请看 [类专属的协议](#class-only-protocol)。
如下所示,`SnakesAndLadders` 是 [控制流](./05_Control_Flow.md) 章节引入的蛇梯棋游戏的新版本。新版本使用 `Dice` 实例作为骰子,并且实现了 `DiceGame``DiceGameDelegate` 协议,后者用来记录游戏的过程:
@ -355,7 +355,7 @@ class SnakesAndLadders: DiceGame {
游戏使用 `SnakesAndLadders` 类的 `init()` 构造器来初始化游戏。所有的游戏逻辑被转移到了协议中的 `play()` 方法,`play()` 方法使用协议要求的 `dice` 属性提供骰子摇出的值。
注意,`delegate` 并不是游戏的必备条件,因此 `delegate` 被定义为 `DiceGameDelegate` 类型的可选属性。因为 `delegate` 是可选值,因此会被自动赋予初始值 `nil`。随后,可以在游戏中为 `delegate` 设置适当的值。
注意,`delegate` 并不是游戏的必备条件,因此 `delegate` 被定义为 `DiceGameDelegate` 类型的可选属性。因为 `delegate` 是可选值,因此会被自动赋予初始值 `nil`。随后,可以在游戏中为 `delegate` 设置适当的值。因为 `DiceGameDelegate` 协议是类专属的,可以将 `delegate` 声明为 `weak`,从而避免循环引用。
`DicegameDelegate` 协议提供了三个方法用来追踪游戏过程。这三个方法被放置于游戏的逻辑中,即 `play()` 方法内。分别在游戏开始时,新一轮开始时,以及游戏结束时被调用。
@ -387,7 +387,7 @@ class DiceGameTracker: DiceGameDelegate {
`gameDidStart(_:)` 方法从 `game` 参数获取游戏信息并打印。`game` 参数是 `DiceGame` 类型而不是 `SnakeAndLadders` 类型,所以在 `gameDidStart(_:)` 方法中只能访问 `DiceGame` 协议中的内容。当然了,`SnakeAndLadders` 的方法也可以在类型转换之后调用。在上例代码中,通过 `is` 操作符检查 `game` 是否为 `SnakesAndLadders` 类型的实例,如果是,则打印出相应的消息。
无论当前进行的是何种游戏,由于 `game` 符合 `DiceGame` 协议,可以确保 `game` 含有 `dice` 属性。因此在 `gameDidStart(_:)` 方法中可以通过传入的 `game` 参数来访问 `dice` 属性,进而打印出 `dice``sides` 属性的值。
无论当前进行的是何种游戏,由于 `game` 遵循 `DiceGame` 协议,可以确保 `game` 含有 `dice` 属性。因此在 `gameDidStart(_:)` 方法中可以通过传入的 `game` 参数来访问 `dice` 属性,进而打印出 `dice``sides` 属性的值。
`DiceGameTracker` 的运行情况如下所示:
@ -455,7 +455,7 @@ print(game.textualDescription)
## 有条件地遵循协议 {#Conditionally-Conforming-to-a-Protocol}
泛型类型可能只在某些情况下满足一个协议的要求,比如当类型的泛型形式参数遵循对应协议时。你可以通过在扩展类型时列出限制让泛型类型有条件地遵循某协议。在你采纳协议的名字后面写泛型 `where` 分句。更多关于泛型 `where` 分句,见 [泛型 Where 分句](./22_Generics.md##where_clauses)。
泛型类型可能只在某些情况下满足一个协议的要求,比如当类型的泛型形式参数遵循对应协议时。你可以通过在扩展类型时列出限制让泛型类型有条件地遵循某协议。在你采纳协议的名字后面写泛型 `where` 分句。更多关于泛型 `where` 分句,见 [泛型 Where 分句](./22_Generics.md##where-clauses)。
下面的扩展让 `Array` 类型只要在存储遵循 `TextRepresentable` 协议的元素时就遵循 `TextRepresentable` 协议。
@ -473,7 +473,7 @@ print(myDice.textualDescription)
## 在扩展里声明采纳协议 {#declaring-protocol-adoption-with-an-extension}
当一个类型已经符合了某个协议中的所有要求,却还没有声明采纳该协议时,可以通过空的扩展来让它采纳该协议:
当一个类型已经遵循了某个协议中的所有要求,却还没有声明采纳该协议时,可以通过空的扩展来让它采纳该协议:
```swift
struct Hamster {
@ -498,9 +498,65 @@ print(somethingTextRepresentable.textualDescription)
>
> 即使满足了协议的所有要求,类型也不会自动遵循协议,必须显式地遵循协议。
## 使用合成实现来采纳协议 {#adopting-a-protocol-using-a-synthesized-implementation}
Swift 可以自动提供一些简单场景下遵循 `Equatable``Hashable``Comparable` 协议的实现。在使用这些合成实现之后,无需再编写重复的代码来实现这些协议所要求的方法。
Swift 为以下几种自定义类型提供了 `Equatable` 协议的合成实现:
- 遵循 `Equatable` 协议且只有存储属性的结构体。
- 遵循 `Equatable` 协议且只有关联类型的枚举
- 没有任何关联类型的枚举
在包含类型原始声明的文件中声明对 `Equatable` 协议的遵循,可以得到 `==` 操作符的合成实现,且无需自己编写任何关于 `==` 的实现代码。`Equatable` 协议同时包含 `!=` 操作符的默认实现。
下面的例子中定义了一个 `Vector3D` 结构体来表示一个类似 `Vector2D` 的三维向量 `(x, y, z)`。由于 `x``y``z` 都是满足 `Equatable` 的类型,`Vector3D` 可以得到连等判断的合成实现。
```swift
struct Vector3D: Equatable {
var x = 0.0, y = 0.0, z = 0.0
}
let twoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
let anotherTwoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
if twoThreeFour == anotherTwoThreeFour {
print("These two vectors are also equivalent.")
}
// 打印 "These two vectors are also equivalent."
```
Swift 为以下几种自定义类型提供了 `Hashable` 协议的合成实现:
- 遵循 `Hashable` 协议且只有存储属性的结构体。
- 遵循 `Hashable` 协议且只有关联类型的枚举
- 没有任何关联类型的枚举
在包含类型原始声明的文件中声明对 `Hashable` 协议的遵循,可以得到 `hash(into:)` 的合成实现,且无需自己编写任何关于 `hash(into:)` 的实现代码。
Swift 为没有原始值的枚举类型提供了 `Comparable` 协议的合成实现。如果枚举类型包含关联类型,那这些关联类型也必须同时遵循 `Comparable` 协议。在包含原始枚举类型声明的文件中声明其对 `Comparable` 协议的遵循,可以得到 `<` 操作符的合成实现,且无需自己编写任何关于 `<` 的实现代码。`Comparable` 协议同时包含 `<=``>``>=` 操作符的默认实现。
下面的例子中定义了 `SkillLevel` 枚举类型其中定义了初学者beginner、中级intermediate和专家expert三种等级专家等级会由额外的星级stars来进行排名。
```swift
enum SkillLevel: Comparable {
case beginner
case intermediate
case expert(stars: Int)
}
var levels = [SkillLevel.intermediate, SkillLevel.beginner,
SkillLevel.expert(stars: 5), SkillLevel.expert(stars: 3)]
for level in levels.sorted() {
print(level)
}
// 打印 "beginner"
// 打印 "intermediate"
// 打印 "expert(stars: 3)"
// 打印 "expert(stars: 5)"
```
## 协议类型的集合 {#collections-of-protocol-types}
协议类型可以在数组或者字典这样的集合中使用,在 [协议类型](./21_Protocols.md##protocols_as_types) 提到了这样的用法。下面的例子创建了一个元素类型为 `TextRepresentable` 的数组:
协议类型可以在数组或者字典这样的集合中使用,在 [协议类型](./21_Protocols.md##protocols-as-types) 提到了这样的用法。下面的例子创建了一个元素类型为 `TextRepresentable` 的数组:
```swift
let things: [TextRepresentable] = [game, d12, simonTheHamster]
@ -588,7 +644,7 @@ protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
> 注意
>
> 当协议定义的要求需要遵循协议的类型必须是引用语义而非值语义时,应该采用类类型专属协议。关于引用语义和值语义的更多内容,请查看 [结构体和枚举是值类型](./09_Classes_and_Structures.md#structures_and_enumerations_are_value_types) 和 [类是引用类型](./09_Classes_and_Structures.md#classes_are_reference_types)。
> 当协议定义的要求需要遵循协议的类型必须是引用语义而非值语义时,应该采用类类型专属协议。关于引用语义和值语义的更多内容,请查看 [结构体和枚举是值类型](./09_Structures_And_Classes.md#structures-and-enumerations-are-value-types) 和 [类是引用类型](./09_Structures_And_Classes.md#classes-are-reference-types)。
## 协议合成 {#protocol-composition}
@ -619,9 +675,9 @@ wishHappyBirthday(to: birthdayPerson)
`Named` 协议包含 `String` 类型的 `name` 属性。`Aged` 协议包含 `Int` 类型的 `age` 属性。`Person` 结构体采纳了这两个协议。
`wishHappyBirthday(to:)` 函数的参数 `celebrator` 的类型为 `Named & Aged` 这意味着“任何同时遵循 Named 和 Aged 的协议”。它不关心参数的具体类型,只要参数符合这两个协议即可。
`wishHappyBirthday(to:)` 函数的参数 `celebrator` 的类型为 `Named & Aged` 这意味着“任何同时遵循 Named 和 Aged 的协议”。它不关心参数的具体类型,只要参数遵循这两个协议即可。
上面的例子创建了一个名为 `birthdayPerson``Person` 的实例,作为参数传递给了 `wishHappyBirthday(to:)` 函数。因为 `Person` 同时符合这两个协议,所以这个参数合法,函数将打印生日问候语。
上面的例子创建了一个名为 `birthdayPerson``Person` 的实例,作为参数传递给了 `wishHappyBirthday(to:)` 函数。因为 `Person` 同时遵循这两个协议,所以这个参数合法,函数将打印生日问候语。
这里有一个例子:将 Location 类和前面的 Named 协议进行组合:
@ -650,16 +706,16 @@ beginConcert(in: seattle)
// 打印 "Hello, Seattle!"
```
`beginConcert(in:)` 函数接受一个类型为 `Location & Named` 的参数,这意味着“任何 Location 的子类,并且遵循 Named 协议”。例如City 就满足这样的条件。
`beginConcert(in:)` 函数接受一个类型为 `Location & Named` 的参数,这意味着“任何 Location 的子类,并且遵循 Named 协议”。在这个例子中City 就满足这样的条件。
将 birthdayPerson 传入 `beginConcert(in:)` 函数是不合法的,因为 Person 不是 Location 的子类。同理,如果你新建一个类继承于 Location但是没有遵循 Named 协议,而用这个类的实例去调用 `beginConcert(in:)` 函数也是非法的。
## 检查协议一致性 {#checking-for-protocol-conformance}
你可以使用[类型转换](./18_Type_Casting.md)中描述的 `is``as` 操作符来检查协议一致性,即是否符合某协议,并且可以转换到指定的协议类型。检查和转换协议的语法与检查和转换类型是完全一样的:
你可以使用 [类型转换](./18_Type_Casting.md) 中描述的 `is``as` 操作符来检查协议一致性,即是否遵循某协议,并且可以转换到指定的协议类型。检查和转换协议的语法与检查和转换类型是完全一样的:
* `is` 用来检查实例是否符合某个协议,若符合则返回 `true`,否则返回 `false`
* `as?` 返回一个可选值,当实例符合某个协议时,返回类型为协议类型的可选值,否则返回 `nil`
* `is` 用来检查实例是否遵循某个协议,若遵循则返回 `true`,否则返回 `false`
* `as?` 返回一个可选值,当实例遵循某个协议时,返回类型为协议类型的可选值,否则返回 `nil`
* `as!` 将实例强制向下转换到某个协议类型,如果强转失败,将触发运行时错误。
下面的例子定义了一个 `HasArea` 协议,该协议定义了一个 `Double` 类型的可读属性 `area`
@ -708,7 +764,7 @@ let objects: [AnyObject] = [
`objects` 数组使用字面量初始化,数组包含一个 `radius``2``Circle` 的实例,一个保存了英国国土面积的 `Country` 实例和一个 `legs``4``Animal` 实例。
如下所示,`objects` 数组可以被迭代,并对迭代出的每一个元素进行检查,看它是否符合 `HasArea` 协议:
如下所示,`objects` 数组可以被迭代,并对迭代出的每一个元素进行检查,看它是否遵循 `HasArea` 协议:
```swift
for object in objects {
@ -723,7 +779,7 @@ for object in objects {
// Something that doesn't have an area
```
当迭代出的元素符合 `HasArea` 协议时,将 `as?` 操作符返回的可选值通过可选绑定,绑定到 `objectWithArea` 常量上。`objectWithArea``HasArea` 协议类型的实例,因此 `area` 属性可以被访问和打印。
当迭代出的元素遵循 `HasArea` 协议时,将 `as?` 操作符返回的可选值通过可选绑定,绑定到 `objectWithArea` 常量上。`objectWithArea``HasArea` 协议类型的实例,因此 `area` 属性可以被访问和打印。
`objects` 数组中的元素的类型并不会因为强转而丢失类型信息,它们仍然是 `Circle``Country``Animal` 类型。然而,当它们被赋值给 `objectWithArea` 常量时,只被视为 `HasArea` 类型,因此只有 `area` 属性能够被访问。
@ -733,7 +789,7 @@ for object in objects {
使用可选要求时(例如,可选的方法或者属性),它们的类型会自动变成可选的。比如,一个类型为 `(Int) -> String` 的方法会变成 `((Int) -> String)?`。需要注意的是整个函数类型是可选的,而不是函数的返回值。
协议中的可选要求可通过可选链式调用来使用,因为遵循协议的类型可能没有实现这些可选要求。类似 `someOptionalMethod?(someArgument)` 这样,你可以在可选方法名称后加上 `?` 来调用可选方法。详细内容可在[可选链式调用](./16_Optional_Chaining.md)章节中查看。
协议中的可选要求可通过可选链式调用来使用,因为遵循协议的类型可能没有实现这些可选要求。类似 `someOptionalMethod?(someArgument)` 这样,你可以在可选方法名称后加上 `?` 来调用可选方法。详细内容可在 [可选链式调用](./16_Optional_Chaining.md) 章节中查看。
下面的例子定义了一个名为 `Counter` 的用于整数计数的类,它使用外部的数据源来提供每次的增量。数据源由 `CounterDataSource` 协议定义,它包含两个可选要求:
@ -772,7 +828,7 @@ class Counter {
这里使用了两层可选链式调用。首先,由于 `dataSource` 可能为 `nil`,因此在 `dataSource` 后边加上了 `?`,以此表明只在 `dataSource` 非空时才去调用 `increment(forCount:)` 方法。其次,即使 `dataSource` 存在,也无法保证其是否实现了 `increment(forCount:)` 方法,因为这个方法是可选的。因此,`increment(forCount:)` 方法同样使用可选链式调用进行调用,只有在该方法被实现的情况下才能调用它,所以在 `increment(forCount:)` 方法后边也加上了 `?`
调用 `increment(forCount:)` 方法在上述两种情形下都有可能失败,所以返回值为 `Int?` 类型。虽然在 `CounterDataSource` 协议中,`increment(forCount:)` 的返回值类型是非可选 `Int`。另外,即使这里使用了两层可选链式调用,最后的返回结果依旧是单层的可选类型。关于这一点的更多信息,请查阅 [连接多层可选链式调用](./16_Optional_Chaining)
调用 `increment(forCount:)` 方法在上述两种情形下都有可能失败,所以返回值为 `Int?` 类型。虽然在 `CounterDataSource` 协议中,`increment(forCount:)` 的返回值类型是非可选 `Int`。另外,即使这里使用了两层可选链式调用,最后的返回结果依旧是单层的可选类型。关于这一点的更多信息,请查阅 [连接多层可选链式调用](./16_Optional_Chaining.md)
在调用 `increment(forCount:)` 方法后,`Int?` 型的返回值通过可选绑定解包并赋值给常量 `amount`。如果可选值确实包含一个数值,也就是说,数据源和方法都存在,数据源方法返回了一个有效值。之后便将解包后的 `amount` 加到 `count` 上,增量操作完成。
@ -861,9 +917,11 @@ print("And here's a random Boolean: \(generator.randomBool())")
// 打印 “And here's a random Boolean: true”
```
协议扩展可以为遵循协议的类型增加实现,但不能声明该协议继承自另一个协议。协议的继承只能在协议声明处进行指定。
### 提供默认实现 {#providing-default-implementations}
可以通过协议扩展来为协议要求的属性、方法以及下标提供默认的实现。如果遵循协议的类型为这些要求提供了自己的实现,那么这些自定义实现将会替代扩展中的默认实现被使用。
可以通过协议扩展来为协议要求的方法、计算属性提供默认的实现。如果遵循协议的类型为这些要求提供了自己的实现,那么这些自定义实现将会替代扩展中的默认实现被使用。
> 注意
>
@ -881,9 +939,9 @@ extension PrettyTextRepresentable {
### 为协议扩展添加限制条件 {#adding-constraints-to-protocol-extensions}
在扩展协议的时候,可以指定一些限制条件,只有遵循协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用 `where` 子句来描述,正如[泛型 Where 子句](./22_Generics.md#where_clauses)中所描述的。
在扩展协议的时候,可以指定一些限制条件,只有遵循协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用 `where` 子句来描述,正如 [泛型 Where 子句](./22_Generics.md#where-clauses) 中所描述的。
例如,你可以扩展 `Collection` 协议,适用于集合中的元素遵循了 `Equatable` 协议的情况。通过限制集合元素遵 `Equatable` 协议, 作为标准库的一部分, 你可以使用 `==``!=` 操作符来检查两个元素的等价性和非等价性。
例如,你可以扩展 `Collection` 协议,适用于集合中的元素遵循了 `Equatable` 协议的情况。通过限制集合元素遵 `Equatable` 协议, 作为标准库的一部分, 你可以使用 `==``!=` 操作符来检查两个元素的等价性和非等价性。
```swift
extension Collection where Element: Equatable {

View File

@ -1,6 +1,6 @@
# 泛型
*泛型代码*让你能根据自定义的需求,编写出适用于任意类型的、灵活可复用的函数及类型。你可避免编写重复的代码,用一种清晰抽象的方式来表达代码的意图。
*泛型代码*让你能根据自定义的需求,编写出适用于任意类型的、灵活可复用的函数及类型。你可避免编写重复的代码,而是用一种清晰抽象的方式来表达代码的意图。
泛型是 Swift 最强大的特性之一,很多 Swift 标准库是基于泛型代码构建的。实际上,即使你没有意识到,你也一直在*语言指南*中使用泛型。例如Swift 的 `Array``Dictionary` 都是泛型集合。你可以创建一个 `Int` 类型数组,也可创建一个 `String` 类型数组,甚至可以是任意其他 Swift 类型的数组。同样,你也可以创建一个存储任意指定类型的字典,并对该类型没有限制。
@ -16,7 +16,7 @@ func swapTwoInts(_ a: inout Int, _ b: inout Int) {
}
```
这个函数使用输入输出参数(`inout`)来交换 `a``b` 的值,具体请参考[输入输出参数](./06_Functions.md#in_out_parameters)。
这个函数使用输入输出参数(`inout`)来交换 `a``b` 的值,具体请参考 [输入输出参数](./06_Functions.md#in-out-parameters)。
`swapTwoInts(_:_:)` 函数将 `b` 的原始值换成了 `a`,将 `a` 的原始值换成了 `b`,你可以调用这个函数来交换两个 `Int` 类型变量:
@ -44,7 +44,7 @@ func swapTwoDoubles(_ a: inout Double, _ b: inout Double) {
}
```
你可能注意到了,`swapTwoInts(_:_:)``swapTwoStrings(_:_:)``swapTwoDoubles(_:_:)` 函数体是一样的,唯一的区别是它们接受的参数类型(`Int``String``Double`)。
你可能注意到了,`swapTwoInts(_:_:)``swapTwoStrings(_:_:)``swapTwoDoubles(_:_:)` 函数体是一样的,唯一的区别是它们接受的参数类型(`Int``String``Double`)。
在实际应用中,通常需要一个更实用更灵活的函数来交换两个任意类型的值,幸运的是,泛型代码帮你解决了这种问题。(这些函数的泛型版本已经在下面定义好了。)
@ -226,13 +226,15 @@ if let topItem = stackOfStrings.topItem {
// 打印“The top item on the stack is tres.”
```
泛型类型的扩展,还可以包括类型扩展需要额外满足的条件,从而对类型添加新功能,这一部分将在 [具有泛型 Where 子句的扩展](#extensions-with-a-generic-where-clause) 中进行讨论。
## 类型约束 {#type-constraints}
`swapTwoValues(_:_:)` 函数和 `Stack` 适用于任意类型。不过,如果能对泛型函数或泛型类型中添加特定的*类型约束*,这将在某些情况下非常有用。类型约束指定类型参数必须继承自指定类、遵循特定的协议或协议组合。
例如Swift 的 `Dictionary` 类型对字典的键的类型做了些限制。在 [字典的描述](./04_Collection_Types.md#dictionaries) 中字典键的类型必须是可哈希hashable的。也就是说必须有一种方法能够唯一地表示它。字典键之所以要是可哈希的是为了便于检查字典中是否已经包含某个特定键的值。若没有这个要求字典将无法判断是否可以插入或替换某个指定键的值也不能查找到已经存储在字典中的指定键的值。
这个要求通过 `Dictionary` 键类型上的类型约束实现,它指明了键必须遵循 Swift 标准库中定义的 `Hashable` 协议。所有 Swift 的基本类型(例如 `String``Int``Double``Bool`)默认都是可哈希的。
这个要求通过 `Dictionary` 键类型上的类型约束实现,它指明了键必须遵循 Swift 标准库中定义的 `Hashable` 协议。所有 Swift 的基本类型(例如 `String``Int``Double``Bool`)默认都是可哈希的。如何让自定义类型遵循 `Hashable` 协议,可以查看文档 [遵循 Hashable 协议](https://developer.apple.com/documentation/swift/hashable#2849490)。
当自定义泛型类型时,你可以定义你自己的类型约束,这些约束将提供更为强大的泛型编程能力。像 `可哈希hashable` 这种抽象概念根据它们的概念特征来描述类型,而不是它们的具体类型。
@ -408,7 +410,7 @@ struct Stack<Element>: Container {
### 扩展现有类型来指定关联类型 {#extending-an-existing-type-to-specify-an-associated-type}
[在扩展添加协议一致性](./21_Protocols.md#adding_protocol_conformance_with_an_extension)中描述了如何利用扩展让一个已存在的类型符合一个协议,这包括使用了关联类型协议。
[在扩展添加协议一致性](./21_Protocols.md#adding-protocol-conformance-with-an-extension) 中描述了如何利用扩展让一个已存在的类型遵循一个协议,这包括使用了关联类型协议。
Swift 的 `Array` 类型已经提供 `append(_:)` 方法,`count` 属性,以及带有 `Int` 索引的下标来检索其元素。这三个功能都符合 `Container` 协议的要求,也就意味着你只需声明 `Array` 遵循`Container` 协议,就可以扩展 Array使其遵从 Container 协议。你可以通过一个空扩展来实现这点,正如通过扩展采纳协议中的描述:
@ -433,7 +435,7 @@ protocol Container {
要遵守 `Container` 协议,`Item` 类型也必须遵守 `Equatable` 协议。
### 在关联类型约束里使用协议 {#using-a-protocol-in-its-associated-types-constraints}
### 在关联类型约束里使用协议 {#using-a-protocol-in-its-associated-types-constraints}
协议可以作为它自身的要求出现。例如,有一个协议细化了 `Container` 协议,添加了一个` suffix(_:)` 方法。`suffix(_:)` 方法返回容器中从后往前给定数量的元素,并把它们存储在一个 `Suffix` 类型的实例里。
@ -444,9 +446,9 @@ protocol SuffixableContainer: Container {
}
```
在这个协议里,`Suffix` 是一个关联类型,就像上边例子中 `Container``Item` 类型一样。`Suffix` 拥有两个约束:它必须遵循 `SuffixableContainer` 协议(就是当前定义的协议),以及它的 `Item` 类型必须是和容器里的 `Item` 类型相同。`Item` 的约束是一个 `where` 分句,它在下面有泛型 `Where` 分句的扩展中有讨论。
在这个协议里,`Suffix` 是一个关联类型,就像上边例子中 `Container``Item` 类型一样。`Suffix` 拥有两个约束:它必须遵循 `SuffixableContainer` 协议(就是当前定义的协议),以及它的 `Item` 类型必须是和容器里的 `Item` 类型相同。`Item` 的约束是一个 `where` 分句,它在下面 [有泛型 Where 子句的扩展](#extensions-with-a-generic-where-clause) 中有讨论。
这是上面 [强引用循环闭包](./23_Automatic_Reference_Counting.md#strong_reference_cycles_for_closures) 中 `Stack` 类型的扩展,它遵循了 SuffixableContainer 协议:
这是上面 [泛型类型](#generic-types) 中 `Stack` 类型的扩展,它遵循了 SuffixableContainer 协议:
```swift
extension Stack: SuffixableContainer {
@ -484,7 +486,7 @@ extension IntStack: SuffixableContainer {
## 泛型 Where 语句 {#where-clauses}
[类型约束](#type_constraints)让你能够为泛型函数、下标、类型的类型参数定义一些强制要求。
[类型约束](#type-constraints) 让你能够为泛型函数、下标、类型的类型参数定义一些强制要求。
对关联类型添加约束通常是非常有用的。你可以通过定义一个泛型 `where` 子句来实现。通过泛型 `where` 子句让关联类型遵从某个特定的协议,以及某个特定的类型参数和关联类型必须类型相同。你可以通过将 `where` 关键字紧跟在类型参数列表后面来定义 `where` 子句,`where` 子句后跟一个或者多个针对关联类型的约束,以及一个或多个类型参数和关联类型间的相等关系。你可以在函数体或者类型的大括号之前添加 `where` 子句。
@ -641,9 +643,55 @@ print([1260.0, 1200.0, 98.6, 37.0].average())
就像可以在其他地方写泛型 `where` 子句一样,你可以在一个泛型 `where` 子句中包含多个条件作为扩展的一部分。用逗号分隔列表中的每个条件。
## 包含上下文关系的 where 分句 {#contextual-where-clauses}
当你使用泛型时,可以为没有独立类型约束的声明添加 `where` 分句。例如,你可以使用 `where` 分句为泛型添加下标,或为扩展方法添加泛型约束。`Container` 结构体是个泛型,下面的例子通过 `where` 分句让新的方法声明其调用所需要满足的类型约束。
```swift
extension Container {
func average() -> Double where Item == Int {
var sum = 0.0
for index in 0..<count {
sum += Double(self[index])
}
return sum / Double(count)
}
func endsWith(_ item: Item) -> Bool where Item: Equatable {
return count >= 1 && self[count-1] == item
}
}
let numbers = [1260, 1200, 98, 37]
print(numbers.average())
// 输出 "648.75"
print(numbers.endsWith(37))
// 输出 "true"
```
例子中,当 `Item` 是整型时为 `Container` 添加 `average()` 方法,当 `Item` 遵循 `Equatable` 时添加 `endsWith(_:)` 方法。两个方法都通过 `where` 分句对 `Container` 中定义的泛型 `Item` 进行了约束。
如果不使用包含上下文关系的 `where` 分句,需要写两个扩展,并为每个扩展分别加上 `where` 分句。下面的例子和上面的具有相同效果。
```swift
extension Container where Item == Int {
func average() -> Double {
var sum = 0.0
for index in 0..<count {
sum += Double(self[index])
}
return sum / Double(count)
}
}
extension Container where Item: Equatable {
func endsWith(_ item: Item) -> Bool {
return count >= 1 && self[count-1] == item
}
}
```
在包含上下文关系的 `where` 分句的例子中,由于每个方法的 `where` 分句各自声明了需要满足的条件,因此 `average()``endsWith(_:)` 的实现能放在同一个扩展里。而将 `where` 分句放在扩展进行声明也能起到同样的效果,但每一个扩展只能有一个必备条件。
## 具有泛型 Where 子句的关联类型 {#associated-types-with-a-generic-where-clause}
你可以在关联类型后面加上具有泛型 `where`句。例如,建立一个包含迭代器(`Iterator`)的容器,就像是标准库中使用的 `Sequence` 协议那样。你应该这么写:
你可以在关联类型后面加上具有泛型 `where`句。例如,建立一个包含迭代器(`Iterator`)的容器,就像是标准库中使用的 `Sequence` 协议那样。你应该这么写:
```swift
protocol Container {

View File

@ -0,0 +1,249 @@
# 不透明类型
具有不透明返回类型的函数或方法会隐藏返回值的类型信息。函数不再提供具体的类型作为返回类型,而是根据它支持的协议来描述返回值。在处理模块和调用代码之间的关系时,隐藏类型信息非常有用,因为返回的底层数据类型仍然可以保持私有。而且不同于返回协议类型,不透明类型能保证类型一致性 —— 编译器能获取到类型信息,同时模块使用者却不能获取到。
## 不透明类型解决的问题 {#the-problem-that-opaque-types-solve}
举个例子,假设你正在写一个模块,用来绘制 ASCII 符号构成的几何图形。它的基本特征是有一个 `draw()` 方法,会返回一个代表最终几何图形的字符串,你可以用包含这个方法的 `Shape` 协议来描述:
```swift
protocol Shape {
func draw() -> String
}
struct Triangle: Shape {
var size: Int
func draw() -> String {
var result = [String]()
for length in 1...size {
result.append(String(repeating: "*", count: length))
}
return result.joined(separator: "\n")
}
}
let smallTriangle = Triangle(size: 3)
print(smallTriangle.draw())
// *
// **
// ***
```
你可以利用泛型来实现垂直翻转之类的操作,就像下面这样。然而,这种方式有一个很大的局限:翻转操作的结果会暴露我们用于构造结果的泛型类型:
```swift
struct FlippedShape<T: Shape>: Shape {
var shape: T
func draw() -> String {
let lines = shape.draw().split(separator: "\n")
return lines.reversed().joined(separator: "\n")
}
}
let flippedTriangle = FlippedShape(shape: smallTriangle)
print(flippedTriangle.draw())
// ***
// **
// *
```
如下方代码所示,用同样的方式定义了一个 `JoinedShape<T: Shape, U: Shape>` 结构体,能将几何图形垂直拼接起来。如果拼接一个翻转三角形和一个普通三角形,它就会得到类似于 `JoinedShape<FlippedShape<Triangle>, Triangle>` 这样的类型。
```swift
struct JoinedShape<T: Shape, U: Shape>: Shape {
var top: T
var bottom: U
func draw() -> String {
return top.draw() + "\n" + bottom.draw()
}
}
let joinedTriangles = JoinedShape(top: smallTriangle, bottom: flippedTriangle)
print(joinedTriangles.draw())
// *
// **
// ***
// ***
// **
// *
```
暴露构造所用的具体类型会造成类型信息的泄露,因为 ASCII 几何图形模块的部分公开接口必须声明完整的返回类型,而实际上这些类型信息并不应该被公开声明。输出同一种几何图形,模块内部可能有多种实现方式,而外部使用时,应该与内部各种变换顺序的实现逻辑无关。诸如 `JoinedShape``FlippedShape` 这样包装后的类型,模块使用者并不关心,它们也不应该可见。模块的公开接口应该由拼接、翻转等基础操作组成,这些操作也应该返回独立的 `Shape` 类型的值。
## 返回不透明类型 {#returning-an-opaque-type}
你可以认为不透明类型和泛型相反。泛型允许调用一个方法时,为这个方法的形参和返回值指定一个与实现无关的类型。举个例子,下面这个函数的返回值类型就由它的调用者决定:
```swift
func max<T>(_ x: T, _ y: T) -> T where T: Comparable { ... }
```
`x``y` 的值由调用 `max(_:_:)` 的代码决定,而它们的类型决定了 `T` 的具体类型。调用代码可以使用任何遵循了 `Comparable` 协议的类型,函数内部也要以一种通用的方式来写代码,才能应对调用者传入的各种类型。`max(_:_:)` 的实现就只使用了所有遵循 `Comparable` 协议的类型共有的特性。
而在返回不透明类型的函数中,上述角色发生了互换。不透明类型允许函数实现时,选择一个与调用代码无关的返回类型。比如,下面的例子返回了一个梯形,却没直接输出梯形的底层类型:
```swift
struct Square: Shape {
var size: Int
func draw() -> String {
let line = String(repeating: "*", count: size)
let result = Array<String>(repeating: line, count: size)
return result.joined(separator: "\n")
}
}
func makeTrapezoid() -> some Shape {
let top = Triangle(size: 2)
let middle = Square(size: 2)
let bottom = FlippedShape(shape: top)
let trapezoid = JoinedShape(
top: top,
bottom: JoinedShape(top: middle, bottom: bottom)
)
return trapezoid
}
let trapezoid = makeTrapezoid()
print(trapezoid.draw())
// *
// **
// **
// **
// **
// *
```
这个例子中,`makeTrapezoid()` 函数将返回值类型定义为 `some Shape`;因此,该函数返回遵循 `Shape` 协议的给定类型,而不需指定任何具体类型。这样写 `makeTrapezoid()` 函数可以表明它公共接口的基本性质 —— 返回的是一个几何图形 —— 而不是部分的公共接口生成的特殊类型。上述实现过程中使用了两个三角形和一个正方形,还可以用其他多种方式重写画梯形的函数,都不必改变返回类型。
这个例子凸显了不透明返回类型和泛型的相反之处。`makeTrapezoid()` 中代码可以返回任意它需要的类型,只要这个类型是遵循 `Shape` 协议的,就像调用泛型函数时可以使用任何需要的类型一样。这个函数的调用代码需要采用通用的方式,就像泛型函数的实现代码一样,这样才能让 `makeTrapezoid()` 返回的任何 `Shape` 类型的值都能被正常使用。
你也可以将不透明返回类型和泛型结合起来,下面的两个泛型函数也都返回了遵循 `Shape` 协议的不透明类型。
```swift
func flip<T: Shape>(_ shape: T) -> some Shape {
return FlippedShape(shape: shape)
}
func join<T: Shape, U: Shape>(_ top: T, _ bottom: U) -> some Shape {
JoinedShape(top: top, bottom: bottom)
}
let opaqueJoinedTriangles = join(smallTriangle, flip(smallTriangle))
print(opaqueJoinedTriangles.draw())
// *
// **
// ***
// ***
// **
// *
```
这个例子中 `opaqueJoinedTriangles` 的值和前文 [不透明类型解决的问题](#the-problem-that-opaque-types-solve) 中关于泛型的那个例子中的 `joinedTriangles` 完全一样。不过和前文不一样的是,`flip(-:)``join(-:-:)` 将对泛型参数的操作后的返回结果包装成了不透明类型,这样保证了在结果中泛型参数类型不可见。两个函数都是泛型函数,因为他们都依赖于泛型参数,而泛型参数又将 `FlippedShape``JoinedShape` 所需要的类型信息传递给它们。
如果函数中有多个地方返回了不透明类型,那么所有可能的返回值都必须是同一类型。即使对于泛型函数,不透明返回类型可以使用泛型参数,但仍需保证返回类型唯一。比如,下面就是一个*非法*示例 —— 包含针对 `Square` 类型进行特殊处理的翻转函数。
```swift
func invalidFlip<T: Shape>(_ shape: T) -> some Shape {
if shape is Square {
return shape // 错误:返回类型不一致
}
return FlippedShape(shape: shape) // 错误:返回类型不一致
}
```
如果你调用这个函数时传入一个 `Square` 类型,那么它会返回 `Square` 类型;否则,它会返回一个 `FlippedShape` 类型。这违反了返回值类型唯一的要求,所以 `invalidFlip(_:)` 不正确。修正 `invalidFlip(_:)` 的方法之一就是将针对 `Square` 的特殊处理移入到 `FlippedShape` 的实现中去,这样就能保证这个函数始终返回 `FlippedShape`
```swift
struct FlippedShape<T: Shape>: Shape {
var shape: T
func draw() -> String {
if shape is Square {
return shape.draw()
}
let lines = shape.draw().split(separator: "\n")
return lines.reversed().joined(separator: "\n")
}
}
```
返回类型始终唯一的要求,并不会影响在返回的不透明类型中使用泛型。比如下面的函数,就是在返回的底层类型中使用了泛型参数:
```swift
func `repeat`<T: Shape>(shape: T, count: Int) -> some Collection {
return Array<T>(repeating: shape, count: count)
}
```
这种情况下,返回的底层类型会根据 `T` 的不同而发生变化:但无论什么形状被传入,`repeat(shape:count:)` 都会创建并返回一个元素为相应形状的数组。尽管如此,返回值始终还是同样的底层类型 `[T]` 所以这符合不透明返回类型始终唯一的要求。
## 不透明类型和协议类型的区别 {#differences-between-opaque-types-and-protocol-types}
虽然使用不透明类型作为函数返回值,看起来和返回协议类型非常相似,但这两者有一个主要区别,就在于是否需要保证类型一致性。一个不透明类型只能对应一个具体的类型,即便函数调用者并不能知道是哪一种类型;协议类型可以同时对应多个类型,只要它们都遵循同一协议。总的来说,协议类型更具灵活性,底层类型可以存储更多样的值,而不透明类型对这些底层类型有更强的限定。
比如,这是 `flip(_:)` 方法不采用不透明类型,而采用返回协议类型的版本:
```swift
func protoFlip<T: Shape>(_ shape: T) -> Shape {
return FlippedShape(shape: shape)
}
```
这个版本的 `protoFlip(_:)``flip(_:)` 有相同的函数体,并且它也始终返回唯一类型。但不同于 `flip(_:)``protoFlip(_:)` 返回值其实不需要始终返回唯一类型 —— 返回类型只需要遵循 `Shape` 协议即可。换句话说,`protoFlip(_:)` 比起 `flip(_:)` 对 API 调用者的约束更加松散。它保留了返回多种不同类型的灵活性:
```swift
func protoFlip<T: Shape>(_ shape: T) -> Shape {
if shape is Square {
return shape
}
return FlippedShape(shape: shape)
}
```
修改后的代码根据代表形状的参数的不同,可能返回 `Square` 实例或者 `FlippedShape` 实例,所以同样的函数可能返回完全不同的两个类型。当翻转相同形状的多个实例时,此函数的其他有效版本也可能返回完全不同类型的结果。`protoFlip(_:)` 返回类型的不确定性,意味着很多依赖返回类型信息的操作也无法执行了。举个例子,这个函数的返回结果就不能用 == 运算符进行比较了。
```swift
let protoFlippedTriangle = protoFlip(smallTriangle)
let sameThing = protoFlip(smallTriangle)
protoFlippedTriangle == sameThing // 错误
```
上面的例子中,最后一行的错误来源于多个原因。最直接的问题在于,`Shape` 协议中并没有包含对 == 运算符的声明。如果你尝试加上这个声明,那么你会遇到新的问题,就是 == 运算符需要知道左右两侧参数的类型。这类运算符通常会使用 `Self` 类型作为参数,用来匹配符合协议的具体类型,但是由于将协议当成类型使用时会发生类型擦除,所以并不能给协议加上对 `Self` 的实现要求。
将协议类型作为函数的返回类型能更加灵活,函数只要返回遵循协议的类型即可。然而,更具灵活性导致牺牲了对返回值执行某些操作的能力。上面的例子就说明了为什么不能使用 == 运算符 —— 它依赖于具体的类型信息,而这正是使用协议类型所无法提供的。
这种方法的另一个问题在于,变换形状的操作不能嵌套。翻转三角形的结果是一个 `Shape` 类型的值,而 `protoFlip(_:)` 方法的则将遵循 `Shape` 协议的类型作为形参,然而协议类型的值并不遵循这个协议;`protoFlip(_:)` 的返回值也并不遵循 `Shape` 协议。这就是说 `protoFlip(protoFlip(smallTriange))` 这样的多重变换操作是非法的,因为经过翻转操作后的结果类型并不能作为 `protoFlip(_:)` 的形参。
相比之下不透明类型则保留了底层类型的唯一性。Swift 能够推断出关联类型,这个特点使得作为函数返回值,不透明类型比协议类型有更大的使用场景。比如,下面这个例子是 [泛型](./22_Generics.md) 中讲到的 `Container` 协议:
```swift
protocol Container {
associatedtype Item
var count: Int { get }
subscript(i: Int) -> Item { get }
}
extension Array: Container { }
```
你不能将 `Container` 作为方法的返回类型,因为此协议有一个关联类型。你也不能将它用于对泛型返回类型的约束,因为函数体之外并没有暴露足够多的信息来推断泛型类型。
```swift
// 错误:有关联类型的协议不能作为返回类型。
func makeProtocolContainer<T>(item: T) -> Container {
return [item]
}
// 错误:没有足够多的信息来推断 C 的类型。
func makeProtocolContainer<T, C: Container>(item: T) -> C {
return [item]
}
```
而使用不透明类型 `some Container` 作为返回类型,就能够明确地表达所需要的 API 契约 —— 函数会返回一个集合类型,但并不指明它的具体类型:
```swift
func makeOpaqueContainer<T>(item: T) -> some Container {
return [item]
}
let opaqueContainer = makeOpaqueContainer(item: 12)
let twelve = opaqueContainer[0]
print(type(of: twelve))
// 输出 "Int"
```
`twelve` 的类型可以被推断出为 `Int` 这说明了类型推断适用于不透明类型。在 `makeOpaqueContainer(item:)` 的实现中,底层类型是不透明集合 `[T]`。在上述这种情况下,`T` 就是 `Int` 类型,所以返回值就是整数数组,而关联类型 `Item` 也被推断出为 `Int``Container` 协议中的 `subscipt` 方法会返回 `Item`,这也意味着 `twelve` 的类型也被能推断出为 `Int`

View File

@ -2,7 +2,7 @@
Swift 使用*自动引用计数ARC*机制来跟踪和管理你的应用程序的内存。通常情况下Swift 内存管理机制会一直起作用你无须自己来考虑内存的管理。ARC 会在类的实例不再被使用时,自动释放其占用的内存。
然而在少数情况下为了能帮助你管理内存ARC 需要更多的,代码之间关系的信息。本章描述了这些情况,并且为你示范怎样才能使 ARC 来管理你的应用程序的所有内存。在 Swift 使用 ARC 与在 Obejctive-C 中使用 ARC 非常类似,具体请参考[过渡到 ARC 的发布说明](https://developer.apple.com/library/content/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011226)
然而在少数情况下为了能帮助你管理内存ARC 需要更多的,代码之间关系的信息。本章描述了这些情况,并且为你示范怎样才能使 ARC 来管理你的应用程序的所有内存。在 Swift 使用 ARC 与在 Obejctive-C 中使用 ARC 非常类似,具体请参考 [过渡到 ARC 的发布说明](https://developer.apple.com/library/content/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html#//apple-ref/doc/uid/TP40011226)
> 注意
>
@ -10,11 +10,11 @@ Swift 使用*自动引用计数ARC*机制来跟踪和管理你的应用程
## 自动引用计数的工作机制 {#how-arc-works}
当你每次创建一个类的新的实例的时候ARC 会分配一块内存来储存该实例信息。内存中会包含实例的类型信息,以及这个实例所有相关的存储属性的值。
当你创建一个新的实例ARC 会分配一块内存来储存该实例信息。内存中会包含实例的类型信息,以及这个实例所关联的任何存储属性的值。
此外当实例不再被使用时ARC 释放实例所占用的内存,并让释放的内存能挪作他用。这确保了不再被使用的实例,不会一直占用内存空间。
然而,当 ARC 收回和释放了正在被使用中的实例,该实例的属性和方法将不能再被访问和调用。实际上,如果你试图访问这个实例,你的应用程序很可能会崩溃。
然而,当 ARC 回收并释放了正在被使用中的实例,该实例的属性和方法将不能再被访问和调用。实际上,如果你试图访问这个实例,你的应用程序很可能会崩溃。
为了确保使用中的实例不会被销毁ARC 会跟踪和计算每一个实例正在被多少属性,常量和变量所引用。哪怕实例的引用数为 1ARC 都不会销毁这个实例。
@ -37,9 +37,9 @@ class Person {
}
```
`Person` 类有一个构造器,此构造器实例的 `name` 属性赋值,并打印一条消息以表明初始化过程生效。`Person` 类也拥有一个析构器,这个析构器会在实例被销毁时打印一条消息。
`Person` 类有一个构造器,此构造器实例的 `name` 属性赋值,并打印一条消息以表明初始化过程生效。`Person` 类也拥有一个析构器,这个析构器会在实例被销毁时打印一条消息。
接下来的代码片段定义了三个类型为 `Person?` 的变量,用来按照代码片段中的顺序,为新的 `Person` 实例建立多个引用。由于这些变量是被定义为可选类型(`Person?`,而不是 `Person`),它们的值会被自动初始化为 `nil`,目前还不会引用到 `Person` 类的实例。
接下来的代码片段定义了三个类型为 `Person?` 的变量,按照代码片段中的顺序,为新的 `Person` 实例建立多个引用。由于这些变量是被定义为可选类型(`Person?`,而不是 `Person`),它们的值会被自动初始化为 `nil`,目前还不会引用到 `Person` 类的实例。
```swift
var reference1: Person?
@ -87,7 +87,7 @@ reference3 = nil
然而,我们可能会写出一个类实例的强引用数*永远不能*变成 `0` 的代码。如果两个类实例互相持有对方的强引用,因而每个实例都让对方一直存在,就是这种情况。这就是所谓的*循环强引用*。
你可以通过定义类之间的关系为弱引用或无主引用,替代强引用,从而解决循环强引用的问题。具体的过程在[解决类实例之间的循环强引用](#resolving_strong_reference_cycles_between_class_instances)中有描述。不管怎样,在你学习怎样解决循环强引用之前,很有必要了解一下它是怎样产生的。
你可以通过定义类之间的关系为弱引用或无主引用,替代强引用,从而解决循环强引用的问题。具体的过程在 [解决类实例之间的循环强引用](#resolving-strong-reference-cycles-between-class-instances) 中有描述。不管怎样,在你学习怎样解决循环强引用之前,很有必要了解一下它是怎样产生的。
下面展示了一个不经意产生循环强引用的例子。例子定义了两个类:`Person``Apartment`,用来建模公寓和它其中的居民:
@ -111,9 +111,9 @@ class Apartment {
类似的,每个 `Apartment` 实例有一个叫 `unit`,类型为 `String` 的属性,并有一个可选的初始化为 `nil``tenant` 属性。`tenant` 属性是可选的,因为一栋公寓并不总是有居民。
这两个类都定义了析构器,用以在类实例被析构的时候输出信息。这让你能够知晓 `Person``Apartment` 的实例是否像预期的那样被销毁。
这两个类都定义了析构器,在类实例被析构的时候输出信息。这让你能够知晓 `Person``Apartment` 的实例是否像预期的那样被销毁。
接下来的代码片段定义了两个可选类型的变量 `john``unit4A`,并分别被设为下面的 `Apartment``Person` 的实例。这两个变量都被初始化为 `nil`,这正是可选类型的优点:
接下来的代码片段定义了两个可选类型的变量 `john``unit4A`,并分别被设为下面的 `Apartment``Person` 的实例。这两个变量都被初始化为 `nil`,这正是可选类型的优点:
```swift
var john: Person?
@ -131,7 +131,7 @@ unit4A = Apartment(unit: "4A")
![](https://docs.swift.org/swift-book/_images/referenceCycle01_2x.png)
现在你能够将这两个实例关联在一起,这样人就能有公寓住了,而公寓也有了房客。注意感叹号是用来展开和访问可选变量 `john``unit4A` 中的实例,这样实例的属性才能被赋值:
现在你能够将这两个实例关联在一起,这样人就能有公寓住了,而公寓也有了房客。注意感叹号是用来解包和访问可选变量 `john``unit4A` 中的实例,这样实例的属性才能被赋值:
```swift
john!.apartment = unit4A
@ -171,7 +171,7 @@ Swift 提供了两种办法用来解决你在使用类的属性时所遇到的
因为弱引用不会保持所引用的实例即使引用存在实例也有可能被销毁。因此ARC 会在引用的实例被销毁后自动将其弱引用赋值为 `nil`。并且因为弱引用需要在运行时允许被赋值为 `nil`,所以它们会被定义为可选类型变量,而不是常量。
你可以像其他可选值一样,检查弱引用的值是否存在,你将永远不会访问已销毁的实例的引用。
你可以像其他可选值一样,检查弱引用的值是否存在,这样可以避免访问已销毁的实例的引用。
> 注意
>
@ -230,7 +230,7 @@ unit4A = nil
// 打印“Apartment 4A is being deinitialized”
```
由于再也没有指向 `Person` 实例的强引用,该实例会被销毁:
由于再也没有指向 `Apartment` 实例的强引用,该实例会被销毁:
![](https://docs.swift.org/swift-book/_images/weakReference03_2x.png)
@ -331,7 +331,7 @@ john = nil
然而,存在着第三种场景,在这种场景中,两个属性都必须有值,并且初始化完成后永远不会为 `nil`。在这种场景中,需要一个类使用无主属性,而另外一个类使用隐式解包可选值属性。
这使两个属性在初始化完成后能被直接访问(不需要可选展开),同时避免了循环引用。这一节将为你展示如何建立这种关系。
这使两个属性在初始化完成后能被直接访问(不需要可选解包),同时避免了循环引用。这一节将为你展示如何建立这种关系。
下面的例子定义了两个类,`Country``City`,每个类将另外一个类的实例保存为属性。在这个模型中,每个国家必须有首都,每个城市必须属于一个国家。为了实现这种关系,`Country` 类拥有一个 `capitalCity` 属性,而 `City` 类有一个 `country` 属性:
@ -357,13 +357,13 @@ class City {
为了建立两个类的依赖关系,`City` 的构造器接受一个 `Country` 实例作为参数,并且将实例保存到 `country` 属性。
`Country` 的构造器调用了 `City` 的构造器。然而,只有 `Country` 的实例完全初始化后,`Country` 的构造器才能把 `self` 传给 `City` 的构造器。在[两段式构造过程](./14_Initialization.md#two_phase_initialization)中有具体描述。
`Country` 的构造器调用了 `City` 的构造器。然而,只有 `Country` 的实例完全初始化后,`Country` 的构造器才能把 `self` 传给 `City` 的构造器。在 [两段式构造过程](./14_Initialization.md#two-phase-initialization) 中有具体描述。
为了满足这种需求,通过在类型结尾处加上感叹号(`City!`)的方式,将 `Country``capitalCity` 属性声明为隐式解包可选值类型的属性。这意味着像其他可选类型一样,`capitalCity` 属性的默认值为 `nil`,但是不需要展开它的值就能访问它。在[隐式解包可选值](./01_The_Basics.md#implicityly_unwrapped_optionals)中有描述。
为了满足这种需求,通过在类型结尾处加上感叹号(`City!`)的方式,将 `Country``capitalCity` 属性声明为隐式解包可选值类型的属性。这意味着像其他可选类型一样,`capitalCity` 属性的默认值为 `nil`,但是不需要解包它的值就能访问它。在 [隐式解包可选值](./01_The_Basics.md#implicityly-unwrapped-optionals) 中有描述。
由于 `capitalCity` 默认值为 `nil`,一旦 `Country` 的实例在构造器中给 `name` 属性赋值后,整个初始化过程就完成了。这意味着一旦 `name` 属性被赋值后,`Country` 的构造器就能引用并传递隐式的 `self``Country` 的构造器在赋值 `capitalCity` 时,就能将 `self` 作为参数传递给 `City` 的构造器。
上的意义在于你可以通过一条语句同时创建 `Country``City` 的实例,而不产生循环强引用,并且 `capitalCity` 的属性能被直接访问,而不需要通过感叹号来展开它的可选值:
的意义在于你可以通过一条语句同时创建 `Country``City` 的实例,而不产生循环强引用,并且 `capitalCity` 的属性能被直接访问,而不需要通过感叹号来解包它的可选值:
```swift
var country = Country(name: "Canada", capitalName: "Ottawa")
@ -453,7 +453,7 @@ print(paragraph!.asHTML())
![](https://docs.swift.org/swift-book/_images/closureReferenceCycle01_2x.png)
实例的 `asHTML` 属性持有闭包的强引用。但是,闭包在其闭包体内使用了 `self`(引用了 `self.name``self.text`),因此闭包捕获了 `self`,这意味着闭包又反过来持有了 `HTMLElement` 实例的强引用。这样两个对象就产生了循环强引用。(更多关于闭包捕获值的信息,请参考[值捕获](./07_Closures.md#capturing_values))。
实例的 `asHTML` 属性持有闭包的强引用。但是,闭包在其闭包体内使用了 `self`(引用了 `self.name``self.text`),因此闭包捕获了 `self`,这意味着闭包又反过来持有了 `HTMLElement` 实例的强引用。这样两个对象就产生了循环强引用。(更多关于闭包捕获值的信息,请参考 [值捕获](./07_Closures.md#capturing-values))。
> 注意
>
@ -477,13 +477,14 @@ paragraph = nil
### 定义捕获列表 {#defining-a-capture-list}
捕获列表中的每一项都由一对元素组成,一个元素是 `weak``unowned` 关键字,另一个元素是类实例的引用(例如 `self`)或初始化过的变量(如 `delegate = self.delegate!`)。这些项在方括号中用逗号分开。
捕获列表中的每一项都由一对元素组成,一个元素是 `weak``unowned` 关键字,另一个元素是类实例的引用(例如 `self`)或初始化过的变量(如 `delegate = self.delegate`)。这些项在方括号中用逗号分开。
如果闭包有参数列表和返回类型,把捕获列表放在它们前面:
```swift
lazy var someClosure: (Int, String) -> String = {
[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
lazy var someClosure = {
[unowned self, weak delegate = self.delegate]
(index: Int, stringToProcess: String) -> String in
// 这里是闭包的函数体
}
```
@ -491,8 +492,8 @@ lazy var someClosure: (Int, String) -> String = {
如果闭包没有指明参数列表或者返回类型,它们会通过上下文推断,那么可以把捕获列表和关键字 `in` 放在闭包最开始的地方:
```swift
lazy var someClosure: () -> String = {
[unowned self, weak delegate = self.delegate!] in
lazy var someClosure = {
[unowned self, weak delegate = self.delegate] in
// 这里是闭包的函数体
}
```
@ -557,4 +558,4 @@ paragraph = nil
// 打印“p is being deinitialized”
```
你可以查看[捕获列表](../chapter3/04_Expressions.html)章节,获取更多关于捕获列表的信息。
你可以查看 [捕获列表](../03_language_reference/04_Expressions.md) 章节,获取更多关于捕获列表的信息。

View File

@ -30,7 +30,7 @@ print("We're number \(one)!")
>
> 如果你写过并发和多线程的代码,内存访问冲突也许是同样的问题。然而,这里访问冲突的讨论是在单线程的情境下讨论的,并没有使用并发或者多线程。
>
> 如果你曾经在单线程代码里有访问冲突Swift 可以保证你在编译或者运行时会得到错误。对于多线程的代码,可以使用 [Thread Sanitizer](https://developer.apple.com/documentation/code_diagnostics/thread_sanitizer) 去帮助检测多线程的冲突。
> 如果你曾经在单线程代码里有访问冲突Swift 可以保证你在编译或者运行时会得到错误。对于多线程的代码,可以使用 [Thread Sanitizer](https://developer.apple.com/documentation/xcode/diagnosing_memory_thread_and_crash_issues_early) 去帮助检测多线程的冲突。
### 内存访问性质 {#characteristics-of-memory-access}

View File

@ -1,14 +1,14 @@
# 访问控制
*访问控制*可以限定其它源文件或模块中的代码对你的代码的访问级别。这个特性可以让我们隐藏代码的一些实现细节,并且可以为其他人可以访问和使用的代码提供接口
*访问控制*可以限定其它源文件或模块对你的代码的访问。这个特性可以让隐藏代码的实现细节,并且能提供一个接口来让别人访问和使用的代码。
你可以明确地给单个类型(类、结构体、枚举)设置访问级别,也可以给这些类型的属性、方法、构造器、下标等设置访问级别。协议也可以被限定在一定的范围内使用,包括协议里的全局常量、变量和函数。
你可以明确地给单个类型(类、结构体、枚举)设置访问级别,也可以给这些类型的属性、方法、构造器、下标等设置访问级别。协议也可以被限定在一定访问级别的范围内使用,包括协议里的全局常量、变量和函数。
Swift 不仅提供了多种不同的访问级别,还为某些典型场景提供了默认的访问级别,这样就不需要我们在每段代码中都申明显式访问级别。其实,如果只是开发一个单 target 的应用程序,我们完全可以不用显式声明代码的访问级别。
Swift 不仅提供了多种不同的访问级别,还为某些典型场景提供了默认的访问级别,这样就不需要我们在每段代码中都显式声明访问级别。如果只是开发一个单 target 的应用程序,完全可以不用显式声明代码的访问级别。
> 注意
>
> 为了简单起见,对于代码中可以设置访问级别的特性(属性、基本类型、函数等),在下面的章节中我们会称之为“实体”。
> 为了简单起见,对于代码中可以设置访问级别的特性(属性、基本类型、函数等),在下面的章节中我们会统一称之为“实体”。
## 模块和源文件 {#modules-and-source-files}
@ -16,56 +16,49 @@ Swift 中的访问控制模型基于模块和源文件这两个概念。
*模块*指的是独立的代码单元,框架或应用程序会作为一个独立的模块来构建和发布。在 Swift 中,一个模块可以使用 `import` 关键字导入另外一个模块。
在 Swift 中Xcode 的每个 target例如框架或应用程序都被当作独立的模块处理。如果你是为了实现某个通用的功能或者是为了封装一些常用方法而将代码打包成独立的框架这个框架就是 Swift 中的一个模块。当它被导入到某个应用程序或者其他框架时,框架内容都将属于这个独立的模块。
在 Swift 中Xcode 的每个 target例如框架或应用程序都被当作独立的模块处理。如果你是为了实现某个通用的功能或者是为了封装一些常用方法而将代码打包成独立的框架这个框架就是 Swift 中的一个模块。当它被导入到某个应用程序或者其他框架时,框架内容都将属于这个独立的模块。
*源文件*就是 Swift 中的源代码文件,它通常属于一个模块,即一个应用程序或框架。尽管我们一般会将不同的类型分别定义在不同的源文件中,但是同一个源文件也可以包含多个类型、函数之类的定义。
*源文件* 就是 Swift 模块中的源代码文件(实际上,源文件属于一个应用程序或框架。尽管我们一般会将不同的类型分别定义在不同的源文件中,但是同一个源文件也可以包含多个类型、函数的定义。
## 访问级别 {#access-levels}
Swift 为代码中的实体提供了五种不同的*访问级别*。这些访问级别不仅与源文件中定义的实体相关,同时也与源文件所属的模块相关。
- *Open**Public* 级别可以让实体被同一模块源文件中的所有实体访问,在模块外也可以通过导入该模块来访问源文件里的所有实体。通常情况下,你会使用 Open 或 Public 级别来指定框架的外部接口。Open 和 Public 的区别在后面会提到。
- *Internal* 级别让实体被同一模块源文件中的任何实体访问,但是不能被模块外的实体访问。通常情况下,如果某个接口只在应用程序或框架内部使用,就可以将其设置为 Internal 级别。
- *File-private* 限制实体只能在其定义的文件内部访问。如果功能的部分细节只需要在文件内使用时,可以使用 File-private 来将其隐藏。
- *Private* 限制实体只能在其定义的作用域,以及同一文件内的 extension 访问。如果功能的部分细节只需要在当前作用域内使用时,可以使用 Private 来将其隐藏。
- *open**public* 级别可以让实体被同一模块源文件中的所有实体访问,在模块外也可以通过导入该模块来访问源文件里的所有实体。通常情况下,你会使用 open 或 public 级别来指定框架的外部接口。open 和 public 的区别在后面会提到。
- *internal* 级别让实体被同一模块源文件中的任何实体访问,但是不能被模块外的实体访问。通常情况下,如果某个接口只在应用程序或框架内部使用,就可以将其设置为 internal 级别。
- *fileprivate* 限制实体只能在其定义的文件内部访问。如果功能的部分实现细节只需要在文件内使用时,可以使用 fileprivate 来将其隐藏。
- *private* 限制实体只能在其定义的作用域,以及同一文件内的 extension 访问。如果功能的部分细节只需要在当前作用域内使用时,可以使用 private 来将其隐藏。
Open 为最高访问级别(限制最少),Private 为最低访问级别(限制最多)。
open 为最高访问级别(限制最少),private 为最低访问级别(限制最多)。
Open 只能作用于类和类的成员,它和 Public 的区别如下:
* Public 或者其它更严访问级别的类,只能在其定义的模块内部被继承。
* Public 或者其它更严访问级别的类成员,只能在其定义的模块内部的子类中重写。
* Open 的类,可以在其定义的模块中被继承,也可以在引用它的模块中被继承。
* Open 的类成员,可以在其定义的模块中子类中重写,也可以在引用它的模块中的子类重写。
把一个类标记为 `open`,明确的表示你已经充分考虑过外部模块使用此类作为父类的影响,并且设计好了你的类的代码了。
open 只能作用于类和类的成员,它和 public 的区别主要在于 open 限定的类和成员能够在模块外能被继承和重写,在下面的 [子类](#subclassing) 这一节中有详解。将类的访问级别显式指定为 `open` 表明你已经设计好了类的代码,并且充分考虑过这个类在其他模块中用作父类时的影响。
### 访问级别基本原则 {#guiding-principle-of-access-levels}
Swift 中的访问级别遵循一个基本原则:*不可以在某个实体中定义访问级别更低(更严格)的实体*。
Swift 中的访问级别遵循一个基本原则:*实体不能定义在具有更低访问级别(更严格)的实体*。
例如:
- 一个 Public 的变量,其类型的访问级别不能是 InternalFile-private 或是 Private。因为无法保证变量的类型在使用变量的地方也具有访问权限。
- 一个 public 的变量,其类型的访问级别不能是 internalfileprivate 或是 private。因为无法保证变量的类型在使用变量的地方也具有访问权限。
- 函数的访问级别不能高于它的参数类型和返回类型的访问级别。因为这样就会出现函数可以在任何地方被访问,但是它的参数类型和返回类型却不可以的情况。
关于此原则在各种情况下的具体表现,将在下文有所体现。
### 默认访问级别 {#default-access-levels}
如果你没有为代码中的实体显式指定访问级别,那么它们默认为 `internal` 级别(有一些例外情况,稍后会进行说明)。因此,在大多数情况下,我们不需要显指定实体的访问级别。
代码中所有的实体,如果你不显式指定它们的访问级别,那么它们将都有一个 `internal` 的默认访问级别(有一些例外情况,本文稍后会说明)。因此,多数情况下不需要显指定实体的访问级别。
### 单 target 应用程序的访问级别 {#access-levels-for-single-target-apps}
当你编写一个单目标应用程序时,应用的所有功能都是为该应用服务,而不需要提供给其他应用或者模块使用,所以我们不需要明确设置访问级别,使用默认的访问级别 Internal 即可。但是,你也可以使用 `fileprivate` 访问`private` 访问级别,用于隐藏一些功能的实现细节。
当你编写一个单 target 应用程序时,应用的所有功能都是为该应用服务,而不需要提供给其他应用或者模块使用,所以不需要明确设置访问级别,使用默认的访问级别 internal 即可。但是,你也可以使用 `fileprivate``private` 访问级别,用于隐藏一些功能的实现细节。
### 框架的访问级别 {#access-levels-for-frameworks}
当你开发框架时,就需要把一些对外的接口定义为 Open 或 Public以便使用者导入该框架后可以正常使用其功能。这些被你定义为对外的接口就是这个框架的 API。
当你开发框架时,就需要把一些对外的接口定义为 open 或 public 访问级别,以便使用者导入该框架后可以正常使用其功能。这些被你定义为对外的接口,就是这个框架的 API。
> 注意
>
> 框架依然会使用默认的 `internal` ,也可以指定为 `fileprivate` 访问或者 `private` 访问级别。当你想把某个实体作为框架的 API 的时候,需显式为其指定开放访问或公开访问级别
> 框架的内部实现仍然可以使用默认的访问级别 `internal`,当你需要对框架内部其它部分隐藏细节时可以使用 `private` `fileprivate`。对于框架的对外 API 部分,你就需要将它们设置为 `open``public`
### 单元测试 target 的访问级别 {#access-levels-for-unit-test-targets}
@ -87,7 +80,7 @@ fileprivate func someFilePrivateFunction() {}
private func somePrivateFunction() {}
```
除非专门指定,否则实体默认的访问级别为 `internal`,可以查阅[默认访问级别](#default_access_levels)这一节。这意味着在不使用修饰符显式声明访问级别的情况下,`SomeInternalClass``someInternalConstant` 仍然拥有隐式`internal`
除非专门指定,否则实体默认的访问级别为 `internal`,可以查阅 [默认访问级别](#default-access-levels) 这一节。这意味着在不使用修饰符显式声明访问级别的情况下,`SomeInternalClass``someInternalConstant`访问级别是 `internal`
```swift
class SomeInternalClass {} // 隐式 internal
@ -96,9 +89,9 @@ var someInternalConstant = 0 // 隐式 internal
## 自定义类型 {#custom-types}
如果想为一个自定义类型指定访问级别,在定义类型时进行指定即可。新类型只能在它的访问级别限制范围内使用。例如,你定义了一个 `fileprivate` 级别的类,那这个类就只能在定义它的源文件中使用,可以作为属性类型、函数参数类型或者返回类型等等。
如果想为一个自定义类型指定访问级别,在定义类型时进行指定即可。新类型只能在它的访问级别限制范围内使用。例如,你定义了一个 `fileprivate` 级别的类,那这个类就只能在定义它的源文件中使用,可以作为属性类型、函数参数类型或者返回类型等等。
一个类型的访问级别也会影响到类型*成员*(属性、方法、构造器、下标)的默认访问级别。如果你将类型指定为 `private` 或者 `fileprivate` 级别,那么该类型的所有成员的默认访问级别也会变成 `private` 或者 `fileprivate` 级别。如果你将类型指定为公开或者 `internal` (或者不明确指定访问级别,而使用默认的 `internal` ),那么该类型的所有成员的默认访问级别将是内部访问
一个类型的访问级别也会影响到类型*成员*(属性、方法、构造器、下标)的默认访问级别。如果你将类型指定为 `private` 或者 `fileprivate` 级别,那么该类型的所有成员的默认访问级别也会变成 `private` 或者 `fileprivate` 级别。如果你将类型指定为 `internal` `public`(或者不明确指定访问级别,而使用默认的 `internal` ),那么该类型的所有成员的默认访问级别将是 `internal`
> 重点
>
@ -127,13 +120,15 @@ private class SomePrivateClass { // 显式 private 类
func somePrivateMethod() {} // 隐式 private 类成员
}
```
### 元组类型 {#tuple-types}
元组的访问级别将由元组中访问级别最严格的类型来决定。例如,如果你构建了一个包含两种不同类型的元组,其中一个类型为 `internal`,另一个类型为 `private`,那么这个元组的访问级别为 `private`
> 注意
>
> 元组不同于类、结构体、枚举、函数那样有单独的定义。元组的访问级别是在它被使用时自动推断出的,而无法明确指定。
> 元组不同于类、结构体、枚举、函数那样有单独的定义。一个元组的访问级别由元组中元素的访问级别来决定的,不能被显示指定。
### 函数类型 {#function-types}
@ -147,9 +142,9 @@ func someFunction() -> (SomeInternalClass, SomePrivateClass) {
}
```
我们可以看到,这个函数的返回类型是一个元组,该元组中包含两个自定义的类(可查阅[自定义类型](#custom_types))。其中一个类的访问级别是 `internal`,另一个的访问级别是 `private`,所以根据元组访问级别的原则,该元组的访问级别是 `private`(元组的访问级别与元组中访问级别最低的类型一致)。
我们可以看到,这个函数的返回类型是一个元组,该元组中包含两个自定义的类(可查阅 [自定义类型](#custom-types))。其中一个类的访问级别是 `internal`,另一个的访问级别是 `private`,所以根据元组访问级别的原则,该元组的访问级别是 `private`(元组的访问级别与元组中访问级别最低的类型一致)。
因为该函数返回类型的访问级别是 `private`,所以你必须使用 `private` 修饰符明确指定该函数的访问级别:
因为该函数返回类型的访问级别是 `private`,所以你必须使用 `private` 修饰符明确指定该函数的访问级别:
```swift
private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
@ -163,7 +158,7 @@ private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
枚举成员的访问级别和该枚举类型相同,你不能为枚举成员单独指定不同的访问级别。
比如下面的例子,枚举 `CompassPoint` 被明确指定为 `public`,那么它的成员 `North``South``East``West` 的访问级别同样也是 `public`
比如下面的例子,枚举 `CompassPoint` 被明确指定为 `public`,那么它的成员 `north``south``east``west` 的访问级别同样也是 `public`
```swift
public enum CompassPoint {
@ -180,15 +175,15 @@ public enum CompassPoint {
### 嵌套类型 {#nested-types}
如果在 `private` 的类型中定义嵌套类型,那么该嵌套类型就自动拥有 `private` 访问级别。如果在 `public` 或者 `internal` 级别的类型中定义嵌套类型,那么嵌套类型自动拥有 `internal` 访问级别。如果想让嵌套类型拥有 `public` 访问级别,那么需要明确指定该嵌套类型的访问级别。
嵌套类型的访问级别和包含它的类型的访问级别相同,嵌套类型是 public 的情况除外。在一个 public 的类型中定义嵌套类型,那么嵌套类型自动拥有 `internal` 访问级别。如果想让嵌套类型拥有 `public` 访问级别,那么必须显式指定该嵌套类型的访问级别为 public
## 子类 {#subclassing}
子类的访问级别不得高于父类的访问级别。例如,父类的访问级别是 `internal`,子类的访问级别就不能是 `public`
你可以继承同一模块中的所有有访问权限的类,也可以继承不同模块中被 open 修饰的类。一个子类的访问级别不得高于父类的访问级别。例如,父类的访问级别是 `internal`,子类的访问级别就不能是 `public`
此外,你可以在符合当前访问级别的条件下重写任意类成员(方法、属性、构造器、下标等)。
此外,在同一模块中,你可以在符合当前访问级别的条件下重写任意类成员(方法、属性、构造器、下标等)。在不同模块中,你可以重写类中被 open 修饰的成员。
可以通过重写为继承来的类成员提供更高的访问级别。下面的例子中,类 `A` 的访问级别是 `public`,它包含一个方法 `someMethod()`,访问级别为 `private`。类 `B` 继承自类 `A`,访问级别为 `internal`,但是在类 `B` 中重写了类 `A` 中访问级别为 `private` 的方法 `someMethod()`,并重新指定为 `internal` 级别。通过这种方式,我们就可以将某类中 `private` 级别的类成员重新指定为更高的访问级别,以便其他人使用:
可以通过重写给所继承类的成员提供更高的访问级别。下面的例子中,类 `A` 的访问级别是 `public`,它包含一个方法 `someMethod()`,访问级别为 `fileprivate`。类 `B` 继承自类 `A`,访问级别为 `internal`,但是在类 `B` 中重写了类 `A` 中访问级别为 `fileprivate` 的方法 `someMethod()`,并重新指定为 `internal` 级别。通过这种方式,我们就可以将某类中 `fileprivate` 级别的类成员重新指定为更高的访问级别,以便其他人使用:
```swift
public class A {
@ -200,7 +195,7 @@ internal class B: A {
}
```
我们甚至可以在子类中,用子类成员去访问访问级别更低的父类成员,只要这一操作在相应访问级别的限制范围内(也就是说,在同一源文件中访问父类 `private` 级别的成员,在同一模块内访问父类 `internal` 级别的成员):
我们甚至可以在子类中,用子类成员去访问访问级别更低的父类成员,只要这一操作在相应访问级别的限制范围内(也就是说,在同一源文件中访问父类 `fileprivate` 级别的成员,在同一模块内访问父类 `internal` 级别的成员):
```swift
public class A {
@ -234,7 +229,7 @@ private var privateInstance = SomePrivateClass()
> 注意
>
> 这个规则同时适用于存储型属性和计算型属性。即使你不明确指定存储型属性的 `Getter``Setter`Swift 也会隐式地为其创建 `Getter``Setter`,用于访问该属性的后备存储。使用 `fileprivate(set)``private(set)``internal(set)` 可以改变 `Setter` 的访问级别,这对计算型属性也同样适用。
> 这个规则同时适用于存储型属性和计算型属性。即使你不明确指定存储型属性的 `Getter``Setter`Swift 也会隐式地为其创建 `Getter``Setter`,用于访问该属性的存储内容。使用 `fileprivate(set)``private(set)``internal(set)` 可以改变 `Setter` 的访问级别,这对计算型属性也同样适用。
下面的例子中定义了一个名为 `TrackedString` 的结构体,它记录了 `value` 属性被修改的次数:
@ -282,13 +277,13 @@ public struct TrackedString {
## 构造器 {#initializers}
自定义构造器的访问级别可以低于或等于其所属类型的访问级别。唯一的例外是[必要构造器](./14_Initialization.md#required_initializers),它的访问级别必须和所属类型的访问级别相同。
自定义构造器的访问级别可以低于或等于其所属类型的访问级别。唯一的例外是 [必要构造器](./14_Initialization.md#required-initializers),它的访问级别必须和所属类型的访问级别相同。
如同函数或方法的参数,构造器参数的访问级别也不能低于构造器本身的访问级别。
### 默认构造器 {#default-initializers}
如[默认构造器](./14_Initialization.md#default_initializers)所述Swift 会为结构体和类提供一个默认的无参数的构造器,只要它们为所有存储型属性设置了默认初始值,并且未提供自定义的构造器。
[默认构造器](./14_Initialization.md#default-initializers) 所述Swift 会为结构体和类提供一个默认的无参数的构造器,只要它们为所有存储型属性设置了默认初始值,并且未提供自定义的构造器。
默认构造器的访问级别与所属类型的访问级别相同,除非类型的访问级别是 `public`。如果一个类型被指定为 `public` 级别,那么默认构造器的访问级别将为 `internal`。如果你希望一个 `public` 级别的类型也能在其他模块中使用这种无参数的默认构造器,你只能自己提供一个 `public` 访问级别的无参数构造器。
@ -300,17 +295,17 @@ public struct TrackedString {
## 协议 {#protocols}
如果想为一个协议类型明确地指定访问级别,在定义协议时指定即可。这将限制该协议只能在适当的访问级别范围内被遵循。
如果想为一个协议类型明确地指定访问级别,在声明协议时指定即可。这将限制该协议只能在适当的访问级别范围内被遵循。
协议中的每一个要求都具有和该协议相同的访问级别。你不能将协议中的要求设置为其他访问级别。这样才能确保该协议的所有要求对于任意遵循者都可用。
协议中的每个方法或属性都必须具有和该协议相同的访问级别。你不能将协议中的方法或属性设置为其他访问级别。这样才能确保该协议的所有方法或属性对于任意遵循者都可用。
> 注意
>
> 如果你定义了一个 `public` 访问级别的协议,那么该协议的所有实现也会是 `public` 访问级别。这一点不同于其他类型,例如,类型是 `public` 访问级别时,其成员的访问级别却只是 `internal`
> 如果你定义了一个 `public` 访问级别的协议,那么该协议的所有实现也会是 `public` 访问级别。这一点不同于其他类型,例如,类型是 `public` 访问级别时,其成员的访问级别却只是 `internal`
### 协议继承 {#protocol-inheritance}
如果定义了一个继承自其他协议的新协议,那么新协议拥有的访问级别最高也只能和被继承协议的访问级别相同。例如,你不能将继承自 `internal` 协议的新协议定`public` 协议。
如果定义了一个继承自其他协议的新协议,那么新协议拥有的访问级别最高也只能和被继承协议的访问级别相同。例如,你不能将继承自 `internal` 协议的新协议访问级别指定为 `public` 协议。
### 协议遵循 {#protocol-conformance}
@ -326,9 +321,9 @@ public struct TrackedString {
## Extension {#extensions}
Extension 可以在访问级别允许的情况下对类、结构体、枚举进行扩展。Extension 的成员具有和原始类型成员一致的访问级别。例如,你使用 extension 扩展了一个 `public` 或者 `internal` 类型extension 中的成员就默认使用 `internal` 访问级别,和原始类型中的成员一致。如果你使用 extension 扩展了一个 `private` 类型,则 extension 的成员默认使用 `private` 访问级别。
Extension 可以在访问级别允许的情况下对类、结构体、枚举进行扩展。Extension 的新增成员具有和原始类型成员一致的访问级别。例如,你使用 extension 扩展了一个 `public` 或者 `internal` 类型,extension 中的成员就默认使用 `internal` 访问级别。如果你使用 extension 扩展一个 `fileprivate` 类型,则 extension 中的成员默认使用 `fileprivate` 访问级别。如果你使用 extension 扩展了一个 `private` 类型,则 extension 的成员默认使用 `private` 访问级别。
或者,你可以明确指定 extension 的访问级别(例如,`private extension`),从而给该 extension 中的所有成员指定一个新的默认访问级别。这个新的默认访问级别仍然可以被单独指定的访问级别所覆盖。
或者,你可以通过修饰语重新指定 extension 的默认访问级别(例如,`private`),从而给该 extension 中的所有成员指定一个新的默认访问级别。这个新的默认访问级别仍然可以被单独成员指定的访问级别所覆盖。
如果你使用 extension 来遵循协议的话,就不能显式地声明 extension 的访问级别。extension 每个 protocol 要求的实现都默认使用 protocol 的访问级别。
@ -340,7 +335,7 @@ Extension 可以在访问级别允许的情况下对类、结构体、枚举进
- 在 extension 里声明一个私有成员,在同一文件的另一个 extension 里访问。
- 在 extension 里声明一个私有成员,在同一文件的类型声明里访问。
这意味着你可以像组织的代码去使用 extension而且不受私有成员的影响。例如给定下面这样一个简单的协议
这意味着你可以使用 extension 来组织你的代码,而且不受私有成员的影响。例如,给定下面这样一个简单的协议:
```swift
protocol SomeProtocol {
@ -368,7 +363,7 @@ extension SomeStruct: SomeProtocol {
## 类型别名 {#type-aliases}
你定义的任何类型别名都会被当作不同的类型,以便于进行访问控制。类型别名的访问级别不可高于其表示的类型的访问级别。例如,`private` 级别的类型别名可以作为 `private``file-private``internal``public` 或者 `open` 类型的别名,但是 `public` 级别的类型别名只能作为 `public` 类型的别名,不能作为 `internal``file-private``private` 类型的别名。
你定义的任何类型别名都会被当作不同的类型,以便于进行访问控制。类型别名的访问级别不可高于其表示的类型的访问级别。例如,`private` 级别的类型别名可以作为 `private``fileprivate``internal``public` 或者 `open` 类型的别名,但是 `public` 级别的类型别名只能作为 `public` 类型的别名,不能作为 `internal``fileprivate``private` 类型的别名。
> 注意
>

View File

@ -1,6 +1,6 @@
# 高级运算符
除了之前介绍过的[基本运算符](./02_Basic_Operators.md)Swift 还提供了数种可以对数值进行复杂运算的高级运算符。它们包含了在 C 和 Objective-C 中已经被大家所熟知的位运算符和移位运算符。
除了之前介绍过的 [基本运算符](./02_Basic_Operators.md)Swift 还提供了数种可以对数值进行复杂运算的高级运算符。它们包含了在 C 和 Objective-C 中已经被大家所熟知的位运算符和移位运算符。
与 C 语言中的算术运算符不同Swift 中的算术运算符默认是不会溢出的。所有溢出行为都会被捕获并报告为错误。如果想让系统允许溢出行为,可以选择使用 Swift 中另一套默认支持溢出的运算符,比如溢出加法运算符(`&+`)。所有的这些溢出运算符都是以 `&` 开头的。
@ -210,7 +210,7 @@ unsignedOverflow = unsignedOverflow &- 1
![Art/overflowUnsignedSubtraction_2x.png](https://docs.swift.org/swift-book/_images/overflowUnsignedSubtraction_2x.png)
溢出也会发生在有符号整型上。针对有符号整型的所有溢出加法或者减法运算都是按位运算的方式执行的,符号位也需要参与计算,正如[按位左移、右移运算符](#bitwise_left_and_right_shift_operators)所描述的。
溢出也会发生在有符号整型上。针对有符号整型的所有溢出加法或者减法运算都是按位运算的方式执行的,符号位也需要参与计算,正如 [按位左移、右移运算符](#bitwise-left-and-right-shift-operators) 所描述的。
```Swift
var signedOverflow = Int8.min
@ -219,7 +219,7 @@ signedOverflow = signedOverflow &- 1
// 此时 signedOverflow 等于 127
```
`Int8` 型整数能容纳的最小值是 `-128`,以二进制表示即 `10000000`。当使用溢出减法运算符对其进行减 `1` 运算时,符号位被翻转,得到二进制数值 `01111111`,也就是十进制数值的 `127`,这个值也是 `Int8` 型整所能容纳的最大值。
`Int8` 型整数能容纳的最小值是 `-128`,以二进制表示即 `10000000`。当使用溢出减法运算符对其进行减 `1` 运算时,符号位被翻转,得到二进制数值 `01111111`,也就是十进制数值的 `127`,这个值也是 `Int8` 型整所能容纳的最大值。
![Art/overflowSignedSubtraction_2x.png](https://docs.swift.org/swift-book/_images/overflowSignedSubtraction_2x.png)
@ -266,7 +266,7 @@ signedOverflow = signedOverflow &- 1
因此计算结果为 `17`
有关 Swift 标准库提供的操作符信息,包括操作符优先级组和结性设置的完整列表,请参见[操作符声明](https://developer.apple.com/documentation/swift/operator_declarations)。
有关 Swift 标准库提供的操作符信息,包括操作符优先级组和结性设置的完整列表,请参见 [操作符声明](https://developer.apple.com/documentation/swift/operator_declarations)。
> 注意
>
@ -389,32 +389,11 @@ if twoThree == anotherTwoThree {
// 打印“These two vectors are equivalent.”
```
多数简单情况下,可以使用 Swift 为您提供的等价运算符默认实现。Swift 为以下数种自定义类型提供等价运算符的默认实现:
- 只拥有存储属性,并且它们全都遵循 `Equatable` 协议的结构体
- 只拥有关联类型,并且它们全都遵循 `Equatable` 协议的枚举
- 没有关联类型的枚举
在类型原始的声明中声明遵循 `Equatable` 来接收这些默认实现。
下面为三维位置向量 `(x, y, z)` 定义的 `Vector3D` 结构体,与 `Vector2D` 类似。由于 `x``y``z` 属性都是 `Equatable` 类型,`Vector3D` 获得了默认的等价运算符实现。
```Swift
struct Vector3D: Equatable {
var x = 0.0, y = 0.0, z = 0.0
}
let twoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
let anotherTwoThreeFour = Vector3D(x: 2.0, y: 3.0, z: 4.0)
if twoThreeFour == anotherTwoThreeFour {
print("These two vectors are also equivalent.")
}
// 打印“These two vectors are also equivalent.”
```
多数简单情况下,可以 Swift 合成等价运算符的实现,详见 [使用合成实现来采纳协议](./21_Protocols.md#adopting-a-protocol-using-a-synthesized-implementation)。
## 自定义运算符 {#custom-operators}
除了实现标准运算符,在 Swift 中还可以声明和实现*自定义运算符*。可以用来自定义运算符的字符列表请参考[运算符](../chapter3/02_Lexical_Structure.html#operators)。
除了实现标准运算符,在 Swift 中还可以声明和实现*自定义运算符*。可以用来自定义运算符的字符列表请参考 [运算符](../03_language_reference/02_Lexical_Structure.md#operators)。
新的运算符要使用 `operator` 关键字在全局作用域内进行定义,同时还要指定 `prefix``infix` 或者 `postfix` 修饰符:
@ -440,7 +419,7 @@ let afterDoubling = +++toBeDoubled
### 自定义中缀运算符的优先级 {#precedence-and-associativity-for-custom-infix-operators}
每个自定义中缀运算符都属于某个优先级组。优先级组指定了这个运算符相对于其他中缀运算符的优先级和结合性。[优先级和结合性](#precedence_and_associativity)中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。
每个自定义中缀运算符都属于某个优先级组。优先级组指定了这个运算符相对于其他中缀运算符的优先级和结合性。[优先级和结合性](#precedence-and-associativity) 中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。
而没有明确放入某个优先级组的自定义中缀运算符将会被放到一个默认的优先级组内,其优先级高于三元运算符。
@ -459,7 +438,7 @@ let plusMinusVector = firstVector +- secondVector
// plusMinusVector 是一个 Vector2D 实例,并且它的值为 (4.0, -2.0)
```
这个运算符把两个向量的 `x` 值相加,同时从第一个向量的 `y` 中减去第二个向量的 `y` 。因为它本质上是属于“相加型”运算符,所以将它放置在 `+``-` 等默认中缀“相加型”运算符相同的优先级组中。关于 Swift 标准库提供的运算符,以及完整的运算符优先级组和结合性设置,请参考 [运算符声明](https://developer.apple.com/documentation/swift/operator_declarations)。而更多关于优先级组以及自定义操作符和优先级组的语法,请参考[运算符声明](./06_Declarations.md#operator_declaration)。
这个运算符把两个向量的 `x` 值相加,同时从第一个向量的 `y` 中减去第二个向量的 `y` 。因为它本质上是属于“相加型”运算符,所以将它放置在 `+``-` 等默认中缀“相加型”运算符相同的优先级组中。关于 Swift 标准库提供的运算符,以及完整的运算符优先级组和结合性设置,请参考 [运算符声明](https://developer.apple.com/documentation/swift/operator_declarations)。而更多关于优先级组以及自定义操作符和优先级组的语法,请参考 [运算符声明](../03_language_reference/06_Declarations.md#operator-declaration)。
> 注意
>

View File

@ -0,0 +1 @@
# 并发

View File

@ -1,3 +1,3 @@
# Swift 教程
# Swift 语言教程
本章介绍了 Swift 的各种特性及其使用方法,是全书的核心部分。

View File

@ -19,14 +19,14 @@ Swift 语言相对较小,这是由于 Swift 代码中常用的类型、函数
> getter-setter 方法块语法
>
> *getter-setter 方法块* → { [*getter 子句*](./06_Declarations.md#getter-clause) [*setter 子句*](./06_Declarations.md#setter-clause)<sub>可选</sub> } | { [*setter 子句*](./06_Declarations.md#setter-clause) [*getter 子句*](./06_Declarations.md#getter-clause) }
>
> *getter-setter 方法块* → { [getter 子句](./06_Declarations.md#getter-clause) [setter 子句](./06_Declarations.md#setter-clause)<sub>可选</sub> } | { [setter 子句](./06_Declarations.md#setter-clause) [getter 子句](./06_Declarations.md#getter-clause) }
这个定义表明,一个 getter-setter 方法块可以由一个 getter 分句后跟一个可选的 setter 分句构成,然后用大括号括起来,或者由一个 setter 分句后跟一个 getter 分句构成,然后用大括号括起来。上述的语法产式等价于下面的两个语法产式,
> getter-setter 方法块语法
>
> getter-setter 方法块 → { [*getter 子句*](./06_Declarations.md#getter-clause) [*setter 子句*](./06_Declarations.md#setter-clause)<sub>可选</sub> }
> getter-setter 方法块 → { [getter 子句](./06_Declarations.md#getter-clause) [setter 子句](./06_Declarations.md#setter-clause)<sub>可选</sub> }
>
> getter-setter 方法块 → { [*setter 子句*](./06_Declarations.md#setter-clause) [*getter 子句*](./06_Declarations.md#getter-clause) }
> getter-setter 方法块 → { [setter 子句](./06_Declarations.md#setter-clause) [getter 子句](./06_Declarations.md#getter-clause) }
>

View File

@ -0,0 +1,645 @@
# 词法结构Lexical Structure
Swift 的*“词法结构lexical structure”* 描述了能构成该语言中有效符号token的字符序列。这些合法符号组成了语言中最底层的构建基块并在之后的章节中用于描述语言的其他部分。一个合法符号由一个标识符identifier、关键字keyword、标点符号punctuation、字面量literal或运算符operator组成。
通常情况下,符号是考虑了输入文本中最长可能的子字符串,并被随后将介绍的语法约束,根据 Swift 源文件的字符生成的。这种方法称为*“最长匹配longest match”*,或者*“最大适合maximal munch”*。
## 空白与注释 {#whitespace}
空白whitespace有两个用途分隔源文件中的符号以及帮助区分运算符属于前缀还是后缀参见 [运算符](#operators)在其他情况下空白则会被忽略。以下的字符会被当作空白空格U+0020、换行符U+000A、回车符U+000D、水平制表符U+0009、垂直制表符U+000B、换页符U+000C以及空字符U+0000
注释被编译器当作空白处理。单行注释由 `//` 开始直至遇到换行符U+000A或者回车符U+000D。多行注释由 `/*` 开始,以 `*/` 结束。多行注释允许嵌套,但注释标记必须成对出现。
注释可以包含其他的格式和标记,如 [标记格式参考](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/index.html)中所述。
> 空白语法
>
> *空白* → [空白项](#whitespace-item) [空白](#whitespace)<sub>可选</sub>
>
#### whitespace-item {#whitespace-item}
>
> *空白项* → [断行符](#line-break)
>
> *空白项* → [注释](#comment)
>
> *空白项* → [多行注释](#multiline-comment)
>
> *空白项* → U+0000U+0009U+000BU+000C 或者 U+0020
>
>
#### line-break {#line-break}
>
> *断行符* → U+000A
>
> *断行符* → U+000D
>
> *断行符* → U+000D 接着是 U+000A
>
>
#### comment {#comment}
>
> *注释* → // [注释内容](#comment-text) [断行符](#line-break)
>
>
#### multiline-comment {#multiline-comment}
>
> *多行注释* → `/*` [多行注释内容](#multiline-commnet-text) `*/`
>
>
#### comment-text {#comment-text}
>
> *注释内容* → [注释内容项](#comment-text-item) [注释内容](#comment-text)<sub>可选</sub>
>
>
#### comment-text-item {#comment-text-item}
>
> *注释内容项* → 任何 Unicode 标量值,除了 U+000A 或者 U+000D
>
>
#### multiline-commnet-text {#multiline-commnet-text}
>
> *多行注释内容* → [多行注释内容项](#multiline-comment-text-item) [多行注释内容](#multiline-comment-text)<sub>可选</sub>
>
> *多行注释内容项* → [多行注释](#multiline-comment).
>
> *多行注释内容项* → [注释内容项](#comment-text-item)
>
> *多行注释内容项* → 任何 Unicode 标量值,除了 `/*` 或者 `*/`
## 标识符 {#identifiers}
*标识符identifier* 可以由以下的字符开始:大写或小写的字母 `A``Z`、下划线(`_`、基本多文种平面Basic Multilingual Plane中非字符数字组合的 Unicode 字符以及基本多文种平面以外的非个人专用区字符。在首字符之后,允许使用数字和组合 Unicode 字符。
使用保留字作为标识符,需要在其前后增加反引号(`` ` ``)。例如,`class` 不是合法的标识符,但可以使用 `` `class` ``。反引号不属于标识符的一部分,`` `x` `` 和 `x` 表示同一标识符。
闭包中如果没有明确指定参数名称,参数将被隐式命名为 `$0`、`$1`、`$2` 等等。这些命名在闭包作用域范围内是合法的标识符。
编译器给含有属性包装器呈现值的属性自动合成以美元符号(*$*)开头的标识符。你的代码可以与这些标识符进行交互,,但是不能使用该前缀声明标识符。更详细的介绍,请查看 [特性](./07_Attributes.md) 章节中的 [属性包装器](./07_Attributes.md#propertywrapper) 部分。
> 标识符语法
>
> *标识符* → [头部标识符](#identifier-head) [标识符字符组](#identifier-characters)<sub>可选</sub>
>
> *标识符* → \`[头部标识符](#identifier-head) [标识符字符组](#identifier-characters)<sub>可选</sub>\`
>
> *标识符* → [隐式参数名](#implicit-parameter-name)
>
> *标识符列表* → [标识符](#identifier) | [标识符](#identifier) **,** [标识符列表](#identifier)
>
>
#### identifier-head {#identifier-head}
>
> *头部标识符* → 大写或小写字母 A - Z
>
> *头部标识符* → **_**
>
> *头部标识符* → U+00A8U+00AAU+00ADU+00AFU+00B2U+00B5或者 U+00B7U+00BA
>
> *头部标识符* → U+00BCU+00BEU+00C0U+00D6U+00D8U+00F6或者 U+00F8U+00FF
>
> *头部标识符* → U+0100U+02FFU+0370U+167FU+1681U+180D或者 U+180FU+1DBF
>
> *头部标识符* → U+1E00U+1FFF
>
> *头部标识符* → U+200BU+200DU+202AU+202EU+203FU+2040U+2054或者 U+2060U+206F
>
> *头部标识符* → U+2070U+20CFU+2100U+218FU+2460U+24FF或者 U+2776U+2793
>
> *头部标识符* → U+2C00U+2DFF 或者 U+2E80U+2FFF
>
> *头部标识符* → U+3004U+3007U+3021U+302FU+3031U+303F或者 U+3040U+D7FF
>
> *头部标识符* → U+F900U+FD3DU+FD40U+FDCFU+FDF0U+FE1F或者 U+FE30U+FE44
>
> *头部标识符* → U+FE47U+FFFD
>
> *头部标识符* → U+10000U+1FFFDU+20000U+2FFFDU+30000U+3FFFD或者 U+40000U+4FFFD
>
> *头部标识符* → U+50000U+5FFFDU+60000U+6FFFDU+70000U+7FFFD或者 U+80000U+8FFFD
>
> *头部标识符* → U+90000U+9FFFDU+A0000U+AFFFDU+B0000U+BFFFD或者 U+C0000U+CFFFD
>
> *头部标识符* → U+D0000U+DFFFD 或者 U+E0000U+EFFFD
>
#### identifier-character {#identifier-character}
>
> *标识符字符* → 数值 0 - 9
>
> *标识符字符* → U+0300U+036FU+1DC0U+1DFFU+20D0U+20FF或者 U+FE20U+FE2F
>
> *标识符字符* → [头部标识符](#identifier-head)
>
>
#### identifier-characters {#identifier-characters}
>
> *标识符字符组* → [标识符字符](#identifier-character) [标识符字符组](#identifier-characters)<sub>可选</sub>
>
>
#### implicit-parameter-name {#implicit-parameter-name}
>
> *隐式参数名* → **$** [十进制数字列表](#decimal-digit)
>
#### property-wrapper-projection {#property-wrapper-projection}
>
> *属性包装器呈现值* → **$** [标识符字符组](#identifier-characters)
>
## 关键字和标点符号 {#keywords-and-punctuation}
下面这些被保留的关键字不允许用作标识符,除非使用反引号转义,具体描述请参考 [标识符](#identifiers)。除了 `inout``var` 以及 `let` 之外的关键字可以用作某个函数声明或者函数调用当中的外部参数名,无需添加反引号转义。当一个成员与一个关键字具有相同的名称时,不需要使用反引号来转义对该成员的引用,除非在引用该成员和使用该关键字之间存在歧义 - 例如,`self``Type``Protocol` 在显式的成员表达式中具有特殊的含义,因此它们必须在该上下文中使用反引号进行转义。
* 用在声明中的关键字:`associatedtype``class``deinit``enum``extension``fileprivate ``func``import``init``inout``internal``let``open``operator``private``protocol``public``rethrows``static``struct``subscript``typealias` 以及 `var`
* 用在语句中的关键字:`break``case``continue``default``defer``do``else``fallthrough``for``guard``if``in``repeat``return``switch``where` 以及 `while`
* 用在表达式和类型中的关键字:`as``Any``catch``false``is``nil``super``self``Self``throw``throws``true` 以及 `try `
* 用在模式中的关键字:`_`
* 以井字号(`#`)开头的关键字:`#available``#colorLiteral``#column``#else``#elseif``#endif``#error``#file``#filePath``#fileLiteral``#function``#if``#imageLiteral``#line``#selector``#sourceLocation`以及 `#warning`
* 特定上下文中被保留的关键字:`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`。这些关键字在特定上下文之外可以被用做标识符。
以下符号被保留为标点符号,不能用于自定义运算符:`(``)``{``}``[``]``.``,``:``;``=``@``#``&`(作为前缀运算符)、`->`、`` ` ``、`?`、以及 `!`(作为后缀运算符)。
## 字面量 {#literal}
*字面量literal* 用来表示源码中某种特定类型的值,比如一个数字或字符串。
下面是字面量的一些示例:
```swift
42 // 整数字面量
3.14159 // 浮点数字面量
"Hello, world!" // 字符串字面量
true // 布尔值字面量
```
字面量本身并不包含类型信息。事实上,一个字面量会被解析为拥有无限的精度,然后 Swift 的类型推导会尝试去推导出这个字面量的类型。比如,在 `let x: Int8 = 42` 这个声明中Swift 使用了显式类型注解(`: Int8`)来推导出 `42` 这个整数字面量的类型是 `Int8`。如果没有可用的类型信息Swift 则会从标准库中定义的字面量类型中推导出一个默认的类型。整数字面量的默认类型是 `Int`,浮点数字面量的默认类型是 `Double`,字符串字面量的默认类型是 `String`,布尔值字面量的默认类型是 `Bool`。比如,在 `let str = "Hello, world"` 这个声明中,字符串 `"Hello, world"` 的默认推导类型就是 `String`。
当为一个字面量值指定了类型注解的时候,这个注解类型必须能通过这个字面量值实例化。也就是说,这个类型必须符合这些 Swift 标准库协议中的一个:整数字面量的 `ExpressibleByIntegerLiteral` 协议、浮点数字面量的 `ExpressibleByFloatLiteral` 协议、字符串字面量的 `ExpressibleByStringLiteral` 协议、布尔值字面量的 `ExpressibleByBooleanLiteral` 协议、只包含单个 Unicode 标量字符串文本的 `ExpressibleByUnicodeScalarLiteral` 协议以及只包含单个扩展字形簇grapheme cluster字符串文字的 `ExpressibleByExtendedGraphemeClusterLiteral` 协议。比如,`Int8` 符合 `ExpressibleByIntegerLiteral` 协议,因此它能在 `let x: Int8 = 42` 这个声明中作为整数字面量 `42` 的类型注解。
> 字面量语法
>
> *字面量* → [数值字面量](#integer-literal) | [字符串字面量](#string-literal) | [布尔值字面量](#integer-literal) | [nil 字面量](#integer-literal)
>
> *数值字面量* → **-**<sub>可选</sub> [整数字面量](#integer-literal) | **-**<sub>可选</sub> [浮点数字面量](#floating-point-literal)
>
> *布尔值字面量* → **true** | **false**
>
> *nil 字面量* → **nil**
### 整数字面量{#integer-literal}
*整数字面量Integer Literals* 表示未指定精度的整数值。整数字面量默认用十进制表示;可以加前缀来指定其他的进制。二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x`。
十进制字面量包含数字 `0` 至 `9`。二进制字面量包含 `0` 和 `1`,八进制字面量包含数字 `0` 至 `7`,十六进制字面量包含数字 `0` 至 `9` 以及字母 `A` 至 `F`(大小写均可)。
负整数字面量的表示方式为在整数字面量前加负号 `-`,比如 `-42`。
整型字面面可以使用下划线(`_`)来增加数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,这同样也会被系统所忽略,并不会影响字面量的值。
除非特别指定,整数字面量的默认推导类型为 Swift 标准库类型中的 `Int`。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 [整数](../02_language_guide/01_The_Basics.md#integers)。
> 整数字面量语法
>
>
#### integer-literal {#integer-literal}
>
> *整数字面量* → [二进制字面量](#binary-literal)
>
> *整数字面量* → [八进制字面量](#octal-literal)
>
> *整数字面量* → [十进制字面量](#decimal-literal)
>
> *整数字面量* → [十六进制字面量](#hexadecimal-literal)
>
>
#### binary-literal {#binary-literal}
>
> *二进制字面量* → **0b** [二进制数字](#binary-digit) [二进制字面量字符组](#binary-literal-characters)<sub>可选</sub>
>
>
#### binary-digit {#binary-digit}
>
> *二进制数字* → 数值 0 或 1
>
> *二进制字面量字符* → [二进制数字](#binary-digit) | **_**
>
>
#### binary-literal-characters {#binary-literal-characters}
>
> *二进制字面量字符组* → [二进制字面量字符](#binary-literal-character) [二进制字面量字符组](#binary-literal-characters)<sub>可选</sub>
>
>
#### octal-literal {#octal-literal}
>
> *八进制字面量* → **0o** [八进字数字](#octal-digit) [八进制字符组](#octal-literal-characters)<sub>可选</sub>
>
>
#### octal-digit {#octal-digit}
>
> *八进字数字* → 数值 0 到 7
>
> *八进制字符* → [八进字数字](#octal-digit) | **_**
>
>
#### octal-literal-characters {#octal-literal-characters}
>
> *八进制字符组* → [八进制字符](#octal-literal-character) [八进制字符组](#octal-literal-characters)<sub>可选</sub>
>
>
#### decimal-literal {#decimal-literal}
>
> *十进制字面量* → [十进制数字](#decimal-digit) [十进制字符组](#decimal-literal-characters)<sub>可选</sub>
>
>
#### decimal-digit {#decimal-digit}
>
> *十进制数字* → 数值 0 到 9
>
>
#### decimal-literal-characters {#decimal-literal-characters}
>
> *十进制数字组* → [十进制数字](#decimal-digit) [十进制数字组](#decimal-literal-characters)<sub>可选</sub>
>
> *十进制字符* → [十进制数字](#decimal-digit) | **_**
>
> *十进制字符组* → [十进制字符](#decimal-literal-characters) [十进制字符组](#decimal-literal-characters)<sub>可选</sub>
>
>
#### hexadecimal-literal {#hexadecimal-literal}
>
> *十六进制字面量* → **0x** [十六进制数字](#hexadecimal-digit) [十六进制字面量字符组](#hexadecimal-literal-characters)<sub>可选</sub>
>
>
#### hexadecimal-digit {#hexadecimal-digit}
>
> *十六进制数字* → 数值 0 到 9, 字母 a 到 f, 或 A 到 F
>
> *十六进制字符* → [十六进制数字](#hexadecimal-digit) | **-**
>
>
#### hexadecimal-literal-characters {#hexadecimal-literal-characters}
>
> *十六进制字面量字符组* → [十六进制字符](#hexadecimal-literal-characters) [十六进制字面量字符组](#hexadecimal-literal-characters)<sub>可选</sub>
### 浮点数字面量{#floating-point-literal}
*浮点数字面量Floating-point literals*表示未指定精度浮点数的值。
浮点数字面量默认用十进制表示(无前缀),也可以用十六进制表示(加前缀 `0x`)。
十进制浮点数字面量由十进制数字串后跟十进制小数部分或十进制指数部分(或两者皆有)组成。十进制小数部分由小数点(`.`)后跟十进制数字串组成。指数部分由大写或小写字母 `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 x 2²也就是 `60`。同样,`0xFp-2` 表示 15 x 2¯²也就是 `3.75`。
负数的浮点数字面量由负号(`-`)和浮点数字面量组成,例如 `-42.5`。
浮点数字面量允许使用下划线(`_`)来增强数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。
除非特别指定,浮点数字面量的默认推导类型为 Swift 标准库类型中的 `Double`,表示 64 位浮点数。Swift 标准库也定义了 `Float` 类型,表示 32 位浮点数。
> 浮点数字面量语法
>
>
#### floating-point-literal {#floating-point-literal}
>
> *浮点数字面量* → [十进制字面量](#decimal-literal) [十进制分数](#decimal-fraction)<sub>可选</sub> [十进制指数](#decimal-exponent)<sub>可选</sub>
>
> *浮点数字面量* → [十六进制字面量](#hexadecimal-literal) [十六进制分数](#hexadecimal-fraction)<sub>可选</sub> [十六进制指数](#hexadecimal-exponent)
>
>
#### decimal-fraction {#decimal-fraction}
>
> *十进制分数* → **.** [十进制字面量](#decimal-literal)
>
>
#### decimal-exponent {#decimal-exponent}
>
> *十进制指数* → [十进制指数 e](#floating-point-e) [正负号](#sign)<sub>可选</sub> [十进制字面量](#decimal-literal)
>
>
#### hexadecimal-fraction {#hexadecimal-fraction}
>
> *十六进制分数* → **.** [十六进制数字](#hexadecimal-digit) [十六进制字面量字符组](#hexadecimal-literal-characters)<sub>可选</sub>
>
>
#### hexadecimal-exponent {#hexadecimal-exponent}
>
> *十六进制指数* → [十六进制指数 p](#floating-point-p) [正负号](#sign)<sub>可选</sub> [十进制字面量](#decimal-literal)
>
>
#### floating-point-e {#floating-point-e}
>
> *十进制指数 e* → **e** | **E**
>
>
#### floating-point-p {#floating-point-p}
>
> *十六进制指数 p* → **p** | **P**
>
>
#### sign {#sign}
>
> *正负号* → **+** | **-**
### 字符串字面量 {#string-literal}
字符串字面量是被引号包括的一串字符组成。单行字符串字面量是被包在双引号中的一串字符组成,形式如下:
> "`字符`"
字符串字面量中不能包含未转义的双引号(`"`)、未转义的反斜线(`\`)、回车符、换行符。
多行字符串字面量被包在三个双引号中的一串字符组成,形式如下:
> """
> `字符`
> """
与单行字符串字面量不同的是,多行字符串字面量可以包含不转义的双引号("),回车以及换行。它不能包含三个未转义的连续双引号。
`"""` 之后的回车或者换行开始多行字符串字面量,它们不是字符串的一部分。结束部分的 `"""` 以及它之前的回车或者换行,也不是字符串的一部分。要让多行字符串字面量的开始或结束带有换行,就在第一行或者最后一行写一个空行。
多行字符串字面量可以使用任何空格或制表符组合进行缩进;这些缩进不会包含在字符串中。`"""` 的结束符号决定了缩进:字面量中的每一个非空行开头都必须与结束符 `"""` 之前出现的缩进完全一致;空格和制表符不会被转换。你可以在缩进后包含额外的空格和制表符;这些空格和制表符会在字符串中出现。
多行字符串字面量中的一行结束使用规范化的换行符号。尽管你的源代码混用了回车和换行符,字符串中所有的行结束都必须一样.
在多行字符串字面量里,在行末用反斜线(`\`)可以省略字符串行间中断。反斜线和换行符之间的空白也将被忽略。你可以在你的代码里用这种语法硬包裹多行字符串字面量,而不改变结果字符串的值。
可以在字符串字面量中使用的转义特殊符号如下:
* 空字符 `\0`
* 反斜线 `\\`
* 水平制表符 `\t`
* 换行符 `\n`
* 回车符 `\r`
* 双引号 `\"`
* 单引号 `\'`
* Unicode 标量 `\u{`n`}`n 为一到八位的十六进制数字
字符串字面量允许在反斜杠(`\`)后的括号 `()` 中插入表达式的值。插入表达式可以包含字符串字面量,但不能包含未转义的反斜线(`\`)、回车符以及换行符。
例如,以下所有字符串字面量的值都是相同的:
```swift
"1 2 3"
"1 2 \("3")"
"1 2 \(3)"
"1 2 \(1 + 2)"
let x = 3; "1 2 \(x)"
```
由扩展分隔符包裹的字符串,它是由引号以及成对出现的数字符号(`#`)包裹的字符串序列。由扩展分隔符包裹的字符串形式如下所示:
> \#"`characters`"#
>
> \#"""
>
> `characters`
>
> """#
特殊字符在被扩展分隔符分隔的结果字符串中会展示为普通字符,而不是特殊字符。你可以使用扩展分隔符来创建一些通常情况下具有特殊效果的字符串。例如,生成字符串插值,启动转义序列或终止字符串。
以下所示,由字符串字面量和扩展分隔符所创建的字符串是等价的:
```swift
let string = #"\(x) \ " \u{2603}"#
let escaped = "\\(x) \\ \" \\u{2603}"
print(string)
// Prints "\(x) \ " \u{2603}"
print(string == escaped)
// Prints "true"
```
如果在一个字符串中使用多对扩展分隔符,请不要在分隔符之间使用空格。
```swift
print(###"Line 1\###nLine 2"###) // OK
print(# # #"Line 1\# # #nLine 2"# # #) // Error
```
使用扩展分隔符创建的多行字符串字面量与普通多行字符串字面量具有相同的缩进要求。
字符串字面量的默认推导类型为 `String`。更多有关 `String` 类型的信息请参考 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 以及 [*字符串结构参考*](https://developer.apple.com/documentation/swift/string)。
`` 操作符连接的字符型字面量是在编译时进行连接的。比如下面的 `textA``textB` 是完全一样的,`textA` 没有任何运行时的连接操作。
```swift
let textA = "Hello " + "world"
let textB = "Hello world"
```
> 字符串字面量语法
>
> *字符串字面量* → [静态字符串字面量](#static-string-literal) | [插值字符串字面量](#interpolated-string-literal)
>
> *字符串开分隔定界符* → [字符串扩展分隔符](#extended-string-literal-delimiter) **"**
>
> *字符串闭分隔定界符* → **"** [字符串扩展分隔符](#extended-string-literal-delimiter)<sub>可选</sub>
>
>
#### static-string-literal {#static-string-literal}
>
> *静态字符串字面量* → [字符串开分隔定界符](#extended-string-literal-delimiter) [引用文本](#quoted-text)<sub>可选</sub> [字符串闭分隔定界符](#extended-string-literal-delimiter)
>
> *静态字符串字面量* → [多行字符串开分隔定界符](#extended-string-literal-delimiter) [多行引用文本](#multiline-quoted-text)<sub>可选</sub> [多行字符串闭分隔定界符](#extended-string-literal-delimiter)
>
> *多行字符串开分隔定界符* → [字符串扩展分隔符](#extended-string-literal-delimiter) **"""**
>
> *多行字符串闭分隔定界符* → **"""** [字符串扩展分隔符](#extended-string-literal-delimiter)
>
>
#### extended-string-literal-delimiter {#extended-string-literal-delimiter}
>
> *字符串扩展分隔符* → **#** [字符串扩展分隔符](#extended-string-literal-delimiter)<sub>可选</sub>
>
>
#### quoted-text {#quoted-text}
>
> *引用文本* → [引用文本项](#quoted-text-item) [引用文本](#quoted-text)<sub>可选</sub>
>
>
#### quoted-text-item {#quoted-text-item}
>
> *引用文本项* → [转义字符](#escaped-character)
>
> *引用文本项* → 除了 **"**、**\\**、U+000A、U+000D 以外的所有 Unicode 字符
>
>
#### multiline-quoted-text {#multiline-quoted-text}
>
> *多行引用文本* → [多行引用文本项](#multiline-quoted-text-item) [多行引用文本](#multiline-quoted-text)<sub>可选</sub>
>
>
#### multiline-quoted-text-item {#multiline-quoted-text-item}
>
> *多行引用文本项* [转义字符](#escaped-character)<sub>可选</sub>
>
>
#### multiline-quoted-text {#multiline-quoted-text}
>
> *多行引用文本* → 除了 **\\** 以外的任何 Unicode 标量值
>
> *多行引用文本* → [转义换行](#escaped-newline)
>
>
#### interpolated-string-literal {#interpolated-string-literal}
>
> *插值字符串字面量* → [字符串开分隔定界符](#extended-string-literal-delimiter) [插值文本](#interpolated-text)<sub>可选</sub> [字符串闭分隔定界符](#extended-string-literal-delimiter)
>
> *插值字符串字面量* → [多行字符串开分隔定界符](#extended-string-literal-delimiter) [插值文本](#interpolated-text)<sub>可选</sub> [多行字符串闭分隔定界符](#extended-string-literal-delimiter)
>
>
#### interpolated-text {#interpolated-text}
>
> *插值文本* → [插值文本项](#interpolated-text-item) [插值文本](#interpolated-text)<sub>可选</sub>
>
>
#### interpolated-text-item {#interpolated-text-item}
>
> *插值文本项* → **\\(**[ 表达式 ](./04_Expressions.md)**)** | [引用文本项](#quoted-text-item)
>
> *多行插值文本* → [多行插值文本项](#multiline-quoted-text-item) [多行插值文本](#multiline-quoted-text)<sub>可选</sub>
>
> *多行插值文本项* → **\\(** [表达式](./04_Expressions.md) **)** | [多行引用文本项](#multiline-quoted-text-item)
>
>
#### escape-sequence {#escape-sequence}
>
> *转义序列* → **\\** [字符串扩展分隔符](#extended-string-literal-delimiter)
>
>
#### escaped-character {#escaped-character}
>
> *转义字符* → [转义序列](#escape-sequence) **0** | [转义序列](#escape-sequence) **\\** | [转义序列](#escape-sequence) **t** | [转义序列](#escape-sequence) **n** | [转义序列](#escape-sequence) **r** | [转义序列](#escape-sequence) **\"** | [转义序列](#escape-sequence) **'**
>
> *转义字符* → [转义序列](#escape-sequence) **u {** [unicode 标量数字](#unicode-scalar-digits) **}**
>
>
#### unicode-scalar-digits {#unicode-scalar-digits}
>
> *unicode 标量数字* → 一到八位的十六进制数字
>
>
#### escaped-newline {#escaped-newline}
>
> *转义换行符* → [转义序列](#escape-sequence) [空白](#whitespace)<sub>可选</sub> [断行符](#line-break)
## 运算符 {#operator}
Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基础运算符](../02_language_guide/02_Basic_Operators.md) 和 [高级运算符](../02_language_guide/27_Advanced_Operators.md) 中进行了阐述。这一小节将描述哪些字符能用于自定义运算符。
自定义运算符可以由以下其中之一的 ASCII 字符 `/``=``-``+``!``*``%``<``>``&``|``^``?` 以及 `~`,或者后面语法中规定的任一个 Unicode 字符(其中包含了*数学运算符*、*零散符号Miscellaneous Symbols* 以及*印刷符号Dingbats*之类的 Unicode 块)开始。在第一个字符之后,允许使用组合型 Unicode 字符。
您也可以以点号(`.`)开头来定义自定义运算符。这些运算符可以包含额外的点。例如 `.+.` 会被看作一个单独的运算符。如果某个运算符不是以点号开头的,那么它就无法再包含另外的点号了。例如,`+.+` 就会被看作为一个 `+` 运算符后面跟着一个 `.+` 运算符。
虽然您可以用问号 `?` 来自定义运算符,但是这个运算符不能只包含单独的一个问号。此外,虽然运算符可以包含一个惊叹号 `!`,但是前缀运算符不能够以问号或者惊叹号开头。
> 注意
>
> 以下这些标记 `=`、`->`、`//`、`/*`、`*/`、`.`,前缀运算符 `<`、`&` 和 `?`,中缀运算符 `?`,后缀运算符 `>`、`!` 和 `?` 是被系统保留的。这些符号不能被重载,也不能用作自定义运算符。
运算符两侧的空白被用来区分该运算符是否为前缀运算符、后缀运算符或二元运算符。规则总结如下:
* 如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:`a+++b``a +++ b` 当中的 `+++` 运算符会被看作二元运算符。
* 如果运算符只有左侧空白,将被看作一元前缀运算符。例如 `a +++b` 中的 `+++` 运算符会被看做是一元前缀运算符。
* 如果运算符只有右侧空白,将被看作一元后缀运算符。例如 `a+++ b` 中的 `+++` 运算符会被看作是一元后缀运算符。
* 如果运算符左侧没有空白并紧跟 `.`,将被看作一元后缀运算符。例如 `a+++.b` 中的 `+++` 运算符会被视为一元后缀运算符(即上式被视为 `a+++ .b` 而不是 `a +++ .b`)。
鉴于这些规则,`(``[``{` 是在运算符前面,`)``]``}` 是在运算符后面,以及字符 `,``;``:` 都被视为空白。
以上规则需注意一点。如果预定义运算符 `!``?` 左侧没有空白,则不管右侧是否有空白都将被看作后缀运算符。如果将 `?` 用作可选链式调用运算符,左侧必须无空白。如果用于条件运算符 `? :`,必须两侧都有空白。
在某些特定的设计中,以 `<``>` 开头的运算符会被分离成两个或多个符号。剩余部分可能会以同样的方式被再次分离。因此,在 `Dictionary<String, Array<Int>>` 中没有必要添加空白来消除闭合字符 `>` 的歧义。在这个例子中,闭合字符 `>` 不会被视为单独的符号,因而不会被错误解析为 `>>` 运算符。
要学习如何自定义运算符,请参考 [自定义运算符](../02_language_guide/27_Advanced_Operators.md#custom-operators) 和 [运算符声明](./06_Declarations.md#operator-declaration)。要学习如何重载运算符,请参考 [运算符函数](../02_language_guide/27_Advanced_Operators.md#operator-functions)。
> 运算符语法
>
> *运算符* → [头部运算符](#operator-head) [运算符字符组](#operator-characters)<sub>可选</sub>
>
> *运算符* → [头部点运算符](#dot-operator-head) [点运算符字符组](#dot-operator-characters)
>
>
#### operator-head {#operator-head}
>
> *头部运算符* → **/** | **=** | **-** | **+** | **!** | __*__ | **%** | **<** | **>** | **&** | **|** | **^** | **~** | **?**
>
> *头部运算符* → U+00A1U+00A7
>
> *头部运算符* → U+00A9 或 U+00AB
>
> *头部运算符* → U+00AC 或 U+00AE
>
> *头部运算符* → U+00B0U+00B1
>
> *头部运算符* → U+00B6U+00BBU+00BFU+00D7或 U+00F7
>
> *头部运算符* → U+2016U+2017
>
> *头部运算符* → U+2020U+2027
>
> *头部运算符* → U+2030U+203E
>
> *头部运算符* → U+2041U+2053
>
> *头部运算符* → U+2055U+205E
>
> *头部运算符* → U+2190U+23FF
>
> *头部运算符* → U+2500U+2775
>
> *头部运算符* → U+2794U+2BFF
>
> *头部运算符* → U+2E00U+2E7F
>
> *头部运算符* → U+3001U+3003
>
> *头部运算符* → U+3008U+3020
>
> *头部运算符* → U+3030
>
>
#### operator-character {#operator-character}
>
> *运算符字符* → [头部运算符](#operator-head)
>
> *运算符字符* → U+0300U+036F
>
> *运算符字符* → U+1DC0U+1DFF
>
> *运算符字符* → U+20D0U+20FF
>
> *运算符字符* → U+FE00U+FE0F
>
> *运算符字符* → U+FE20U+FE2F
>
> *运算符字符* → U+E0100U+E01EF
>
>
#### operator-characters {#operator-characters}
>
> *运算符字符组* → [运算符字符](#operator-character) [运算符字符组](#operator-characters)<sub>可选</sub>
>
>
#### dot-operator-head {#dot-operator-head}
>
> *头部点运算符* → **.**
>
>
#### dot-operator-character {#dot-operator-character}
>
> *点运算符字符* → **.** | [运算符字符](#operator-character)
>
>
#### dot-operator-characters {#dot-operator-characters}
>
> *点运算符字符组* → [点运算符字符](#dot-operator-character) [点运算符字符组](#dot-operator-characters)<sub>可选</sub>
>
> *二元运算符* → [运算符](#operator)
>
> *前缀运算符* → [运算符](#operator)
>
> *后缀运算符* → [运算符](#operator)

View File

@ -2,7 +2,7 @@
Swift 语言存在两种类型:命名型类型和复合型类型。*命名型类型*是指定义时可以给定名字的类型。命名型类型包括类、结构体、枚举和协议。比如,一个用户定义类 `MyClass` 的实例拥有类型 `MyClass`。除了用户定义的命名型类型Swift 标准库也定义了很多常用的命名型类型,包括那些表示数组、字典和可选值的类型。
那些通常被其它语言认为是基本或原始的数据型类型,比如表示数字、字符和字符串的类型,实际上就是命名型类型,这些类型在 Swift 标准库中是使用结构体来定义和实现的。因为它们是命名型类型,因此你可以按照 [扩展](../chapter2/20_Extensions.md) 和 [扩展声明](./06_Declarations.md#extension_declaration) 中讨论的那样,声明一个扩展来增加它们的行为以满足你程序的需求。
那些通常被其它语言认为是基本或原始的数据型类型,比如表示数字、字符和字符串的类型,实际上就是命名型类型,这些类型在 Swift 标准库中是使用结构体来定义和实现的。因为它们是命名型类型,因此你可以按照 [扩展](../02_language_guide/20_Extensions.md) 和 [扩展声明](./06_Declarations.md#extension-declaration) 中讨论的那样,声明一个扩展来增加它们的行为以满足你程序的需求。
*复合型类型*是没有名字的类型,它由 Swift 本身定义。Swift 存在两种复合型类型:函数类型和元组类型。一个复合型类型可以包含命名型类型和其它复合型类型。例如,元组类型 `(Int, (Int, Int))` 包含两个元素:第一个是命名型类型 `Int`,第二个是另一个复合型类型 `(Int, Int)`
@ -14,40 +14,41 @@ Swift 语言存在两种类型:命名型类型和复合型类型。*命名型
> 类型语法
>
> *类型* → [*数组类型*](#array-type)
> *类型* → [函数类型](#function-type)
>
> *类型* → [*字典类型*](#dictionary-type)
> *类型* → [数组类型](#array-type)
>
> *类型* → [*函数类型*](#function-type)
> *类型* → [字典类型](#dictionary-type)
>
> *类型* → [*类型标识*](#type-identifier)
> *类型* → [类型标识](#type-identifier)
>
> *类型* → [*元组类型*](#tuple-type)
> *类型* → [元组类型](#tuple-type)
>
> *类型* → [*可选类型*](#optional-type)
> *类型* → [可选类型](#optional-type)
>
> *类型* → [*隐式解析可选类型*](#implicitly-unwrapped-optional-type)
> *类型* → [隐式解析可选类型](#implicitly-unwrapped-optional-type)
>
> *类型* → [*协议合成类型*](#protocol-composition-type)
> *类型* → [协议合成类型](#protocol-composition-type)
>
> *类型* [*元型类型*](#metatype-type)
> *类型* →[不透明类型](#opaque-type)
>
> *类型***任意类型**
> *类型*[元型类型](#metatype-type)
>
> *类型***自身类型**
> *类型*[自身类型](#self-type)
>
> *类型*[*(类型)*](#type)
> *类型***Any**
>
> *类型***** [类型](#type) ****
## 类型注解 {#type-annotation}
*类型注解*显式地指定一个变量或表达式的类型。类型注解始于冒号 `:` 终于类型,比如下面两个例子:
## 类型注解 {#type-annotation-h}
*类型注解*显式地指定一个变量或表达式的类型。类型注解冒号 `:`)开始, 以类型结尾,比如下面两个例子:
```swift
let someTuple: (Double, Double) = (3.14159, 2.71828)
func someFunction(a: Int) { /* ... */ }
```
在第一个例子中,表达式 `someTuple` 的类型被指定为 `(Double, Double)`。在第二个例子中,函数 `someFunction` 的参 `a` 的类型被指定为 `Int`
在第一个例子中,表达式 `someTuple` 的类型被指定为 `(Double, Double)`。在第二个例子中,函数 `someFunction``a` 的类型被指定为 `Int`
类型注解可以在类型之前包含一个类型特性的可选列表。
@ -55,11 +56,10 @@ func someFunction(a: Int) { /* ... */ }
>
#### type-annotation {#type-annotation}
> *类型注解***:** [*特性列表*](./07_Attributes.md#attributes)<sub>可选</sub> **输入输出参数**<sub>可选</sub> [*类型*](#type)
>
> *类型注解***:** [特性列表](./07_Attributes.md#attributes)<sub>可选</sub> **输入输出参数**<sub>可选</sub> [类型](#type)
## 类型标识符 {#type-identifier}
类型标识符引用命名型类型,还可引用命名型或复合型类型的别名。
## 类型标识符 {#type-identifier-h}
*类型标识符*可以引用命名型类型,还可引用命名型或复合型类型的别名。
大多数情况下,类型标识符引用的是与之同名的命名型类型。例如类型标识符 `Int` 引用命名型类型 `Int`,同样,类型标识符 `Dictionary<String, Int>` 引用命名型类型 `Dictionary<String, Int>`
@ -80,17 +80,15 @@ var someValue: ExampleModule.MyType
>
#### type-identifier {#type-identifier}
> *类型标识符* → [*类型名称*](#type-name) [*泛型参子句*](./09_Generic_Parameters_and_Arguments.md#generic_argument_clause)<sub>可选</sub> | [*类型名称*](#type-name) [*泛型参子句*](./09_Generic_Parameters_and_Arguments.md#generic_argument_clause)<sub>可选</sub> **.** [*类型标识符*](#type-identifier)
>
> *类型标识符* → [类型名称](#type-name) [泛型参子句](./09_Generic_Parameters_and_Arguments.md#generic-argument-clause)<sub>可选</sub> | [类型名称](#type-name) [泛型参子句](./09_Generic_Parameters_and_Arguments.md#generic-argument-clause)<sub>可选</sub> **.** [类型标识符](#type-identifier)
#### type-name {#type-name}
> *类型名称* → [*标识符*](./02_Lexical_Structure.md#identifier)
>
> *类型名称* → [标识符](./02_Lexical_Structure.md#identifier)
## 元组类型 {#tuple-type}
元组类型是使用括号括起来的零个或多个类型,类型间用逗号隔开。
## 元组类型 {#tuple-type-h}
*元组类型*是使用括号括起来的零个或多个类型,类型间用逗号隔开。
你可以使用元组类型作为一个函数的返回类型,这样就可以使函数返回多个值。你也可以命名元组类型中的元素,然后用这些名字来引用每个元素的值。元素的名字由一个标识符紧跟一个冒号 `(:)` 组成。[函数和多返回值](../chapter2/06_Functions.md#functions_with_multiple_return_values) 章节里有一个展示上述特性的例子。
你可以使用元组类型作为一个函数的返回类型,这样就可以使函数返回多个值。你也可以命名元组类型中的元素,然后用这些名字来引用每个元素的值。元素的名字由一个标识符紧跟一个冒号 `(:)` 组成。[函数和多返回值](../02_language_guide/06_Functions.md#functions-with-multiple-return-values) 章节里有一个展示上述特性的例子。
当一个元组类型的元素有名字的时候,这个名字就是类型的一部分。
@ -107,37 +105,37 @@ someTuple = (left: 5, right: 5) // 错误:命名类型不匹配
>
#### tuple-type {#tuple-type}
> *元组类型***(** **)** | **(** [*元组类型元素*](#tuple-type-element) **,** [*元组类型元素列表*](#tuple-type-element-list) **)**
> *元组类型***(** **)** | **(** [元组类型元素](#tuple-type-element) **,** [元组类型元素列表](#tuple-type-element-list) **)**
>
#### tuple-type-element-list {#tuple-type-element-list}
> *元组类型元素列表* → [*元组类型元素*](#tuple-type-element) | [*元组类型元素*](#tuple-type-element) **,** [*元组类型元素列表*](#tuple-type-element-list)
> *元组类型元素列表* → [元组类型元素](#tuple-type-element) | [元组类型元素](#tuple-type-element) **,** [元组类型元素列表](#tuple-type-element-list)
>
#### tuple-type-element {#tuple-type-element}
> *元组类型元素* → [*元素名*](#element-name) [*类型注解*](#type-annotation) | [*类型*](#type)
> *元组类型元素* → [元素名](#element-name) [类型注解](#type-annotation) | [类型](#type)
>
#### element-name {#element-name}
> *元素名* → [*标识符*](./02_Lexical_Structure.md#identifier)
> *元素名* → [标识符](./02_Lexical_Structure.md#identifier)
>
## 函数类型 {#function-type}
函数类型表示一个函数、方法或闭包的类型,它由参类型和返回值类型组成,中间用箭头(`->`)隔开:
## 函数类型 {#function-type-h}
*函数类型*表示一个函数、方法或闭包的类型,它由参类型和返回值类型组成,中间用箭头(`->`)隔开:
> `参类型`->`返回值类型`
> `参类型`->`返回值类型`
*参类型*是由逗号间隔的类型列表。由于*返回值类型*可以是元组类型,所以函数类型支持多返回值的函数与方法。
*参类型*是由逗号间隔的类型列表。由于*返回值类型*可以是元组类型,所以函数类型支持多返回值的函数与方法。
你可以对参类型为 `() -> T`(其中 T 是任何类型)的函数使用 `autoclosure` 特性这会自动将参数表达式转化为闭包,表达式的结果即闭包返回值。这从语法结构上提供了一种便捷:延迟对表达式的求值,直到其值在函数体中被调用。以自动闭包做为参的函数类型的例子详见 [自动闭包](../chapter2/07_Closures.md#autoclosures)。
你可以对参类型为 `() -> T`(其中 T 是任何类型)的函数使用 `autoclosure` 特性这会在调用侧隐式创建一个闭包。这从语法结构上提供了一种便捷:延迟对表达式的求值,直到其值在函数体中被调用。以自动闭包做为参的函数类型的例子详见 [自动闭包](../02_language_guide/07_Closures.md#autoclosures)。
函数类型可以拥有一个可变参数作为*参数类型*中的最后一个参数。从语法角度上讲,可变参数由一个基础类型名字紧随三个点(`...`)组成,如 `Int...`。可变参数被认为是一个包含了基础类型元素的数组。即 `Int...` 就是 `[Int]`。关于使用可变参数的例子,请参阅 [可变参数](../chapter2/06_Functions.md#variadic_parameters)。
函数类型可以拥有一个可变参数在*形参类型*中。从语法角度上讲,可变参数由一个基础类型名字紧随三个点(`...`)组成,如 `Int...`。可变参数被认为是一个包含了基础类型元素的数组。即 `Int...` 就是 `[Int]`。关于使用可变参数的例子,请参阅 [可变参数](../02_language_guide/06_Functions.md#variadic-parameters)。
为了指定一个 `in-out` 参数,可以在参类型前加 `inout` 前缀。但是你不可以对可变参数或返回值类型使用 `inout`。关于这种参的详细讲解请参阅 [输入输出参数](../chapter2/06_Functions.md#in_out_parameters)。
为了指定一个 `in-out` 参数,可以在参类型前加 `inout` 前缀。但是你不可以对可变参数或返回值类型使用 `inout`。关于这种参的详细讲解请参阅 [输入输出参数](../02_language_guide/06_Functions.md#in-out-parameters)。
如果一个函数类型只有一个形式参数而且形式参数的类型是元组类型,那么元组类型在写函数类型的时候必须用圆括号括起来。比如说,`((Int, Int)) -> Void` 是接收一个元组 `(Int, Int)` 作为形式参数并且不返回任何值的函数类型。与此相对,不加括号的 `(Int, Int) -> Void` 是一个接收两个 `Int` 作为形式参数并且不返回任何值的函数类型。相似地,因为 `Void` 是空元组类型 `()` 的别名,函数类型 `(Void)-> Void``(()) -> ()` 是一样的 - 一个将空元组作为唯一参的函数。但这些类型和无变量的函数类型 `() -> ()` 是不一样的。
如果函数类型只有一个类型是元组类型的一个形参,那么元组类型在写函数类型的时候必须用圆括号括起来。比如说,`((Int, Int)) -> Void` 是接收一个元组 `(Int, Int)` 作为形并且不返回任何值的函数类型。与此相对,不加括号的 `(Int, Int) -> Void` 是一个接收两个 `Int` 作为形并且不返回任何值的函数类型。相似地,因为 `Void` 是空元组类型 `()` 的别名,函数类型 `(Void)-> Void``(()) -> ()` 是一样的 - 一个将空元组作为唯一参的函数。但这些类型和 `() -> ()` 是不一样的 - 一个无实参的函数
函数和方法中的变量名并不是函数类型的一部分。例如:
函数和方法中的实参名并不是函数类型的一部分。例如:
```swift
func someFunction(left: Int, right: Int) {}
@ -156,7 +154,7 @@ func functionWithDifferentNumberOfArguments(left: Int, right: Int, top: Int) {}
f = functionWithDifferentNumberOfArguments // 错误
```
由于变量标签不是函数类型的一部分,你可以在写函数类型的时候省略它们。
由于实参标签不是函数类型的一部分,你可以在写函数类型的时候省略它们。
```swift
var operation: (lhs: Int, rhs: Int) -> Int // 错误
@ -164,14 +162,14 @@ var operation: (_ lhs: Int, _ rhs: Int) -> Int // 正确
var operation: (Int, Int) -> Int // 正确
```
如果一个函数类型包涵多个箭头(->),那么函数类型将从右向左进行组合。例如,函数类型 `(Int) -> (Int) -> Int` 可以理解为 `(Int) -> ((Int) -> Int)`,也就是说,该函数类型的参数为 `Int` 类型,其返回类型是一个参数类型为 `Int`,返回类型为 `Int` 的函数。
如果一个函数类型包涵多个箭头(->),那么函数类型将从右向左进行组合。例如,函数类型 `(Int) -> (Int) -> Int` 可以理解为 `(Int) -> ((Int) -> Int)`,也就是说,该函数传入 `Int`,并返回另一个传入并返回 `Int` 的函数。
函数类型若要抛出错误就必须使用 `throws` 关键字来标记,若要重抛错误必须使用 `rethrows` 关键字来标记。`throws` 关键字是函数类型的一部分,非抛出函数是抛出函数函数的一个子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。抛出和重抛函数的相关描述见章节 [抛出函数与方法](./06_Declarations.md#throwing_functions_and_methods) 和 [重抛函数与方法](./06_Declarations.md#rethrowing_functions_and_methods)。
函数类型若要抛出重抛错误必须使用 `throws` 关键字来标记。`throws` 关键字是函数类型的一部分,非抛出函数是抛出函数子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。抛出和重抛函数的相关描述见章节 [抛出函数与方法](./06_Declarations.md#throwing-functions-and-methods) 和 [重抛函数与方法](./06_Declarations.md#rethrowing-functions-and-methods)。
### 对非逃逸闭包的限制 {#Restrictions for Nonescaping Closures}
当非逃逸闭包函数是参时,不能存储在属性、变量或任何 `Any` 类型的常量中,因为这可能导致值的逃逸。
当非逃逸闭包函数是参时,不能存储在属性、变量或任何 `Any` 类型的常量中,因为这可能导致值的逃逸。
当非逃逸闭包函数是参时,不能作为参传递到另一个非逃逸闭包函数中。这样的限制可以让 Swift 在编译时就完成更的内存访问冲突检查,而不是在运行时。举个例子:
当非逃逸闭包函数是参时,不能作为参传递到另一个非逃逸闭包函数中。这样的限制可以让 Swift 在编译时就完成更的内存访问冲突检查,而不是在运行时。举个例子:
```swift
let external: (Any) -> Void = { _ in () }
@ -187,39 +185,33 @@ func takesTwoFunctions(first: (Any) -> Void, second: (Any) -> Void) {
}
```
在上面代码里,`takesTwoFunctions(first:second:)` 的两个参都是函数。它们都没有标记为 `@escaping`, 因此它们都是非逃逸的。
在上面代码里,`takesTwoFunctions(first:second:)` 的两个参都是函数。它们都没有标记为 `@escaping`, 因此它们都是非逃逸的。
上述例子里的被标记为“错误”的四个函数调用会产生编译错误。因为参 `first``second` 是非逃逸函数,它们不能够作为参被传递到另一个非闭包函数。相对的, 标记“正确”的两个函数不会产生编译错误。这些函数调用不会违反限制,因为 `external` 不是 `takesTwoFunctions(first:second:)` 的参之一。
上述例子里的被标记为“错误”的四个函数调用会产生编译错误。因为`first``second` 是非逃逸函数,它们不能够作为参被传递到另一个非闭包函数。相对的, 标记“正确”的两个函数不会产生编译错误。这些函数调用不会违反限制,因为 `external` 不是 `takesTwoFunctions(first:second:)`参之一。
如果你需要避免这个限制,标记其中之一的参数为逃逸,或者使用 `withoutActuallyEscaping(_:do:)` 函数临时转换非逃逸函数的其中一个参数为逃逸函数。关于避免内存访问冲突,可以参阅[内存安全](../chapter2/24_Memory_Safety.md)。
如果你需要避免这个限制,标记其中一个形参为逃逸,或者使用 `withoutActuallyEscaping(_:do:)` 函数临时转换其中一个非逃逸函数形参为逃逸函数。关于避免内存访问冲突,可以参阅 [内存安全](../02_language_guide/25_Memory_Safety.md)。
> 函数类型语法
>
#### function-type {#function-type}
> *函数类型* → [*特性列表*](./07_Attributes.md#attributes)<sub>可选</sub> [*函数类型子句*](#function-type-argument-clause) **throws**<sub>可选</sub> **->** [*类型*](#type)
>
> *函数类型* → [*特性列表*](./07_Attributes.md#attributes)<sub>可选</sub> [*函数类型子句*](#function-type-argument-clause) **rethrows­** **->** [*类型*](#type)
>
> *函数类型* → [特性列表](./07_Attributes.md#attributes)<sub>可选</sub> [函数类型子句](#function-type-argument-clause) **throws**<sub>可选</sub> **->** [类型](#type)
#### function-type-argument-clause {#function-type-argument-clause}
> *函数类型子句***(**­ **)**­
> *函数类型子句***(** [*函数类型参列表*](#function-type-argument-list) *...*­ <sub>可选</sub> **)**
>
> *函数类型子句***(** [函数类型参列表](#function-type-argument-list) *...*­ <sub>可选</sub> **)**
#### function-type-argument-list {#function-type-argument-list}
> *函数类型参列表* → [*函数类型参数*](function-type-argument) | [*函数类型参数*](function-type-argument) [*函数类型参列表*](#function-type-argument-list)
>
> *函数类型参列表* → [函数类型](#function-type-argument) | [函数类型](#function-type-argument) [函数类型参列表](#function-type-argument-list)
#### function-type-argument {#function-type-argument}
> *函数类型参数* → [*特性列表*](./07_Attributes.md#attributes)<sub>可选</sub> **输入输出参数**<sub>可选</sub> [*类型*](#type) | [*参数标签*](#argument-label) [*类型注解*](#type-annotation)
>
> *函数类型实参* → [特性列表](./07_Attributes.md#attributes)<sub>可选</sub> **输入输出参数**<sub>可选</sub> [类型](#type) | [实参标签](#argument-label) [类型注解](#type-annotation)
#### argument-label {#argument-label}
> *参标签* → [*标识符*](./02_Lexical_Structure.md#identifier)
>
> *参标签* → [标识符](./02_Lexical_Structure.md#identifier)
## 数组类型 {#array-type}
## 数组类型 {#array-type-h}
Swift 语言为标准库中定义的 `Array<Element>` 类型提供了如下语法糖:
> [`类型`]
@ -242,16 +234,16 @@ var array3D: [[[Int]]] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
访问一个多维数组的元素时,最左边的下标指向最外层数组的相应位置元素。接下来往右的下标指向第一层嵌入的相应位置元素,依次类推。这就意味着,在上面的例子中,`array3D[0]``[[1, 2], [3, 4]]``array3D[0][1]``[3, 4]``array3D[0][1][1]` 则是 `4`
关于 Swift 标准库中 `Array` 类型的详细讨论,请参阅 [数组](../chapter2/04_Collection_Types.md#arrays)。
关于 Swift 标准库中 `Array` 类型的详细讨论,请参阅 [数组](../02_language_guide/04_Collection_Types.md#arrays)。
> 数组类型语法
>
#### array-type {#array-type}
> *数组类型***[** [*类型*](#type) **]**
> *数组类型***[** [类型](#type) **]**
>
## 字典类型 {#dictionary-type}
## 字典类型 {#dictionary-type-h}
Swift 语言为标准库中定义的 `Dictionary<Key, Value>` 类型提供了如下语法糖:
> [`键类型` : `值类型`]
@ -270,16 +262,16 @@ let someDictionary: Dictionary<String, Int> = ["Alex": 31, "Paul": 39]
字典中键的类型必须符合 Swift 标准库中的 `Hashable` 协议。
关于 Swift 标准库中 `Dictionary` 类型的详细讨论,请参阅 [字典](../chapter2/04_Collection_Types.md#dictionaries)。
关于 Swift 标准库中 `Dictionary` 类型的详细讨论,请参阅 [字典](../02_language_guide/04_Collection_Types.md#dictionaries)。
> 字典类型语法
>
#### dictionary-type {#dictionary-type}
> *字典类型***[** [*类型*](#type) **:** [*类型*](#type) **]**
> *字典类型***[** [类型](#type) **:** [类型](#type) **]**
>
## 可选类型 {#optional-type}
## 可选类型 {#optional-type-h}
Swift 定义后缀 `?` 来作为标准库中定义的命名型类型 `Optional<Wrapped>` 的语法糖。换句话说,下面两个声明是等价的:
```swift
@ -300,18 +292,18 @@ optionalInteger! // 42
使用 `!` 运算符解包值为 `nil` 的可选值会导致运行错误。
你也可以使用可选链式调用和可选绑定来选择性在可选表达式上执行操作。如果值为 `nil`,不会执行任何操作,因此也就没有运行错误产生。
你也可以使用可选链式调用和可选绑定来选择性在可选表达式上执行操作。如果值为 `nil`,不会执行任何操作,因此也就没有运行错误产生。
更多细节以及更多如何使用可选类型的例子,请参阅 [可选类型](../chapter2/01_The_Basics.md#optionals)。
更多细节以及更多如何使用可选类型的例子,请参阅 [可选类型](../02_language_guide/01_The_Basics.md#optionals)。
> 可选类型语法
>
#### optional-type {#optional-type}
> *可选类型* → [*类型*](#type) **?**
> *可选类型* → [类型](#type) **?**
>
## 隐式解析可选类型 {#implicitly-unwrapped-optional-type}
## 隐式解析可选类型 {#implicitly-unwrapped-optional-type-h}
当可以被访问时Swift 语言定义后缀 `!` 作为标准库中命名类型 `Optional<Wrapped>` 的语法糖,来实现自动解包的功能。如果尝试对一个值为 `nil` 的可选类型进行隐式解包,将会产生运行时错误。因为隐式解包,下面两个声明等价:
```swift
@ -337,24 +329,23 @@ let implicitlyUnwrappedArray: [Int]! // 正确
可以使用可选链式调用对隐式解析可选表达式选择性地执行操作。如果值为 `nil`,就不会执行任何操作,因此也不会产生运行错误。
关于隐式解析可选类型的更多细节,请参阅 [隐式解析可选类型](../chapter2/01_The_Basics.md#implicityly_unwrapped_optionals)。
关于隐式解析可选类型的更多细节,请参阅 [隐式解析可选类型](../02_language_guide/01_The_Basics.md#implicityly-unwrapped-optionals)。
> 隐式解析可选类型语法
>
#### implicitly-unwrapped-optional-type {#implicitly-unwrapped-optional-type}
> *隐式解析可选类型* → [*类型*](#type) **!**
> *隐式解析可选类型* → [类型](#type) **!**
>
## 协议合成类型 {#protocol-composition-type}
协议合成类型定义了一种遵循协议列表中每个指定协议的类型,或者一个现有类型的子类并遵循协议列表中每个指定协议。协议合成类型只能用在类型注解、泛型参子句和泛型 `where` 子句中指定类型。
## 协议合成类型 {#protocol-composition-type-h}
*协议合成类型*定义了一种遵循协议列表中每个指定协议的类型,或者一个现有类型的子类并遵循协议列表中每个指定协议。协议合成类型只能用在类型注解、泛型参子句和泛型 `where` 子句中指定类型。
协议合成类型的形式如下:
> `Protocol 1` & `Procotol 2`
>
协议合成类型允许你指定一个值,其类型遵循多个协议的要求而不需要定义一个新的命名型协议来继承它想要符合的各个协议。比如,协议合成类型 `Protocol A & Protocol B & Protocol C` 等效于一个从 `Protocol A``Protocol B``Protocol C` 继承而来的新协议。同样的,你可以使用 `SuperClass & ProtocolA` 来取代明一个新的协议作为 `SuperClass` 的子类并遵循 `ProtocolA`
协议合成类型允许你指定一个值,其类型遵循多个协议的要求而不需要定义一个新的命名型协议来继承它想要符合的各个协议。比如,协议合成类型 `Protocol A & Protocol B & Protocol C` 等效于一个从 `Protocol A``Protocol B``Protocol C` 继承而来的新协议。同样的,你可以使用 `SuperClass & ProtocolA` 来取代明一个新的协议作为 `SuperClass` 的子类并遵循 `ProtocolA`
协议合成列表中的每一项都必须是下面所列情况之一,列表中最多只能包含一个类:
@ -373,15 +364,39 @@ typealias PQR = PQ & Q & R
>
#### protocol-composition-type {#protocol-composition-type}
> *协议合成类型* → [*协议标识符*](#protocol-identifier) & [*协议合成延续*](#protocol-composition-continuation)
> *协议合成类型* → [协议标识符](#protocol-identifier) & [协议合成延续](#protocol-composition-continuation)
>
#### protocol-composition-continuation {#protocol-composition-continuation}
> *协议合成延续* → [*协议标识符*](#protocol-identifier) | [*协议合成类型*](#protocol-composition-type)
>
> *协议合成延续* → [协议标识符](#protocol-identifier) | [协议合成类型](#protocol-composition-type)
## 类型 {#metatype-type}
元类型是指任意类型的类型,包括类类型、结构体类型、枚举类型和协议类型。
## 不透明类型 {#opaque-type-h}
*不透明类型*定义了遵循某个协议或者合成协议的类型,但不需要指明底层的具体类型。
不透明类型可以作为函数或下标的返回值,亦或是属性的类型使用。
不透明类型不能作为元组类型的一部分或范型类型使用,比如数组元素类型或者可选值的包装类型。
不透明类型的形式如下:
> some `constraint`
*constraint* 可以是类类型,协议类型,协议组合类型或者 `Any`。值只有当它遵循该协议或者组合协议,或者从该类继承的时候,才能作为这个不透明类型的实例使用。和不透明值交互的代码只能使用该值定义在 *constraint* 上的接口。
协议声明里不能包括不透明类型。类不能使用不透明类型作为非 final 方法的返回值。
使用不透明类型作为返回值的函数必须返回单一公用底层类型。返回的类型可以包含函数范型类型形参的一部分。举个例子,函数 `someFunction<T>()` 可以返回类型 `T` 或者 `Dictionary<String,T>` 的值。
> 不透明类型语法
#### opaque-type {#opaque-type}
> *不透明类型***some** [type](#type)
## 元类型 {#metatype-type-h}
*元类型*是指任意类型的类型,包括类类型、结构体类型、枚举类型和协议类型。
类、结构体或枚举类型的元类型是相应的类型名紧跟 `.Type`。协议类型的元类型——并不是运行时遵循该协议的具体类型——是该协议名字紧跟 `.Protocol`。比如,类 `SomeClass` 的元类型就是 `SomeClass.Type`,协议 `SomeProtocol` 的元类型就是 `SomeProtocal.Protocol`
@ -427,34 +442,76 @@ let anotherInstance = metatype.init(string: "some string")
>
#### metatype-type {#metatype-type}
> *元类型* → [*类型*](#type) **.** **Type** | [*类型*](#type) **.** **Protocol**
>
> *元类型* → [类型](#type) **.** **Type** | [类型](#type) **.** **Protocol**
## 类型继承子句 {#type-inheritance-clause}
类型继承子句被用来指定一个命名型类型继承自哪个类、采纳哪些协议。类型继承子句开始于冒号 `:`,其后是类型标识符列表。
## 自身类型 {#self-type-h}
类可以继承自单个超类,并遵循任意数量的协议。当定义一个类时,超类的名字必须出现在类型标识符列表首位,然后跟上该类需要遵循的任意数量的协议。如果一个类不是从其它类继承而来,那么列表可以以协议开头。关于类继承更多的讨论和例子,请参阅 [继承](../chapter2/13_Inheritance.md)
`Self` 类型不是具体的类型,而是让你更方便的引用当前类型,不需要重复或者知道该类的名字
在协议声明或者协议成员声明时,`Self` 类型引用的是最终遵循该协议的类型。
在结构体类或者枚举值声明时Self 类型引用的是声明的类型。在某个类型成员声明时Self 类型引用的是该类型。在类成员声明时,`Self` 只能在以下几种情况中出现:
* 作为方法的返回类型
* 作为只读下标的返回类型
* 作为只读计算属性的类型
* 在方法体中
举个例子,下面的代码演示了返回值是 `Self` 的实例方法 `f`
```swift
class Superclass {
func f() -> Self { return self }
}
let x = Superclass()
print(type(of: x.f()))
// 打印 "Superclass"
class Subclass: Superclass { }
let y = Subclass()
print(type(of: y.f()))
// 打印 "Subclass"
let z: Superclass = Subclass()
print(type(of: z.f()))
// 打印 "Subclass"
```
上面例子的最后一部分表明 `Self` 引用的是值 `z` 的运行时类型 `Subclass` ,而不是变量本身的编译时类型 `Superclass`
在嵌套类型声明时,`Self` 类型引用的是最内层声明的类型。
`Self` 类型引用的类型和 Swift 标准库中 [type(of:)](https://developer.apple.com/documentation/swift/2885064-type) 函数的结果一样。使用 `Self.someStaticMember` 访问当前类型中的成员和使用 `type(of: self).someStaticMember` 是一样的。
> 自身类型语法
#### self-type{#self-type}
> *自身类型* → **Self**
## 类型继承子句 {#type-inheritance-clause-h}
*类型继承子句*被用来指定一个命名型类型继承自哪个类、采纳哪些协议。类型继承子句开始于冒号 `:`,其后是类型标识符列表。
类可以继承自单个超类,并遵循任意数量的协议。当定义一个类时,超类的名字必须出现在类型标识符列表首位,然后跟上该类需要遵循的任意数量的协议。如果一个类不是从其它类继承而来,那么列表可以以协议开头。关于类继承更多的讨论和例子,请参阅 [继承](../02_language_guide/13_Inheritance.md)。
其它命名型类型只能继承自或采纳一系列协议。协议类型可以继承自任意数量的其他协议。当一个协议类型继承自其它协议时,其它协议中定义的要求会被整合在一起,然后从当前协议继承的任意类型必须符合所有这些条件。
枚举定义中的类型继承子句可以是一系列协议,或者是指定单一的命名类型,此时枚举为其用例分配原始值。在枚举定义中使用类型继承子句来指定原始值类型的例子,请参阅 [原始值](../chapter2/08_Enumerations.md#raw_values)。
枚举定义中的类型继承子句可以是一系列协议,或者是指定单一的命名类型,此时枚举为其用例分配原始值。在枚举定义中使用类型继承子句来指定原始值类型的例子,请参阅 [原始值](../02_language_guide/08_Enumerations.md#raw-values)。
> 类型继承子句语法
>
#### type_inheritance_clause {#type-inheritance-clause}
> *类型继承子句***:** [*类型继承列表*](#type-inheritance-list)
#### type-inheritance-clause {#type-inheritance-clause}
> *类型继承子句***:** [类型继承列表](#type-inheritance-list)
>
#### type-inheritance-list {#type-inheritance-list}
> *类型继承列表* → [*类型标识符*](#type-identifier) | [*类型标识符*](#type-identifier) **,** [*类型继承列表*](#type-inheritance-list)
>
> *类型继承列表* → [类型标识符](#type-identifier) | [类型标识符](#type-identifier) **,** [类型继承列表](#type-inheritance-list)
#### class-requirement {#class-requirement}
## 类型推断
## 类型推断 {#type-inference}
Swift 广泛使用类型推断,从而允许你省略代码中很多变量和表达式的类型或部分类型。比如,对于 `var x: Int = 0`,你可以完全省略类型而简写成 `var x = 0`,编译器会正确推断出 `x` 的类型 `Int`。类似的,当完整的类型可以从上下文推断出来时,你也可以省略类型的一部分。比如,如果你写了 `let dict: Dictionary = ["A" : 1]`,编译器能推断出 `dict` 的类型是 `Dictionary<String, Int>`
Swift 广泛使用*类型推断*,从而允许你省略代码中很多变量和表达式的类型或部分类型。比如,对于 `var x: Int = 0`,你可以完全省略类型而简写成 `var x = 0`,编译器会正确推断出 `x` 的类型 `Int`。类似的,当完整的类型可以从上下文推断出来时,你也可以省略类型的一部分。比如,如果你写了 `let dict: Dictionary = ["A" : 1]`,编译器能推断出 `dict` 的类型是 `Dictionary<String, Int>`
在上面的两个例子中,类型信息从表达式树的叶子节点传向根节点。也就是说,`var x: Int = 0``x` 的类型首先根据 `0` 的类型进行推断,然后将该类型信息传递到根节点(变量 `x`)。

View File

@ -8,33 +8,33 @@ Swift 中存在四种表达式:前缀表达式,二元表达式,基本表
>
#### expression {#expression}
> *表达式* → [*try 运算符*](#try-operator)<sub>可选</sub> [*前缀表达式*](#prefix-expression) [*二元表达式列表*](#binary-expressions)<sub>可选</sub>
>
> *表达式* → [try 运算符](#try-operator)<sub>可选</sub> [前缀表达式](#prefix-expression) [二元表达式列表](#binary-expressions)<sub>可选</sub>
#### expression-list {#expression-list}
> *表达式列表* → [*表达式*](#expression) | [*表达式*](#expression) **,** [*表达式列表*](#expression-list)
> *表达式列表* → [表达式](#expression) | [表达式](#expression) **,** [表达式列表](#expression-list)
>
## 前缀表达式 {#prefix-expressions}
前缀表达式由可选的前缀运算符和表达式组成。前缀运算符只接收一个参数,表达式则紧随其后。
关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.md) 和 [高级运算符](../chapter2/26_Advanced_Operators.md)。
关于这些运算符的更多信息,请参阅 [基本运算符](../02_language_guide/02_Basic_Operators.md) 和 [高级运算符](../02_language_guide/27_Advanced_Operators.md)。
关于 Swift 标准库提供的运算符的更多信息,请参阅 [*Operators Declarations*](https://developer.apple.com/documentation/swift/operator_declarations)。
除了标准库运算符,你也可以对某个变量使用 `&` 运算符,从而将其传递给函数的输入输出参数。更多信息,请参阅 [输入输出参数](../chapter2/06_Functions.md#in_out_parameters)。
除了标准库运算符,你也可以对某个变量使用 `&` 运算符,从而将其传递给函数的输入输出参数。更多信息,请参阅 [输入输出参数](../02_language_guide/06_Functions.md#in-out-parameters)。
> 前缀表达式语法
>
#### prefix-expression {#prefix-expression}
> *前缀表达式* → [*前缀运算符*](./02_Lexical_Structure.md#prefix-operator)<sub>可选</sub> [*后缀表达式*](#postfix-expression)
> *前缀表达式* → [前缀运算符](./02_Lexical_Structure.md#prefix-operator)<sub>可选</sub> [后缀表达式](#postfix-expression)
>
> *前缀表达式* → [*输入输出表达式*](#in-out-expression)
> *前缀表达式* → [输入输出表达式](#in-out-expression)
>
#### in-out-expression {#in-out-expression}
> *输入输出表达式***&** [*标识符*](./02_Lexical_Structure.md#identifier)
> *输入输出表达式***&** [标识符](./02_Lexical_Structure.md#identifier)
>
### Try 运算符 {#try-operator}
@ -57,7 +57,7 @@ try 表达式由 `try` 运算符加上紧随其后的可抛出错误的表达式
如果可抛出错误的表达式抛出了错误,将会引发运行时错误。
在二进制运算符左侧的表达式被标记上 `try``try?` 或者 `try!` 时,这个运算符对整个二进制表达式都产生作用。也就是说,你可以使用括号来明确运算符的作用范围。
在二运算符左侧的表达式被标记上 `try``try?` 或者 `try!` 时,这个运算符对整个二表达式都产生作用。也就是说,你可以使用括号来明确运算符的作用范围。
```swift
sum = try someThrowingFunction() + anotherThrowingFunction() // try 对两个函数调用都产生作用
@ -65,9 +65,9 @@ sum = try (someThrowingFunction() + anotherThrowingFunction()) // try 对两个
sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误try 只对第一个函数调用产生作用
```
`try` 表达式不能出现在二进制运算符的的右侧,除非二进制运算符是赋值运算符或者 `try` 表达式是被圆括号括起来的。
`try` 表达式不能出现在二运算符的的右侧,除非二运算符是赋值运算符或者 `try` 表达式是被圆括号括起来的。
关于 `try``try?``try!` 的更多信息,以及该如何使用的例子,请参阅 [错误处理](../chapter2/17_Error_Handling.md)。
关于 `try``try?``try!` 的更多信息,以及该如何使用的例子,请参阅 [错误处理](../02_language_guide/17_Error_Handling.md)。
> Try 表达式语法
>
@ -81,7 +81,7 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误try
> `左侧参数` `二元运算符` `右侧参数`
>
关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.md) 和 [高级运算符](../chapter2/26_Advanced_Operators.md)。
关于这些运算符的更多信息,请参阅 [基本运算符](../02_language_guide/02_Basic_Operators.md) 和 [高级运算符](../02_language_guide/27_Advanced_Operators.md)。
关于 Swift 标准库提供的运算符的更多信息,请参阅 [*Swift Standard Library Operators Reference*](https://developer.apple.com/documentation/swift/operator_declarations)。
@ -94,17 +94,17 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误try
#### binary-expression {#binary-expression}
> 二元表达式语法
>
> *二元表达式* → [*二元运算符*](./02_Lexical_Structure.md#binary-operator) [*前缀表达式*](#prefix-expression)
> *二元表达式* → [二元运算符](./02_Lexical_Structure.md#binary-operator) [前缀表达式](#prefix-expression)
>
> *二元表达式* → [*赋值运算符*](#assignment-operator) [*try 运算符*](#try-operator)<sub>可选</sub> [*前缀表达式*](#prefix-expression)
> *二元表达式* → [赋值运算符](#assignment-operator) [try 运算符](#try-operator)<sub>可选</sub> [前缀表达式](#prefix-expression)
>
> *二元表达式* → [*条件运算符*](#conditional-operator) [*try 运算符*](#try-operator)<sub>可选</sub> [*前缀表达式*](#prefix-expression)
> *二元表达式* → [条件运算符](#conditional-operator) [try 运算符](#try-operator)<sub>可选</sub> [前缀表达式](#prefix-expression)
>
> *二元表达式* → [*类型转换运算符*](#type-casting-operator)
> *二元表达式* → [类型转换运算符](#type-casting-operator)
>
#### binary-expressions {#binary-expressions}
> *二元表达式列表* → [*二元表达式*](#binary-expression) [*二元表达式列表*](#binary-expressions)<sub>可选</sub>
> *二元表达式列表* → [二元表达式](#binary-expression) [二元表达式列表](#binary-expressions)<sub>可选</sub>
>
### 赋值表达式 {#assignment-operator}
@ -137,13 +137,13 @@ sum = (try someThrowingFunction()) + anotherThrowingFunction() // 错误try
如果条件为真,那么对第一个表达式进行求值并返回结果。否则,对第二个表达式进行求值并返回结果。未使用的表达式不会进行求值。
关于使用三元条件运算符的例子,请参阅 [三元条件运算符](../chapter2/02_Basic_Operators.md#ternary_conditional_operator)。
关于使用三元条件运算符的例子,请参阅 [三元条件运算符](../02_language_guide/02_Basic_Operators.md#ternary-conditional-operator)。
> 三元条件运算符语法
>
#### conditional-operator {#conditional-operator}
> *三元条件运算符***?** [*表达式*](#expression) **:**
> *三元条件运算符***?** [表达式](#expression) **:**
>
### 类型转换运算符 {#type-casting-operators}
@ -183,19 +183,19 @@ f(x as Any)
`as!` 运算符执行强制类型转换,返回目标类型的非可选值。如果转换失败,则会导致运行时错误。表达式 `x as! T` 效果等同于 `(x as? T)!`
关于类型转换的更多内容和例子,请参阅 [类型转换](../chapter2/18_Type_Casting.md)。
关于类型转换的更多内容和例子,请参阅 [类型转换](../02_language_guide/18_Type_Casting.md)。
#### type-casting-operator {#type-casting-operator}
> 类型转换运算符语法
>
> *类型转换运算符***is** [*类型*](./03_Types.md#type)
> *类型转换运算符***is** [类型](./03_Types.md#type)
>
> *类型转换运算符***as** [*类型*](./03_Types.md#type)
> *类型转换运算符***as** [类型](./03_Types.md#type)
>
> *类型转换运算符***as** **?** [*类型*](./03_Types.md#type)
> *类型转换运算符***as** **?** [类型](./03_Types.md#type)
>
> *类型转换运算符***as** **!** [*类型*](./03_Types.md#type)
> *类型转换运算符***as** **!** [类型](./03_Types.md#type)
>
## 基本表达式 {#primary-expressions}
@ -205,25 +205,25 @@ f(x as Any)
>
#### primary-expression {#primary-expression}
> *基本表达式* → [*标识符*](./02_Lexical_Structure.md#identifier) [*泛型实参子句*](./09_Generic_Parameters_and_Arguments.md#generic-argument-clause)<sub>可选</sub>
> *基本表达式* → [标识符](./02_Lexical_Structure.md#identifier) [泛型实参子句](./09_Generic_Parameters_and_Arguments.md#generic-argument-clause)<sub>可选</sub>
>
> *基本表达式* → [*字面量表达式*](#literal-expression)
> *基本表达式* → [字面量表达式](#literal-expression)
>
> *基本表达式* → [*self 表达式*](#self-expression)
> *基本表达式* → [self 表达式](#self-expression)
>
> *基本表达式* → [*父类表达式*](#superclass-expression)
> *基本表达式* → [父类表达式](#superclass-expression)
>
> *基本表达式* → [*闭包表达式*](#closure-expression)
> *基本表达式* → [闭包表达式](#closure-expression)
>
> *基本表达式* → [*圆括号表达式*](#parenthesized-expression)
> *基本表达式* → [圆括号表达式](#parenthesized-expression)
>
> *基本表达式* → [*隐式成员表达式*](#implicit-member-expression)
> *基本表达式* → [隐式成员表达式](#implicit-member-expression)
>
> *基本表达式* → [*通配符表达式*](#wildcard-expression)
> *基本表达式* → [通配符表达式](#wildcard-expression)
>
> *基本表达式* → [*选择器表达式*](#selector-expression)
> *基本表达式* → [选择器表达式](#selector-expression)
>
> *基本表达式* → [*key-path字符串表达式*](#key-patch-string-expression)
> *基本表达式* → [key-path字符串表达式](#key-patch-string-expression)
>
### 字面量表达式 {#literal-expression}
@ -231,13 +231,20 @@ f(x as Any)
字面量 | 类型 | 值
:------------- | :---------- | :----------
`#file` | `String` | 所在的文件名
`#file` | `String` | 所在的文件名及模块
`#filePath` | `String` | 所在的文件路径
`#line` | `Int` | 所在的行数
`#column` | `Int` | 所在的列数
`#function` | `String` | 所在的声明的名字
`#dsohandle` | `UnsafeRawPointer` | 所使用的 DSO动态共享对象句柄
`#file` 表达式的值的格式是 *module*/*file**file* 是表达式所在的文件名,*module* 是文件所所在的模块名。`#filePath` 表达式的字符串值是表达式所在的文件在整个文件系统里的路径。所有这些值可以被 `#sourceLocation` 改变,详见 [行控制语句](./05_Statements.md#line-control-statements)。
对于 `function`,在函数中会返回当前函数的名字,在方法中会返回当前方法的名字,在属性的存取器中会返回属性的名字,在特殊的成员如 `init``subscript` 中会返回这个关键字的名字,在某个文件中会返回当前模块的名字。
> 注意
>
> 要解析 `#file` 表达式,第一个斜杠(/)之前的文本作为模块名,最后一个斜杠之后的文本作为文件名。将来,该字符串可能包含多个斜杠,例如 `MyModule/some/disambiguation/MyFile.swift`
对于 `#function`,在函数中会返回当前函数的名字,在方法中会返回当前方法的名字,在属性的存取器中会返回属性的名字,在特殊的成员如 `init``subscript` 中会返回这个关键字的名字,在某个文件中会返回当前模块的名字。
当其作为函数或者方法的默认参数值时,该字面量的值取决于函数或方法的调用环境。
@ -275,7 +282,7 @@ var emptyDictionary: [String : Double] = [:]
Xcode 使用 playground 字面量对程序编辑器中的颜色、文件或者图片创建可交互的展示。在 Xcode 之外的空白文本中playground 字面量使用一种特殊的字面量语法来展示。
更多关于在 Xcode 中使用 playground 字面量的信息,请参阅 [添加颜色、文件或图片字面量](https://help.apple.com/xcode/mac/current/#/dev4c60242fc)
更多关于在 Xcode 中使用 playground 字面量的信息,请参阅 [添加颜色、文件或图片字面量](https://help.apple.com/xcode/mac/current/#/dev4c60242fc)
> 字面量表达式语法
>
@ -283,55 +290,53 @@ Xcode 使用 playground 字面量对程序编辑器中的颜色、文件或者
>
#### literal-expression {#literal-expression}
>
> *字面量表达式* → [*字面量*](./02_Lexical_Structure.md#literal)
> *字面量表达式* → [字面量](./02_Lexical_Structure.md#literal)
>
> *字面量表达式* → [*数组字面量*](#array-literal) | [*字典字面量*](#dictionary-literal) | [*练习场字面量*](#playground-literal)
> *字面量表达式* → [数组字面量](#array-literal) | [字典字面量](#dictionary-literal) | [练习场字面量](#playground-literal)
>
> *字面量表达式***#file** | **#line** | **#column** | **#function**
> *字面量表达式***#file** **#filePath** | **#line** | **#column** | **#function**
>
>
#### array-literal {#array-literal}
>
> *数组字面量* → [[*数组字面量项列表*](#array-literal-items)<sub>可选</sub> **]**
> *数组字面量* → [[数组字面量项列表](#array-literal-items)<sub>可选</sub> **]**
>
>
#### array-literal-items {#array-literal-items}
>
> *数组字面量项列表* → [*数组字面量项*](#array-literal-item) **,**<sub>可选</sub> | [*数组字面量项*](#array-literal-item) **,** [*数组字面量项列表*](#array-literal-items)
> *数组字面量项列表* → [数组字面量项](#array-literal-item) **,**<sub>可选</sub> | [数组字面量项](#array-literal-item) **,** [数组字面量项列表](#array-literal-items)
>
>
#### array-literal-item {#array-literal-item}
>
> *数组字面量项* → [*表达式*](#expression)
> *数组字面量项* → [表达式](#expression)
>
>
>
#### dictionary-literal {#dictionary-literal}
>
> *字典字面量* → [[*字典字面量项列表*](#dictionary-literal-items) **]** | **[** **:** **]**
> *字典字面量* → [[字典字面量项列表](#dictionary-literal-items) **]** | **[** **:** **]**
>
>
#### dictionary-literal-items {#dictionary-literal-items}
>
> *字典字面量项列表* → [*字典字面量项*](#dictionary-literal-item) **,**<sub>可选</sub> | [*字典字面量项*](#dictionary-literal-item) **,** [*字典字面量项列表*](#dictionary-literal-items)
> *字典字面量项列表* → [字典字面量项](#dictionary-literal-item) **,**<sub>可选</sub> | [字典字面量项](#dictionary-literal-item) **,** [字典字面量项列表](#dictionary-literal-items)
>
>
#### dictionary-literal-item {#dictionary-literal-item}
>
> *字典字面量项* → [*表达式*](#expression) **:** [*表达式*](#expression)。
> *字典字面量项* → [表达式](#expression) **:** [表达式](#expression)。
>
>
#### playground-literal {#playground-literal}
>
> *playground 字面量***#colorLiteral ( red : [*表达式*](#expression) , green :[*表达式*](#expression) [*表达式*](#e[*表达式*](#expression) xpression) , blue :[*表达式*](#expression) , alpha : [*表达式*](#expression) )**
> *playground 字面量***#colorLiteral ( red : [表达式](#expression) , green :[表达式](#expression) [表达式](#e[*表达式*](#expression) xpression) , blue :[表达式](#expression) , alpha : [表达式](#expression) )**
>
> *playground 字面量***#fileLiteral ( resourceName : [*表达式*](#expression) )**
>
>
#### playground 字面量* → **#imageLiteral ( resourceName : [*表达式*](#expression) )**self_expression {#self-expression}
> *playground 字面量***#fileLiteral ( resourceName : [表达式](#expression) )**
>
> #### playground 字面量* → **#imageLiteral ( resourceName : [表达式](#expression) )**self-expression {#self-expression}
### Self 表达式
@ -376,16 +381,16 @@ struct Point {
>
#### self-expression {#self-expression}
> *self 表达式***self** | [*self 方法表达式*](#self-method-expression) [*self 下标表达式*](#self-subscript-expression) | [*self 构造器表达式*](#self-initializer-expression)
> *self 表达式***self** | [self 方法表达式](#self-method-expression) [self 下标表达式](#self-subscript-expression) | [self 构造器表达式](#self-initializer-expression)
>
>
#### self-method-expression {#self-method-expression}
> *self 方法表达式***self** **.** [*标识符*](./02_Lexical_Structure.md#identifier)
> *self 方法表达式***self** **.** [标识符](./02_Lexical_Structure.md#identifier)
>
#### self-subscript-expression {#self-subscript-expression}
> *self 下标表达式***self** **[** [*函数调用参数表*](#function-call-argument-list­) **]**
> *self 下标表达式***self** **[** [函数调用参数表](#function-call-argument-list­) **]**
>
#### self-initializer-expression {#self-initializer-expression}
@ -410,15 +415,15 @@ struct Point {
>
#### superclass-expression {#superclass-expression}
> *父类表达式* → [*父类方法表达式*](#superclass-method-expression) | [*父类下标表达式*](#superclass-subscript-expression) | [*父类构造器表达式*](#superclass-initializer-expression)
> *父类表达式* → [父类方法表达式](#superclass-method-expression) | [父类下标表达式](#superclass-subscript-expression) | [父类构造器表达式](#superclass-initializer-expression)
>
#### superclass-method-expression {#superclass-method-expression}
> *父类方法表达式***super** **.** [*标识符*](./02_Lexical_Structure.md#identifier)
> *父类方法表达式***super** **.** [标识符](./02_Lexical_Structure.md#identifier)
>
#### superclass-subscript-expression {#superclass-subscript-expression}
> *父类下标表达式***super** [[*函数调用参数表*](#function-call-argument-list­) **]**
> *父类下标表达式***super** [[函数调用参数表](#function-call-argument-list­) **]**
>
#### superclass-initializer-expression {#superclass-initializer-expression}
@ -430,12 +435,11 @@ struct Point {
```swift
{ (parameters) -> return type in
>
statements
}
```
闭包的参数声明形式跟函数一样,请参阅 [函数声明](./06_Declarations.md#function_declaration)。
闭包的参数声明形式跟函数一样,请参阅 [函数声明](./06_Declarations.md#function-declaration)。
闭包还有几种特殊的形式,能让闭包使用起来更加简洁:
@ -448,7 +452,6 @@ struct Point {
```swift
myFunction {
(x: Int, y: Int) -> Int in
>
return x + y
}
@ -462,11 +465,11 @@ myFunction { return $0 + $1 }
myFunction { $0 + $1 }
```
关于如何将闭包作为参数来传递的内容,请参阅 [函数调用表达式](#function_call_expression)。
关于如何将闭包作为参数来传递的内容,请参阅 [函数调用表达式](#function-call-expression)。
使用闭包表达式时,可以不必将其存储在一个变量或常量中,例如作为函数调用的一部分来立即使用一个闭包。在上面的例子中,传入 `myFunction` 的闭包表达式就是这种立即使用类型的闭包。因此,一个闭包是否逃逸与其使用时的上下文相关。一个会被立即调用或者作为函数的非逃逸参数传递的闭包表达式是非逃逸的,否则,这个闭包表达式是逃逸的。
关于逃逸闭包的内容,请参阅[逃逸闭包](./chapter2/07_Closures.md#escaping_closures)
关于逃逸闭包的内容,请参阅 [逃逸闭包](../02_language_guide/07_Closures.md#escaping-closures)
## 捕获列表 {#capture-lists}
默认情况下,闭包会捕获附近作用域中的常量和变量,并使用强引用指向它们。你可以通过一个*捕获列表*来显式指定它的捕获行为。
@ -524,7 +527,7 @@ myFunction { [unowned self] in print(self.title) } // 无主引用捕获
myFunction { [weak parent = self.parent] in print(parent!.title) }
```
关于闭包表达式的更多信息和例子,请参阅 [闭包表达式](../chapter2/07_Closures.md#closure_expressions)。关于捕获列表的更多信息和例子,请参阅 [解决闭包引起的循环强引用](../chapter2/23_Automatic_Reference_Counting.md#resolving_strong_reference_cycles_for_closures)。
关于闭包表达式的更多信息和例子,请参阅 [闭包表达式](../02_language_guide/07_Closures.md#closure-expressions)。关于捕获列表的更多信息和例子,请参阅 [解决闭包引起的循环强引用](../02_language_guide/24_Automatic_Reference_Counting.md#resolving-strong-reference-cycles-for-closures)。
> 闭包表达式语法
>
@ -532,39 +535,39 @@ myFunction { [weak parent = self.parent] in print(parent!.title) }
>
#### closure-expression {#closure-expression}
>
> *闭包表达式***{** [*闭包签名*](#closure-signature)<sub>可选</sub> [*语句*](#statements) **}**
> *闭包表达式***{** [闭包签名](#closure-signature)<sub>可选</sub> [语句](#statements) **}**
>
>
>
#### closure-signature {#closure-signature}
>
>
> 闭包签名* → [*参数子句*](#parameter-clause) [*函数结果*](05_Declarations.md#function-result)<sub>可选</sub> **in**
> 闭包签名* → [参数子句](#parameter-clause) [函数结果](./06_Declarations.md#function-result)<sub>可选</sub> **in**
>
> *闭包签名* → [*标识符列表*](#identifier-list) [*函数结果*](05_Declarations.md#function-result)<sub>可选</sub> **in**
> *闭包签名* → [标识符列表](#identifier-list) [函数结果](./06_Declarations.md#function-result)<sub>可选</sub> **in**
>
> *闭包签名* → [*捕获列表*](#capture-list) [*参数子句*](05_Declarations.md#parameter-clause) [*函数结果*](./06_Declarations.md#function-result)<sub>可选</sub> **in**
> *闭包签名* → [捕获列表](#capture-list) [参数子句](./06_Declarations.md#parameter-clause) [函数结果](./06_Declarations.md#function-result)<sub>可选</sub> **in**
>
> *闭包签名* → [*捕获列表*](#capture-list) [*标识符列表*](02_Lexical_Structure.md#identifier-list) [*函数结果*](./06_Declarations.md#function-result)<sub>可选</sub> **in**
> *闭包签名* → [捕获列表](#capture-list) [标识符列表](./02_Lexical_Structure.md#identifier-list) [函数结果](./06_Declarations.md#function-result)<sub>可选</sub> **in**
>
> *闭包签名* → [*捕获列表*](#capture-list) **in**
> *闭包签名* → [捕获列表](#capture-list) **in**
>
>
>
#### capture-list {#capture-list}
>
>
> 捕获列表* → [ [*捕获列表项列表*](#capture-list-items) **]**
> 捕获列表* → [ [捕获列表项列表](#capture-list-items) **]**
>
>
#### capture-list-items {#capture-list-items}
>
> *捕获列表项列表* → [*捕获列表项*](#capture-list-item) | [*捕获列表项*](#capture-list-item) **,** [*捕获列表项列表*](#capture-list-items)
> *捕获列表项列表* → [捕获列表项](#capture-list-item) | [捕获列表项](#capture-list-item) **,** [捕获列表项列表](#capture-list-items)
>
>
#### capture-list-item {#capture-list-item}
>
> *捕获列表项* → [*捕获说明符*](#capture-specifier)<sub>可选</sub> [*表达式*](#expression)
> *捕获列表项* → [捕获说明符](#capture-specifier)<sub>可选</sub> [表达式](#expression)
>
>
#### capture-specifier {#capture-specifier}
@ -589,7 +592,7 @@ x = .AnotherValue
>
#### implicit-member-expression {#implicit-member-expression}
> *隐式成员表达式***.** [*标识符*](./02_Lexical_Structure.md#identifier)
> *隐式成员表达式***.** [标识符](./02_Lexical_Structure.md#identifier)
>
### 圆括号表达式 {#parenthesized-expression}
@ -599,7 +602,7 @@ x = .AnotherValue
>
#### parenthesized-expression {#parenthesized-expression}
> *圆括号表达式***( [*表达式*](#expression) )**
> *圆括号表达式***( [表达式](#expression) )**
>
### 元组表达式 {#Tuple-Expression}
@ -608,6 +611,8 @@ x = .AnotherValue
> (`标识符 1` : `表达式 1`, `标识符 2` : `表达式 2`, `...`)
>
元组表达式里的每一个标识符在表达式作用域里必须是唯一的。在嵌套的元组表达式中,同嵌套层级里的标识符也必须是唯一的。例如,`(a: 10, a: 20)` 是不合法的,因为标签 `a` 在同一层级出现了两次。然而,`(a: 10, b: (a: 1, x: 2))` 是合法的,尽管 `a` 出现了两次,但有一次在外层元组里,一次在内层元组里。
元组表达式可以一个表达式都没有,也可以包含两个或是更多的表达式。单个表达式用括号括起来就是括号表达式了。
> 注意
@ -621,15 +626,15 @@ x = .AnotherValue
>
#### tuple-expression {#tuple-expression}
> *元组表达式***( )** | **(**[*元组元素*](#tuple-element) [*元组元素列表*](#tuple-element-list) **)**
> *元组表达式***( )** | **(**[元组元素](#tuple-element) [元组元素列表](#tuple-element-list) **)**
>
#### tuple-element-list {#tuple-element-list}
> *元组元素列表* → [*元组元素*](#tuple-element) | [*元组元素*](#tuple-element) **,** [*元组元素列表*](#tuple-element-list)
> *元组元素列表* → [元组元素](#tuple-element) | [元组元素](#tuple-element) **,** [元组元素列表](#tuple-element-list)
>
#### tuple-element {#tuple-element}
> *元组元素* → [*表达式*](#expression) | [*标识符*](identifier) **:** [*表达式*](#expression)
> *元组元素* → [表达式](#expression) | [标识符](#identifier) **:** [表达式](#expression)
>
### 通配符表达式 {#wildcard-expression}
@ -678,7 +683,7 @@ let value = s[keyPath: pathToProperty]
```swift
class SomeClass: NSObject {
@objc var someProperty: Int
@objc dynamic var someProperty: Int
init(someProperty: Int) {
self.someProperty = someProperty
}
@ -729,7 +734,6 @@ let myGreeting = greetings[keyPath: \[String].[1]]
var index = 2
let path = \[String].[index]
let fn: ([String]) -> String = { strings in strings[index] }
>
print(greetings[keyPath: path])
// 打印 "bonjour"
@ -759,7 +763,7 @@ print(count as Any)
// 打印 "Optional(5)"
```
可以混合使用各种 key-path 组件来访问一些深度嵌套类型的值。下面的代码通过组合不同的组件,使用 key-path 表达式访问了一个字典数组中不同的值和属性。
可以混合使用各种 key path 组件来访问一些深度嵌套类型的值。下面的代码通过组合不同的组件,使用 key-path 表达式访问了一个字典数组中不同的值和属性。
```swift
let interestingNumbers = ["prime": [2, 3, 5, 7, 11, 13, 17],
@ -775,7 +779,40 @@ print(interestingNumbers[keyPath: \[String: [Int]].["hexagonal"]!.count.bitWidth
// 打印 "64"
```
关于更多如何使用 key path 与 Objective-C APIs 交互的信息,请参阅 [在 Swift 中使用 Objective-C 运行时特性](https://developer.apple.com/documentation/swift/using_objective_c_runtime_features_in_swift)。关于更多 key-value 编程和 key-value 观察的信息,请参阅 [Key-Value 编程](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueCoding/index.html#//apple_ref/doc/uid/10000107i) 和 [Key-Value 观察编程](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid/10000177i)
你可以在平时提供函数或者闭包的上下文里使用 key path 表达式。特别地,你可以用根类型是 `SomeType` 和路径产生 `Value` 类型值的 key path 表达式来替换类型是 `(SomeType) -> Value` 的函数或者闭包。
```swift
struct Task {
var description: String
var completed: Bool
}
var toDoList = [
Task(description: "Practice ping-pong.", completed: false),
Task(description: "Buy a pirate costume.", completed: true),
Task(description: "Visit Boston in the Fall.", completed: false),
]
// 下面两种写法是等价的。
let descriptions = toDoList.filter(\.completed).map(\.description)
let descriptions2 = toDoList.filter { $0.completed }.map { $0.description }
```
任何 key path 表达式的副作用发生的关键在于表达式在哪里被执行。例如,如果你在 key path 表达式中的一个下标里使用函数调用,该函数只会在表达式计算的时候调用一次,而不是每次这个 key path 被使用的时候。
```swift
func makeIndex() -> Int {
print("Made an index")
return 0
}
// 下面这行调用 makeIndex()。
let taskKeyPath = \[Task][makeIndex()]
// 打印 "Made an index"
// 使用 taskKeyPath 不会再次调用 makeIndex()。
let someTask = toDoList[keyPath: taskKeyPath]
```
关于更多如何使用 key path 与 Objective-C APIs 交互的信息,请参阅 [在 Swift 中使用 Objective-C 运行时特性](https://developer.apple.com/documentation/swift/using_objective_c_runtime_features_in_swift)。关于更多 key-value 编程和 key-value 观察的信息,请参阅 [Key-Value 编程](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueCoding/index.html#//apple-ref/doc/uid/10000107i) 和 [Key-Value 观察编程](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html#//apple-ref/doc/uid/10000177i)。
> key-path 表达式语法
>
@ -826,7 +863,7 @@ class SomeClass: NSObject {
self.property = property
}
}
let selectorForMethod = #selector(SomeClass.doSomething(_:))
let selectorForMethod = #selector(SomeClass.doSomething(-:))
let selectorForPropertyGetter = #selector(getter: SomeClass.property)
```
@ -839,8 +876,7 @@ extension SomeClass {
@objc(doSomethingWithString:)
func doSomething(_ x: String) { }
}
let anotherSelector = #selector(SomeClass.doSomething(_:) as (SomeClass) -> (String) -> Void)
>
let anotherSelector = #selector(SomeClass.doSomething(-:) as (SomeClass) -> (String) -> Void)
```
由于选择器是在编译时创建的,因此编译器可以检查方法或者属性是否存在,以及是否在运行时暴露给了 Objective-C 。
@ -856,11 +892,11 @@ let anotherSelector = #selector(SomeClass.doSomething(_:) as (SomeClass) -> (Str
>
#### selector-expression {#selector-expression}
> *选择器表达式*__#selector__ **(** [*表达式*](#expression) **)**
> *选择器表达式*__#selector-- **(** [表达式](#expression) **)**
>
> *选择器表达式*__#selector__ **(** [*getter:表达式*](#expression) **)**
> *选择器表达式*__#selector-- **(** [getter:表达式](#expression) **)**
>
> *选择器表达式*__#selector__ **(** [*setter:表达式*](#expression) **)**
> *选择器表达式*__#selector-- **(** [setter:表达式](#expression) **)**
>
## Key-path 字符串表达式 {#key-path-string-expressions}
@ -903,7 +939,7 @@ print(keyPath == c.getSomeKeyPath())
由于 key-path 字符串表达式在编译期才创建,编译期可以检查属性是否存在,以及属性是否暴露给 Objective-C 运行时。
关于更多如何使用 key path 与 Objective-C APIs 交互的信息,请参阅 [在 Swift 中使用 Objective-C 运行时特性](./https://developer.apple.com/documentation/swift/using_objective_c_runtime_features_in_swift)。关于更多 key-value 编程和 key-value 观察的信息,请参阅 [Key-Value 编程](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueCoding/index.md#//apple_ref/doc/uid/10000107i) 和 [Key-Value 观察编程](./https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.md#//apple_ref/doc/uid/10000177i)
关于更多如何使用 key path 与 Objective-C APIs 交互的信息,请参阅 [在 Swift 中使用 Objective-C 运行时特性](https://developer.apple.com/documentation/swift/using_objective_c_runtime_features_in_swift)。关于更多 key-value 编程和 key-value 观察的信息,请参阅 [Key-Value 编程](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueCoding/index.md#//apple-ref/doc/uid/10000107i) 和 [Key-Value 观察编程](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.md#//apple-ref/doc/uid/10000177i)
> 注意
>
@ -922,7 +958,7 @@ print(keyPath == c.getSomeKeyPath())
## 后缀表达式 {#postfix-expressions}
*后缀表达式*就是在某个表达式的后面运用后缀运算符或其他后缀语法。从语法构成上来看,基本表达式也是后缀表达式。
关于这些运算符的更多信息,请参阅 [基本运算符](../chapter2/02_Basic_Operators.md) 和 [高级运算符](../chapter2/26_Advanced_Operators.md)。
关于这些运算符的更多信息,请参阅 [基本运算符](../02_language_guide/02_Basic_Operators.md) 和 [高级运算符](../02_language_guide/27_Advanced_Operators.md)。
关于 Swift 标准库提供的运算符的更多信息,请参阅 [运算符定义](https://developer.apple.com/documentation/swift/operator_declarations)。
@ -930,25 +966,25 @@ print(keyPath == c.getSomeKeyPath())
>
#### postfix-expression {#postfix-expression}
> *后缀表达式* → [*基本表达式*](#primary-expression)
> *后缀表达式* → [基本表达式](#primary-expression)
>
> *后缀表达式* → [*后缀表达式*](#postfix-expression) [*后缀运算符*](02_Lexical_Structure.md#postfix-operator)
> *后缀表达式* → [后缀表达式](#postfix-expression) [后缀运算符](./02_Lexical_Structure.md#postfix-operator)
>
> *后缀表达式* → [*函数调用表达式*](#function-call-expression)
> *后缀表达式* → [函数调用表达式](#function-call-expression)
>
> *后缀表达式* → [*构造器表达式*](#initializer-expression)
> *后缀表达式* → [构造器表达式](#initializer-expression)
>
> *后缀表达式* → [*显式成员表达式*](#explicit-member-expression)
> *后缀表达式* → [显式成员表达式](#explicit-member-expression)
>
> *后缀表达式* → [*后缀 self 表达式*](#postfix-self-expression)
> *后缀表达式* → [后缀 self 表达式](#postfix-self-expression)
>
> *后缀表达式* → [*dynamicType 表达式*](#dynamic-type-expression)
> *后缀表达式* → [dynamicType 表达式](#dynamic-type-expression)
>
> *后缀表达式* → [*下标表达式*](#subscript-expression)
> *后缀表达式* → [下标表达式](#subscript-expression)
>
> *后缀表达式* → [*强制取值表达式*](#forced-value-expression)
> *后缀表达式* → [强制取值表达式](#forced-value-expression)
>
> *后缀表达式* → [*可选链表达式*](#optional-chaining-expression)
> *后缀表达式* → [可选链表达式](#optional-chaining-expression)
>
### 函数调用表达式 {#function-call-expression}
@ -962,14 +998,17 @@ print(keyPath == c.getSomeKeyPath())
如果函数声明中指定了参数的名字,那么在调用的时候也必须得写出来。这种函数调用表达式具有以下形式:
> `函数名`(`参数名 1`: `参数 1`, `参数名 2`: `参数 2`)
>
如果函数的最后一个参数是函数类型,可以在函数调用表达式的尾部(右圆括号之后)加上一个闭包,该闭包会作为函数的最后一个参数。如下两种写法是等价的:
```swift
// someFunction 接受整和闭包参
// someFunction 接受整和闭包的实
someFunction(x, f: {$0 == 13})
someFunction(x) {$0 == 13}
// anotherFunction 接受一个整型和两个闭包的实参
anotherFunction(x: x, f: { $0 == 13 }, g: { print(99) })
anotherFunction(x: x) { $0 == 13 } g: { print(99) }
```
如果闭包是该函数的唯一参数,那么圆括号可以省略。
@ -980,39 +1019,43 @@ myData.someMethod() {$0 == 13}
myData.someMethod {$0 == 13}
```
如 [特殊名称方法](./06_Declarations.md#methods-with-special-names) 所述,通过声明几种方法中的一种,类、结构体或枚举类型可以为函数调用语法启用语法糖。
> 函数调用表达式语法
>
>
#### function-call-expression {#function-call-expression}
>
> *函数调用表达式* → [*后缀表达式*](#postfix-expression) [*函数调用参数子句*](#function-call-argument-clause)
> *函数调用表达式* → [后缀表达式](#postfix-expression) [函数调用参数子句](#function-call-argument-clause)
>
> *函数调用表达式* → [*后缀表达式*](#postfix-expression) [*函数调用参数子句*](#function-call-argument-clause)<sub>可选</sub> [*尾随闭包*](#trailing-closure)
> *函数调用表达式* → [后缀表达式](#postfix-expression) [函数调用参数子句](#function-call-argument-clause)<sub>可选</sub> [尾随闭包](#trailing-closure)
>
>#### function-call-argument-clause {#function-call-argument-clause}
>
> *函数调用参数子句***(** **)** | **(** [函数调用参数表](#function-call-argument-list) **)**
>
#### function-call-argument-clause {#function-call-argument-clause}
>
> *函数调用参数子句***(** **)** | **(** [*函数调用参数表*](#function-call-argument-list) **)**
>
>
#### function-call-argument-list {#function-call-argument-list}
> #### function-call-argument-list {#function-call-argument-list}
>
> *函数调用参数表* → [函数调用参数](#function-call-argument) | [函数调用参数](#function-call-argument) **,** [*函数调用参数表*](#function-call-argument-list)
>
>
#### function-call-argument {#function-call-argument}
> #### function-call-argument {#function-call-argument}
>
> *函数调用参数* → [表达式](#expression) | [标识符](02_Lexical_Structure.md#identifier) **:** [*表达式*](#expression)
> *函数调用参数* → [表达式](#expression) | [标识符](./02_Lexical_Structure.md#identifier) **:** [表达式](#expression)
>
> *函数调用参数* → [运算符](./02_Lexical_Structure.md#operator) | [标识符](./02_Lexical_Structure.md#identifier) **:** [*运算符*](./02_Lexical_Structure.md#operator)
> *函数调用参数* → [运算符](./02_Lexical_Structure.md#operator) | [标识符](./02_Lexical_Structure.md#identifier) **:** [运算符](./02_Lexical_Structure.md#operator)
>
> #### trailing-closure {#trailing-closure}
>
> *尾随闭包* → [闭包表达式](#closure-expression)
>
#### trailing-closure {#trailing-closure}
> #### labeled-trailing-closures {#labeled-trailing-closures}
>
> *尾随闭包* → [*闭包表达式*](#closure-expression)
> *标签尾随闭包* → [标签尾随闭包](#labeled-trailing-closure) [标签尾随闭包集](#labeled-trailing-closures)<sub>可选</sub>
>
> #### labeled-trailing-closure {#labeled-trailing-closure}
> *标签尾随闭包* → [标识符](./02_Lexical_Structure.md#identifier) **:** [闭包表达式](#closure-expression)
### 构造器表达式 {#initializer-expression}
*构造器表达式*用于访问某个类型的构造器,形式如下:
@ -1036,7 +1079,6 @@ class SomeSubClass: SomeSuperClass {
```swift
// 类型注解是必须的,因为 String 类型有多种构造器
let initializer: Int -> String = String.init
>
let oneTwoThree = [1, 2, 3].map(initializer).reduce("", combine: +)
print(oneTwoThree)
// 打印“123”
@ -1056,9 +1098,9 @@ let s3 = someValue.dynamicType.init(data: 7) // 有效
>
#### initializer-expression {#initializer-expression}
> *构造器表达式* → [*后缀表达式*](#postfix-expression) **.** **init**
> *构造器表达式* → [后缀表达式](#postfix-expression) **.** **init**
>
> *构造器表达式* → [*后缀表达式*](#postfix-expression) **.** **init** **(** [*参数名称*](#argument-names) **)**
> *构造器表达式* → [后缀表达式](#postfix-expression) **.** **init** **(** [参数名称](#argument-names) **)**
>
### 显式成员表达式 {#explicit-member-expression}
@ -1087,9 +1129,9 @@ t.0 = t.1
对于模块的成员来说,只能直接访问顶级声明中的成员。
使用 `dynamicMemberLookup` 属性声明的类型包含可以在运行时查找的成员,具体请参阅 [属性](./07_Attributes.md)
使用 `dynamicMemberLookup` 属性声明的类型包含可以在运行时查找的成员,具体请参阅 [属性](./07_Attributes.md)
为了区分只有参数名有所不同的方法或构造器,在圆括号中写出参数名,参数名后紧跟一个冒号,对于没有参数名的参数,使用下划线代替参数名。而对于重载方法,则需使用类型注进行区分。例如:
为了区分只有参数名有所不同的方法或构造器,在圆括号中写出参数名,参数名后紧跟一个冒号,对于没有参数名的参数,使用下划线代替参数名。而对于重载方法,则需使用类型注进行区分。例如:
```swift
class SomeClass {
@ -1106,7 +1148,6 @@ let b = instance.someMethod(_:y:) // 无歧义
let d = instance.overloadedMethod // 有歧义
let d = instance.overloadedMethod(_:y:) // 有歧义
let d: (Int, Bool) -> Void = instance.overloadedMethod(_:y:) // 无歧义
>
```
如果点号(`.`)出现在行首,它会被视为显式成员表达式的一部分,而不是隐式成员表达式的一部分。例如如下代码所展示的被分为多行的链式方法调用:
@ -1115,7 +1156,6 @@ let d: (Int, Bool) -> Void = instance.overloadedMethod(_:y:) // 无歧义
let x = [10, 3, 20, 15, 4]
.sort()
.filter { $0 > 5 }
>
.map { $0 * 100 }
```
@ -1123,19 +1163,17 @@ let x = [10, 3, 20, 15, 4]
>
#### explicit-member-expression {#explicit-member-expression}
> *显式成员表达式* → [*后缀表达式*](#postfix-expression) **.** [*十进制数字*] (02_Lexical_Structure.md#decimal-digit)
> *显式成员表达式* → [后缀表达式](#postfix-expression) **.** [十进制数字](./02_Lexical_Structure.md#decimal-digit)
>
> *显式成员表达式* → [*后缀表达式*](#postfix-expression) **.** [*标识符*](02_Lexical_Structure.md#identifier) [*泛型实参子句*](./09_Generic_Parameters_and_Arguments.md#generic-argument-clause)<sub>可选</sub><br/>
>
> *显式成员表达式* → [*后缀表达式*](#postfix-expression) **.** [*标识符*] (02_Lexical_Structure.md#identifier) **(** [*参数名称*](#argument-names) **)**
> *显式成员表达式* → [后缀表达式](#postfix-expression) **.** [标识符](./02_Lexical_Structure.md#identifier) [泛型实参子句](./09_Generic_Parameters_and_Arguments.md#generic-argument-clause)<sub>可选</sub><br/>
>
> *显式成员表达式* → [后缀表达式](#postfix-expression) **.** [标识符](./02_Lexical_Structure.md#identifier) **(** [参数名称](#argument-names) **)**
#### argument-names {#argument-names}
> *参数名称* → [*参数名*](#argument-name) [*参数名称*](#argument-names)<sub>可选</sub><br/>
>
> *参数名称* → [参数名](#argument-name) [参数名称](#argument-names)<sub>可选</sub><br/>
#### argument-name {#argument-name}
> *参数名* → [*标识符*](./02_Lexical_Structure.md#identifier) **:**
> *参数名* → [标识符](./02_Lexical_Structure.md#identifier) **:**
>
### 后缀 self 表达式 {#postfix-self-expression}
@ -1154,7 +1192,7 @@ let x = [10, 3, 20, 15, 4]
>
#### postfix-self-expression {#postfix-self-expression}
> *后缀 self 表达式* → [*后缀表达式*](#postfix-expression) **.** **self**
> *后缀 self 表达式* → [后缀表达式](#postfix-expression) **.** **self**
>
@ -1166,13 +1204,13 @@ let x = [10, 3, 20, 15, 4]
要获取下标表达式的值,可将索引表达式作为下标表达式的参数来调用下标 getter。下标 setter 的调用方式与之一样。
关于下标的声明,请参阅 [协议下标声明](./06_Declarations.md#protocol_subscript_declaration)。
关于下标的声明,请参阅 [协议下标声明](./06_Declarations.md#protocol-subscript-declaration)。
> 下标表达式语法
>
#### subscript-expression {#subscript-expression}
> *下标表达式* → [*后缀表达式*](#postfix-expression) **[** [*表达式列表*](#expression-list) **]**
> *下标表达式* → [后缀表达式](#postfix-expression) **[** [表达式列表](#expression-list) **]**
>
### 强制取值表达式 {#forced-Value-expression}
@ -1199,7 +1237,7 @@ someDictionary["a"]![0] = 100
>
#### forced-value-expression {#forced-value-expression}
> *强制取值表达式* → [*后缀表达式*](#postfix-expression) **!**
> *强制取值表达式* → [后缀表达式](#postfix-expression) **!**
>
### 可选链表达式 {#optional-chaining-expression}
@ -1232,7 +1270,6 @@ if let unwrappedC = c {
```swift
func someFunctionWithSideEffects() -> Int {
>
// 译者注:为了能看出此函数是否被执行,加上了一句打印
print("someFunctionWithSideEffects")
return 42
@ -1252,5 +1289,4 @@ someDictionary["a"]?[0] = someFunctionWithSideEffects()
>
#### optional-chaining-expression {#optional-chaining-expression}
> *可选链表达式* → [*后缀表达式*](#postfix-expression) **?**
>
> *可选链表达式* → [后缀表达式](#postfix-expression) **?**

View File

@ -8,42 +8,41 @@
> 语句语法
>
> *语句* → [*表达式*](./04_Expressions.md#expression) **;**<sub>可选</sub>
> *语句* → [表达式](./04_Expressions.md#expression) **;**<sub>可选</sub>
>
> *语句* → [*声明*](./06_Declarations.md#declaration) **;**<sub>可选</sub>
> *语句* → [声明](./06_Declarations.md#declaration) **;**<sub>可选</sub>
>
> *语句* → [*循环语句*](#loop-statement) **;**<sub>可选</sub>
> *语句* → [循环语句](#loop-statement) **;**<sub>可选</sub>
>
> *语句* → [*分支语句*](#branch-statement) **;**<sub>可选</sub>
> *语句* → [分支语句](#branch-statement) **;**<sub>可选</sub>
>
> *语句* → [*带标签的语句*](#labeled-statement) **;**<sub>可选</sub>
> *语句* → [带标签的语句](#labeled-statement) **;**<sub>可选</sub>
>
> *语句* → [*控制转移语句*](#control-transfer-statement) **;**<sub>可选</sub>
> *语句* → [控制转移语句](#control-transfer-statement) **;**<sub>可选</sub>
>
> *语句* → [*defer 语句*](#defer-statement) **;**<sub>可选</sub>
> *语句* → [defer 语句](#defer-statement) **;**<sub>可选</sub>
>
> *语句* → [*do 语句*](#do-statement) **:**<sub>可选</sub>
> *语句* → [do 语句](#do-statement) **:**<sub>可选</sub>
>
> *语句* → [*编译器控制语句*](#compiler-control-statement)
>
> *多条语句* → [*语句*](#statement) [*多条语句*](#statements)<sub>可选</sub>
> *语句* → [编译器控制语句](#compiler-control-statement)
>
> *多条语句* → [语句](#statement) [多条语句](#statements)<sub>可选</sub>
## 循环语句 {#loop-statements}
循环语句会根据特定的循环条件来重复执行代码块。Swift 提供三种类型的循环语句:`for-in` 语句、`while` 语句和 `repeat-while` 语句。
通过 `break` 语句和 `continue` 语句可以改变循环语句的控制流。有关这两条语句,详情参 [Break 语句](#break_statement) 和 [Continue 语句](#continue_statement)。
通过 `break` 语句和 `continue` 语句可以改变循环语句的控制流。有关这两条语句,详情参 [Break 语句](#break-statement) 和 [Continue 语句](#continue-statement)。
> 循环语句语法
>
>
#### loop-statement {#loop-statement}
> *循环语句* → [*for-in 语句*](#for-in-statement)
> *循环语句* → [for-in 语句](#for-in-statement)
>
> *循环语句* → [*while 语句*](#while-statement)
> *循环语句* → [while 语句](#while-statement)
>
> *循环语句* → [*repeat-while 语句*](#repeat-while-statement)
> *循环语句* → [repeat-while 语句](#repeat-while-statement)
>
### For-In 语句 {#for-in-statements}
@ -65,7 +64,7 @@ for item in collection {
>
#### for-in-statement {#for-in-statement}
> *for-in 语句***for** **case**<sub>可选</sub> [*模式*](./08_Patterns.md#pattern) **in** [*表达式*](./04_Expressions.md#expression) [*where 子句*](#where-clause)<sub>可选</sub> [*代码块*](05_Declarations.md#code-block)
> *for-in 语句***for** **case**<sub>可选</sub> [模式](./08_Patterns.md#pattern) **in** [表达式](./04_Expressions.md#expression) [where 子句](#where-clause)<sub>可选</sub> [代码块](./06_Declarations.md#code-block)
>
### While 语句 {#while-statements}
@ -86,32 +85,32 @@ while condition {
由于会在执行循环体中的语句前判断条件的值,因此循环体中的语句可能会被执行若干次,也可能一次也不会被执行。
条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。
条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding)。
> while 语句语法
>
>
#### while-statement {#while-statement}
> *while 语句***while** [*条件子句*](#condition-clause) [*代码块*](./05_Declarations.md#code-block)
> *while 语句***while** [条件子句](#condition-clause) [代码块](./06_Declarations.md#code-block)
>
#### condition-clause {#condition-clause}
> *条件子句* → [*表达式*](./04_Expressions.md#expression) | [*表达式*](./04_Expressions.md#expression) **,** [*条件列表*](#condition-list)
> *条件子句* → [表达式](./04_Expressions.md#expression) | [表达式](./04_Expressions.md#expression) **,** [条件列表](#condition-list)
>
#### condition {#condition}
> *条件* → [*表达式*](./04_Expressions.md#expression) |[*可用性条件*](#availability-condition) | [*case 条件*](#case-condition) | [*可选绑定条件*](#optional-binding-condition)
> *条件* → [表达式](./04_Expressions.md#expression) |[可用性条件](#availability-condition) | [case 条件](#case-condition) | [可选绑定条件](#optional-binding-condition)
>
>
#### case-condition {#case-condition}
> *case 条件***case** [*模式*](./08_Patterns.md#pattern) [*构造器*](./06_Declarations.md#initializer)
> *case 条件***case** [模式](./08_Patterns.md#pattern) [构造器](./06_Declarations.md#initializer)
>
#### optional-binding-condition {#optional-binding-condition}
> *可选绑定条件***let** [*模式*](./08_Patterns.md#pattern) [*构造器*](./06_Declarations.md#initializer) | **var** [*模式*](./08_Patterns.md#pattern) [*构造器*](./06_Declarations.md#initializer)
> *可选绑定条件***let** [模式](./08_Patterns.md#pattern) [构造器](./06_Declarations.md#initializer) | **var** [模式](./08_Patterns.md#pattern) [构造器](./06_Declarations.md#initializer)
>
### Repeat-While 语句 {#repeat-while-statements}
@ -132,31 +131,31 @@ repeat {
由于条件的值是在循环体中的语句执行后才进行判断,因此循环体中的语句至少会被执行一次。
条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。
条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding)。
> repeat-while 语句语法
>
>
#### repeat-while-statement {#repeat-while-statement}
> *repeat-while 语句***repeat** [*代码块*](./06_Declarations.md#code-block) **while** [*表达式*](./04_Expressions.md#expression)
> *repeat-while 语句***repeat** [代码块](./06_Declarations.md#code-block) **while** [表达式](./04_Expressions.md#expression)
>
## 分支语句 {#branch-statements}
分支语句会根据一个或者多个条件来执行指定部分的代码。分支语句中的条件将会决定程序如何分支以及执行哪部分代码。Swift 提供三种类型的分支语句:`if` 语句、 `guard` 语句和 `switch` 语句。
`if` 语句和 `switch` 语句中的控制流可以用 `break` 语句改变,请参阅 [Break 语句](#break_statement)。
`if` 语句和 `switch` 语句中的控制流可以用 `break` 语句改变,请参阅 [Break 语句](#break-statement)。
> 分支语句语法
>
>
#### branch-statement {#branch-statement}
> *分支语句* → [*if 语句*](#if-statement)
> *分支语句* → [if 语句](#if-statement)
>
> *分支语句* → [*guard 语句*](#guard-statement)
> *分支语句* → [guard 语句](#guard-statement)
>
> *分支语句* → [*switch 语句*](#switch-statement)
> *分支语句* → [switch 语句](#switch-statement)
>
### If 语句 {#if-statements}
@ -194,19 +193,18 @@ if condition 1 {
}
```
`if` 语句中条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。
`if` 语句中条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件语句也可以使用可选绑定,请参阅 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding)。
> if 语句语法
>
>
#### if-statement {#if-statement}
> *if 语句***if** [*条件子句*](#condition-clause) [*代码块*](05_Declarations.md#code-block) [*else 子句*](#else-clause)<sub>可选</sub>
> *if 语句***if** [条件子句](#condition-clause) [代码块](./06_Declarations.md#code-block) [else 子句](#else-clause)<sub>可选</sub>
>
#### else-clause {#else-clause}
> *else 子句***else** [*代码块*](./06_Declarations.md#code-block) | **else** [*if 语句*](#if-statement)
>
> *else 子句***else** [代码块](./06_Declarations.md#code-block) | **else** [if 语句](#if-statement)
### Guard 语句 {#guard-statements}
如果一个或者多个条件不成立,可用 `guard` 语句来退出当前作用域。
@ -219,7 +217,7 @@ guard condition else {
}
```
`guard` 语句中条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件也可以是一条可选绑定,请参阅 [可选绑定](../chapter2/01_The_Basics.md#optional_binding)。
`guard` 语句中条件的结果必须是 Bool 类型或者 Bool 的桥接类型。另外,条件也可以是一条可选绑定,请参阅 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding)。
`guard` 语句中进行可选绑定的任何常量或者变量,其可用范围从声明开始直到作用域结束。
@ -230,17 +228,17 @@ guard condition else {
* `continue`
* `throw`
关于控制转移语句,请参阅 [控制转移语句](#control_transfer_statements)。关于 `Never` 返回类型的函数,请参阅 [永不返回的函数](05_Declarations.md#rethrowing_functions_and_methods)。
关于控制转移语句,请参阅 [控制转移语句](#control-transfer-statements)。关于 `Never` 返回类型的函数,请参阅 [永不返回的函数](./06_Declarations.md#rethrowing-functions-and-methods)。
> guard 语句语法
>
>
#### guard-statement {#guard-statement}
> *guard 语句***guard** [*条件子句*](#condition-clause) **else** [*代码块*] (05_Declarations.md#code-block)
>
> *guard 语句***guard** [条件子句](#condition-clause) **else** [代码块](./06_Declarations.md#code-block)
### Switch 语句 {#switch-statements}
`switch` 语句会根据控制表达式的值来决定执行哪部分代码。
`switch` 语句的形式如下:
@ -261,7 +259,7 @@ default:
`switch` 语句会先计算*控制表达式*的值,然后与每一个 `case` 的模式进行匹配。如果匹配成功,程序将会执行对应的 `case` 中的语句。另外,每一个 `case` 的作用域都不能为空,也就是说在每一个 `case` 的冒号(`:`)后面必须至少有一条语句。如果你不想在匹配到的 `case` 中执行代码,只需在该 `case` 中写一条 `break` 语句即可。
可以用作控制表达式的值是十分灵活的。除了标量类型外,如 `Int``Character`,你可以使用任何类型的值,包括浮点数、字符串、元组、自定义类型的实例和可选类型。控制表达式的值还可以用来匹配枚举类型中的成员值或是检查该值是否包含在指定的 `Range` 中。关于如何在 `switch` 语句中使用这些类型,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章中的 [Switch](../chapter2/05_Control_Flow.md#switch)。
可以用作控制表达式的值是十分灵活的。除了标量类型外,如 `Int``Character`,你可以使用任何类型的值,包括浮点数、字符串、元组、自定义类型的实例和可选类型。控制表达式的值还可以用来匹配枚举类型中的成员值或是检查该值是否包含在指定的 `Range` 中。关于如何在 `switch` 语句中使用这些类型,请参阅 [控制流](../02_language_guide/05_Control_Flow.md) 一章中的 [Switch](../02_language_guide/05_Control_Flow.md#switch)。
每个 `case` 的模式后面可以有一个 `where` 子句。`where` 子句由 `where` 关键字紧跟一个提供额外条件的表达式组成。因此,当且仅当控制表达式匹配一个 `case` 的模式且 `where` 子句的表达式为真时,`case` 中的语句才会被执行。在下面的例子中,控制表达式只会匹配包含两个相等元素的元组,例如 `(1, 1)`
@ -280,7 +278,7 @@ case let (x, y) where x == y:
在 Swift 中,`switch` 语句中控制表达式的每一个可能的值都必须至少有一个 `case` 与之对应。在某些无法面面俱到的情况下(例如,表达式的类型是 `Int`),你可以使用 `default` 分支满足该要求。
#### 对未来枚举的 `case` 进行 `switch` {#future-case}
非冻结枚举(`nonfronzen enumeration`)是一种特殊的枚举类型,它可能在未来会增加新的枚举 `case`,即使这时候你已经编译并且发布了你的应用,所以在 switch 非冻结枚举前需要深思熟虑。当一个库的作者们把一个枚举标记为非冻结的,这意味着他们保留了增加新的枚举 `case` 的权利,并且任何和这个枚举交互的代码都要在不需要重新编译的条件下能够处理那些未来可能新加入的 `case` 。只有那些标准库,比如用 Swift 实现的苹果的一些框架C 以及 Objective-C 代码才能够声明非冻结枚举。你在 Swift 中声明的枚举不能是非冻结的
非冻结枚举(`nonfronzen enumeration`)是一种特殊的枚举类型,它可能在未来会增加新的枚举 `case`,即使这时候你已经编译并且发布了你的应用,所以在 switch 非冻结枚举前需要深思熟虑。当一个库的作者们把一个枚举标记为非冻结的,这意味着他们保留了增加新的枚举 `case` 的权利,并且任何和这个枚举交互的代码都*必须*在无需重新编译的条件下能够处理那些未来可能新加入的 `case` 。只有演进模式的库代码、标准库代码、用 Swift 实现的 Apple 框架C 以及 Objective-C 代码才能够声明非冻结枚举。更多关于冻结和非冻结枚举的内容,请参阅 [冻结](./07_Attributes.md#frozen)
当你对未来枚举进行 switch 时,你总是需要有一个 `default case`,即使每种枚举类型都已经有对应的 `case` 了。你可以在 default 前标注 `@unknown`,意思是这个 `case` 应该只匹配未来加入的枚举 `case`。如果你的 `default case` 中匹配了任何在编译时就能确定的枚举 `case`Swift 会抛出一个警告。这可以很好地提醒你库的作者已经新增了一种 `case`,并且你还没有去处理。
@ -303,94 +301,94 @@ case .suppressed:
#### 不存在隐式落入
当匹配到的 `case` 中的代码执行完毕后,`switch` 语句会直接退出,而不会继续执行下一个 `case` 。这就意味着,如果你想执行下一个 `case`,需要显式地在当前 `case` 中使用 `fallthrough` 语句。关于 `fallthrough` 语句的更多信息,请参阅 [Fallthrough 语句](#fallthrough_statements)。
当匹配到的 `case` 中的代码执行完毕后,`switch` 语句会直接退出,而不会继续执行下一个 `case` 。这就意味着,如果你想执行下一个 `case`,需要显式地在当前 `case` 中使用 `fallthrough` 语句。关于 `fallthrough` 语句的更多信息,请参阅 [Fallthrough 语句](#fallthrough-statements)。
> switch 语句语法
>
>
#### switch-statement {#switch-statement}
> *switch 语句***switch** [*表达式*](./04_Expressions.md#expression) **{** [*switch-case 列表*](#switch-cases)<sub>可选</sub> **}**
> *switch 语句***switch** [表达式](./04_Expressions.md#expression) **{** [switch-case 列表](#switch-cases)<sub>可选</sub> **}**
>
#### switch-cases {#switch-cases}
> *switch case 列表* → [*switch-case*](#switch-case) [*switch-case 列表*](#switch-cases)<sub>可选</sub>
> *switch case 列表* → [switch-case](#switch-case) [switch-case 列表](#switch-cases)<sub>可选</sub>
>
#### switch-case {#switch-case}
> *switch case* → [*case 标签*](#case-label) [*多条语句*](#statements) | [*default 标签*](#default-label) [*多条语句*](#statements) | [*conditional-switch-case*](#conditional-switch-case-label)
> *switch case* → [case 标签](#case-label) [多条语句](#statements) | [default 标签](#default-label) [多条语句](#statements) | [conditional-switch-case](#conditional-switch-case-label)
>
#### case-label {#case-label}
> *case 标签* → [*属性*](#switch-case-attributes-label)<sub>可选</sub> **case** [*case 项列表*](#case-item-list) **:**
> *case 标签* → [属性](#switch-case-attributes-label)<sub>可选</sub> **case** [case 项列表](#case-item-list) **:**
>
#### case-item-list {#case-item-list}
> *case 项列表* → [*模式*](./08_Patterns.md#pattern) [*where 子句*](#where-clause)<sub>可选</sub> | [*模式*](07_Patterns.md#pattern) [*where 子句*](#where-clause)<sub>可选</sub> **,** [*case 项列表*](#case-item-list)
> *case 项列表* → [模式](./08_Patterns.md#pattern) [where 子句](#where-clause)<sub>可选</sub> | [模式](./08_Patterns.md#pattern) [where 子句](#where-clause)<sub>可选</sub> **,** [case 项列表](#case-item-list)
>
#### default-label {#default-label}
> *default 标签* → [*属性*](#switch-case-attributes-label)<sub>可选</sub> **default** **:**
> *default 标签* → [属性](#switch-case-attributes-label)<sub>可选</sub> **default** **:**
>
>
#### where-clause {#where-clause}
> *where-clause***where** [*where 表达式*](#where-expression)
> *where-clause***where** [where 表达式](#where-expression)
>
#### where-expression {#where-expression}
> *where-expression* → [*表达式*](./04_Expressions.md#expression)
> *where-expression* → [表达式](./04_Expressions.md#expression)
>
>
#### grammar_conditional-switch-case {#grammar-conditional-switch-case}
> *conditional-switch-case* → [*switch-if-directive-clause*](#switch-case-attributes-label) [*switch-elseif-directive-clauses*](#switch-case-attributes-label) <sub>可选</sub> [*switch-else-directive-clause*](#switch-case-attributes-label) <sub>可选</sub> [*endif-directive*](#switch-case-attributes-label)
#### grammar-conditional-switch-case {#grammar-conditional-switch-case}
> *conditional-switch-case* → [switch-if-directive-clause](#switch-case-attributes-label) [switch-elseif-directive-clauses](#switch-case-attributes-label) <sub>可选</sub> [switch-else-directive-clause](#switch-case-attributes-label) <sub>可选</sub> [endif-directive](#switch-case-attributes-label)
>
#### grammar_switch-if-directive-clause {#grammar-switch-if-directive-clause}
> *switch-if-directive 语句* → [*if-directive*](#switch-case-attributes-label) [*compilation-condition*](#switch-case-attributes-label) [*switch-cases*](#switch-case-attributes-label) <sub>可选</sub>
#### grammar-switch-if-directive-clause {#grammar-switch-if-directive-clause}
> *switch-if-directive 语句* → [if-directive](#switch-case-attributes-label) [compilation-condition](#switch-case-attributes-label) [switch-cases](#switch-case-attributes-label) <sub>可选</sub>
>
#### grammar_switch-elseif-directive-clauses {#grammar-switch-elseif-directive-clauses}
> *switch-elseif-directive 语句(复数)* → [*elseif-directive-clause*](#switch-case-attributes-label) [*switch-elseif-directive-clauses*](#switch-case-attributes-label)<sub>可选</sub>
#### grammar-switch-elseif-directive-clauses {#grammar-switch-elseif-directive-clauses}
> *switch-elseif-directive 语句(复数)* → [elseif-directive-clause](#switch-case-attributes-label) [switch-elseif-directive-clauses](#switch-case-attributes-label)<sub>可选</sub>
>
#### grammar_switch-elseif-directive-clause {#grammar-switch-elseif-directive-clause}
> *switch-elseif-directive 语句* → [*elseif-directive*](#switch-case-attributes-label) [*compilation-condition*](#switch-case-attributes-label) [*switch-cases*](#switch-case-attributes-label)<sub>可选</sub>
#### grammar-switch-elseif-directive-clause {#grammar-switch-elseif-directive-clause}
> *switch-elseif-directive 语句* → [elseif-directive](#switch-case-attributes-label) [compilation-condition](#switch-case-attributes-label) [switch-cases](#switch-case-attributes-label)<sub>可选</sub>
>
#### grammar_switch-else-directive-clause {#grammar-switch-else-directive-clause}
> *switch-else-directive 语句* → [*else-directive*](#switch-case-attributes-label) [*switch-cases*](#switch-case-attributes-label) <sub>可选</sub>
#### grammar-switch-else-directive-clause {#grammar-switch-else-directive-clause}
> *switch-else-directive 语句* → [else-directive](#switch-case-attributes-label) [switch-cases](#switch-case-attributes-label) <sub>可选</sub>
>
## 带标签的语句 {#labeled-statements}
你可以在循环语句或 `switch` 语句前面加上标签,它由标签名和紧随其后的冒号(`:`)组成。在 `break``continue` 后面跟上标签名可以显式地在循环语句或 `switch` 语句中改变相应的控制流。关于这两条语句用法,请参阅 [Break 语句](#break_statement) 和 [Continue 语句](#continue_statement)。
你可以在循环语句或 `switch` 语句前面加上标签,它由标签名和紧随其后的冒号(`:`)组成。在 `break``continue` 后面跟上标签名可以显式地在循环语句或 `switch` 语句中改变相应的控制流。关于这两条语句用法,请参阅 [Break 语句](#break-statement) 和 [Continue 语句](#continue-statement)。
标签的作用域在该标签所标记的语句内。可以嵌套使用带标签的语句,但标签名必须唯一。
关于使用带标签的语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章中的 [带标签的语句](../chapter2/05_Control_Flow.md#labeled_statements)。
关于使用带标签的语句的例子,请参阅 [控制流](../02_language_guide/05_Control_Flow.md) 一章中的 [带标签的语句](../02_language_guide/05_Control_Flow.md#labeled-statements)。
> 带标签的语句语法
>
>
#### labeled-statement {#labeled-statement}
> *带标签的语句* → [*语句标签*](#statement-label) [*循环语句*](#grammar_loop-statement)
> *带标签的语句* → [语句标签](#statement-label) [循环语句](#grammar-loop-statement)
>
> *带标签的语句* → [*语句标签*](#statement-label) [*if 语句*](#if-statement)
> *带标签的语句* → [语句标签](#statement-label) [if 语句](#if-statement)
>
> *带标签的语句* → [*语句标签*](#statement-label) [*switch 语句*](#switch-statement)
> *带标签的语句* → [语句标签](#statement-label) [switch 语句](#switch-statement)
>
> > *带标签的语句* → [*语句标签*](#statement-label) [*do 语句*](#sdo-statement)
> > *带标签的语句* → [语句标签](#statement-label) [do 语句](#sdo-statement)
>
#### statement-label {#statement-label}
> *语句标签* → [*标签名称*](#label-name) **:**
> *语句标签* → [标签名称](#label-name) **:**
>
#### label-name {#label-name}
> *标签名称* → [*标识符*](./02_Lexical_Structure.md#identifier)
> *标签名称* → [标识符](./02_Lexical_Structure.md#identifier)
>
## 控制转移语句 {#control-transfer-statements}
@ -401,15 +399,15 @@ case .suppressed:
>
#### control-transfer-statement {#control-transfer-statement}
> *控制转移语句* → [*break 语句*](#break-statement)
> *控制转移语句* → [break 语句](#break-statement)
>
> *控制转移语句* → [*continue 语句*](#continue-statement)
> *控制转移语句* → [continue 语句](#continue-statement)
>
> *控制转移语句* → [*fallthrough 语句*](#fallthrough-statement)
> *控制转移语句* → [fallthrough 语句](#fallthrough-statement)
>
> *控制转移语句* → [*return 语句*](#return-statement)
> *控制转移语句* → [return 语句](#return-statement)
>
> *控制转移语句* → [*throw 语句*](#throw-statement)
> *控制转移语句* → [throw 语句](#throw-statement)
>
### Break 语句 {#break-statement}
@ -426,14 +424,14 @@ case .suppressed:
无论哪种情况,控制权都会被转移给被终止的控制流语句后面的第一行语句。
关于使用 `break` 语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章的 [Break](../chapter2/05_Control_Flow.md#break) 和 [带标签的语句](../chapter2/05_Control_Flow.md#labeled_statements)。
关于使用 `break` 语句的例子,请参阅 [控制流](../02_language_guide/05_Control_Flow.md) 一章的 [Break](../02_language_guide/05_Control_Flow.md#break) 和 [带标签的语句](../02_language_guide/05_Control_Flow.md#labeled-statements)。
> break 语句语法
>
>
#### break-statement {#break-statement}
> *break 语句***break** [*标签名称*](#label-name)<sub>可选</sub>
> *break 语句***break** [标签名称](#label-name)<sub>可选</sub>
>
### Continue 语句 {#continue-statement}
@ -452,14 +450,14 @@ case .suppressed:
`for` 语句中,`continue` 语句执行后,增量表达式还是会被计算,这是因为每次循环体执行完毕后,增量表达式都会被计算。
关于使用 `continue` 语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章的 [Continue](../chapter2/05_Control_Flow.md#continue) 和 [带标签的语句](../chapter2/05_Control_Flow.md#labeled_statements)。
关于使用 `continue` 语句的例子,请参阅 [控制流](../02_language_guide/05_Control_Flow.md) 一章的 [Continue](../02_language_guide/05_Control_Flow.md#continue) 和 [带标签的语句](../02_language_guide/05_Control_Flow.md#labeled-statements)。
> continue 语句语法
>
>
#### continue-statement {#continue-statement}
> *continue 语句***continue** [*标签名称*](#label-name)<sub>可选</sub>
> *continue 语句***continue** [标签名称](#label-name)<sub>可选</sub>
>
### Fallthrough 语句 {#fallthrough-statements}
@ -467,7 +465,7 @@ case .suppressed:
`fallthrough` 语句可出现在 `switch` 语句中的任意 `case` 中,但不能出现在最后一个 `case` 中。同时,`fallthrough` 语句也不能把控制权转移到使用了值绑定的 `case`
关于在 `switch` 语句中使用 `fallthrough` 语句的例子,请参阅 [控制流](../chapter2/05_Control_Flow.md) 一章的 [控制转移语句](../chapter2/05_Control_Flow.md#control_transfer_statements)。
关于在 `switch` 语句中使用 `fallthrough` 语句的例子,请参阅 [控制流](../02_language_guide/05_Control_Flow.md) 一章的 [控制转移语句](../02_language_guide/05_Control_Flow.md#control-transfer-statements)。
> fallthrough 语句语法
>
@ -492,7 +490,7 @@ case .suppressed:
> 注意
>
>
> 正如 [可失败构造器](./06_Declarations.md#failable_initializers) 中所描述的,`return nil` 在可失败构造器中用于表明构造失败。
> 正如 [可失败构造器](./06_Declarations.md#failable-initializers) 中所描述的,`return nil` 在可失败构造器中用于表明构造失败。
>
而只写 `return` 时,仅仅是从该函数或方法中返回,而不返回任何值(也就是说,函数或方法的返回类型为 `Void` 或者说 `()`)。
@ -502,11 +500,10 @@ case .suppressed:
>
#### return-statement {#return-statement}
> *return 语句***return** [*表达式*](./04_Expressions.html#expression)<sub>可选</sub>
> *return 语句***return** [表达式](./04_Expressions.md#expression)<sub>可选</sub>
### Throw 语句 {#throw-statements}
### Throw 语句 {#throw-statements}
`throw` 语句出现在抛出函数或者抛出方法体内,或者类型被 `throws` 关键字标记的闭包表达式体内。
`throw` 语句使程序在当前作用域结束执行,并向外围作用域传播错误。抛出的错误会一直传递,直到被 `do` 语句的 `catch` 子句处理掉。
@ -518,14 +515,14 @@ case .suppressed:
表达式的结果必须符合 `ErrorType` 协议。
关于如何使用 `throw` 语句的例子,请参阅 [错误处理](../chapter2/17_Error_Handling.md) 一章的 [用 throwing 函数传递错误](../chapter2/17_Error_Handling.md#propagating_errors_using_throwing_functions)。
关于如何使用 `throw` 语句的例子,请参阅 [错误处理](../02_language_guide/17_Error_Handling.md) 一章的 [用 throwing 函数传递错误](../02_language_guide/17_Error_Handling.md#propagating-errors-using-throwing-functions)。
> throw 语句语法
>
>
#### throw-statement {#throw-statement}
> *throw 语句***throw** [*表达式*](./04_Expressions.md#expression)
> *throw 语句***throw** [表达式](./04_Expressions.md#expression)
>
## Defer 语句 {#defer-statements}
@ -562,7 +559,7 @@ f()
>
#### defer-statement {#defer-statement}
> *延迟语句***defer** [*代码块*](./06_Declarations.md#code-block)
> *延迟语句***defer** [代码块](./06_Declarations.md#code-block)
>
## Do 语句 {#do-statements}
@ -580,30 +577,43 @@ do {
statements
} catch pattern 2 where condition {
statements
} catch pattern 3, pattern 4 where condition {
statements
} catch {
statements
}
```
`switch` 语句,编译器会判断 `catch` 子句是否有遗漏。如果 `catch` 子句没有遗漏,则认为错误已被处理。否则,错误会自动传递到外作用域,被某个 `catch` 子句处理掉或者被用 `throws` 关键字声明的抛出函数继续向外抛出
`do` 代码块中的任何语句抛出了错误,程序会跳转到第一个能模式匹配该错误的 `catch` 子句。如果没有任何子句匹配,错误会传递到外层作作用域。如果错误在最顶层依旧没有被处理,程序执行会因为运行时错误而停止
如同 `switch` 语句,编译器会判断 `catch` 子句是否有遗漏。如果 `catch` 子句没有遗漏,则认为错误已被处理。否则,错误会自动传递到外层作用域,被某个 `catch` 子句处理掉或者被用 `throws` 关键字声明的抛出函数继续向外抛出。
拥有多个模式匹配的 `catch` 子句只需其中一个匹配到错误即可。如果 `catch` 子句拥有多个模式匹配,所有的模式必须包含相同的绑定常量或变量,并且每个 `catch` 子句里所有绑定的变量或常量的类型必须相同。
为了确保错误已经被处理,可以让 `catch` 子句使用匹配所有错误的模式,如通配符模式(`_`)。如果一个 `catch` 子句不指定一种具体模式,`catch` 子句会匹配任何错误,并绑定到名为 `error` 的局部常量。有关在 `catch` 子句中使用模式的更多信息,请参阅 [模式](./08_Patterns.md)。
关于如何在 `do` 语句中使用一系列 `catch` 子句的例子,请参阅 [错误处理](../chapter2/17_Error_Handling.md#handling_errors)。
关于如何在 `do` 语句中使用一系列 `catch` 子句的例子,请参阅 [错误处理](../02_language_guide/17_Error_Handling.md#handling-errors)。
> do 语句语法
>
>
#### do-statement {#do-statement}
> *do 语句***do** [*代码块*](./06_Declarations.md#code-block) [*多条 catch 子句*](#catch-clauses)<sub>可选</sub>
> *do 语句***do** [代码块](./06_Declarations.md#code-block) [多条 catch 子句](#catch-clauses)<sub>可选</sub>
>
#### catch-clauses {#catch-clauses}
> *多条 catch 子句* → [*catch 子句*](#catch-clause) [*多条 catch 子句*](#catch-clauses)<sub>可选</sub>
> *多条 catch 子句* → [catch 子句](#catch-clause) [多条 catch 子句](#catch-clauses)<sub>可选</sub>
>
#### catch-clause {#catch-clause}
> *catch 子句***catch** [*模式*](./08_Patterns.md#pattern)<sub>可选</sub> [*where 子句*](#where-clause)<sub>可选</sub> [*代码块*](05_Declarations.md#code-block)
>
> *catch 子句***catch** [模式](./08_Patterns.md#pattern)<sub>可选</sub> [where 子句](#where-clause)<sub>可选</sub> [代码块](./06_Declarations.md#code-block)
#### catch-pattern-list{#catch-pattern-list}
> *catch 模式列表* → [catch 模式](#catch-pattern) | [catch 模式](#catch-pattern) [catch 模式列表](#catch-pattern-list)
#### catch-pattern{#catch-pattern}
> *catch 模式* → [模式](./08_Patterns.md#pattern) [where 子句](./05_Statements.md#where-clause)<sub>可选</sub>
## 编译器控制语句 {#compiler-control-statements}
编译器控制语句允许程序改变编译器的行为。Swift 有三种编译器控制语句:条件编译语句、线路控制语句和编译时诊断语句。
@ -613,14 +623,15 @@ do {
>
#### compiler-control-statement {#compiler-control-statement}
> *编译器控制语句* → [*条件编译语句*](#grammar_conditional-compilation-block)
> *编译器控制语句* → [条件编译语句](#grammar-conditional-compilation-block)
>
> *编译器控制语句* → [*线路控制语句*](#line-control-statement)
> *编译器控制语句* → [线路控制语句](#line-control-statement)
>
> *编译器控制语句* → [*诊断语句*](#grammar_diagnostic-statement)
> *编译器控制语句* → [诊断语句](#grammar-diagnostic-statement)
>
### 条件编译代码块 {#Conditional-Compilation-Block}
条件编译代码块可以根据一个或多个配置来有条件地编译代码。
每一个条件编译代码块都以 `#if` 开始,`#endif` 结束。如下:
@ -642,7 +653,7 @@ statements
| `swift()` | `>=``<` 后跟版本号 |
| `compiler()` | `>=``<` 后跟版本号 |
| `canImport()` | 模块名 |
| `targetEnvironment()` | 模拟器 |
| `targetEnvironment()` | `simulator``macCatalyst` |
`swift()``compiler()` 之后的版本号包含有主版本号,可选副版本号,可选补丁版本号类似,并且用(`.`)来分隔。在比较符和版本号之间不能有空格,版本号与前面的函数相对应,比如 `compiler()` 对应的就是这个编译器的版本号,`swift()` 对应的就是你要编译的 `Swift` 语言的版本号。举个简单的例子,如果你在使用 `Swift 5` 的编译器,想编译 `Swift 4.2` ,可以看下面的例子:
@ -697,24 +708,24 @@ statements to compile if both compilation conditions are false
>
>
#### grammar_conditional-compilation-block {#grammar-conditional-compilation-block}
> *条件编译代码块* → [*if-directive 语句*](#grammar_if-directive-clause) [*elseif-directive 语句(复数)*](#grammar_elseif-directive-clauses)<sub>可选</sub> [*else-directive 语句*](#grammar_else-directive-clause)<sub>可选</sub> [*endif-directive*](#grammar_endif-directive)
#### grammar-conditional-compilation-block {#grammar-conditional-compilation-block}
> *条件编译代码块* → [if-directive 语句](#grammar-if-directive-clause) [elseif-directive 语句(复数)](#grammar-elseif-directive-clauses)<sub>可选</sub> [else-directive 语句](#grammar-else-directive-clause)<sub>可选</sub> [endif-directive](#grammar-endif-directive)
>
#### grammar_if-directive-clause {#grammar-if-directive-clause}
> *if-directive 语句* → [*if-directive*](#grammar_if-directive) [*编译条件*](#compilation-condition) [*语句(复数)*](#statements)<sub>可选</sub>
#### grammar-if-directive-clause {#grammar-if-directive-clause}
> *if-directive 语句* → [if-directive](#grammar-if-directive) [编译条件](#compilation-condition) [语句(复数)](#statements)<sub>可选</sub>
>
#### grammar_elseif-directive-clauses {#grammar-elseif-directive-clauses}
> *elseif-directive 语句(复数)* → [*elseif-directive 语句*](#grammar_elseif-directive-clause) [*elseif-directive 语句(复数)*](#grammar_elseif-directive-clauses)
#### grammar-elseif-directive-clauses {#grammar-elseif-directive-clauses}
> *elseif-directive 语句(复数)* → [elseif-directive 语句](#grammar-elseif-directive-clause) [elseif-directive 语句(复数)](#grammar-elseif-directive-clauses)
>
#### grammar_elseif-directive-clauses {#grammar-elseif-directive-clauses}
> *elseif-directive 语句* → [*elseif-directive*](#grammar_elseif-directive) [*编译条件*](#compilation-condition) [*语句(复数)*](#statements)<sub>可选</sub>
#### grammar-elseif-directive-clauses {#grammar-elseif-directive-clauses}
> *elseif-directive 语句* → [elseif-directive](#grammar-elseif-directive) [编译条件](#compilation-condition) [语句(复数)](#statements)<sub>可选</sub>
>
#### grammar_else-directive-clause {#grammar-else-directive-clause}
> *else-directive 语句* → [*else-directive*](#grammar_else-directive) [*语句(复数)*](#statements)<sub>可选</sub>
#### grammar-else-directive-clause {#grammar-else-directive-clause}
> *else-directive 语句* → [else-directive](#grammar-else-directive) [语句(复数)](#statements)<sub>可选</sub>
>
@ -729,47 +740,41 @@ statements to compile if both compilation conditions are false
#### compilation-condition {#compilation-condition}
> *编译条件* → [*平台条件*](#grammar_platform-condition)
> *编译条件* → [平台条件](#grammar-platform-condition)
>
> *编译条件* → [*标识符*](./02_Lexical_Structure.md#identifier)
> *编译条件* → [标识符](./02_Lexical_Structure.md#identifier)
>
> *编译条件* → [*布尔值字面量*](./02_Lexical_Structure.md#boolean-literal)
> *编译条件* → [布尔值字面量](./02_Lexical_Structure.md#boolean-literal)
>
> *编译条件***(** [*编译条件*](#compilation-condition) **)**
> *编译条件***(** [编译条件](#compilation-condition) **)**
>
> *编译条件***!** [*编译条件*](#compilation-condition)
> *编译条件***!** [编译条件](#compilation-condition)
>
> *编译条件* → [*编译条件*](#compilation-condition) **&&** [*编译条件*](#compilation-condition)
> *编译条件* → [编译条件](#compilation-condition) **&&** [编译条件](#compilation-condition)
>
> *编译条件* → [*编译条件*](#compilation-condition) **||** [*编译条件*](#compilation-condition)
> *编译条件* → [编译条件](#compilation-condition) **||** [编译条件](#compilation-condition)
>
#### grammar_platform-condition {#grammar-platform-condition}
#### grammar-platform-condition {#grammar-platform-condition}
#### grammar_platform-condition-os {#grammar-platform-condition-os}
> *平台条件* → **os ( [*操作系统*](#operating-system) )**
>
#### grammar-platform-condition-os {#grammar-platform-condition-os}
> *平台条件* → **os ( [操作系统](#operating-system) )**
#### grammar_platform-condition-arch {#grammar-platform-condition-arch}
> *平台条件* → **arch ( [*架构*](#architecture) )**
>
#### grammar-platform-condition-arch {#grammar-platform-condition-arch}
> *平台条件* → **arch ( [架构](#architecture) )**
#### grammar_platform-condition-swift {#grammar-platform-condition-swift}
> *平台条件***swift ( >= [*swift 版本*](#swift-version) )** | **swift ( < [*swift 版本*](#swift-version) )**
>
#### grammar-platform-condition-swift {#grammar-platform-condition-swift}
> *平台条件***swift ( >= [swift 版本](#swift-version) )** | **swift ( < [swift 版本](#swift-version) )**
#### grammar_platform-condition-compiler {#grammar-platform-condition-compiler}
> *平台条件***compiler ( >= [*swift 版本*](#swift-version) )** | **compiler ( < [*swift 版本*](#swift-version) )**
>
#### grammar-platform-condition-compiler {#grammar-platform-condition-compiler}
> *平台条件***compiler ( >= [swift 版本](#swift-version) )** | **compiler ( < [swift 版本](#swift-version) )**
#### grammar_platform-condition-canImport {#grammar-platform-condition-canImport}
> *平台条件* → **canImport ( [*模块名*](#grammar_module-name) )**
>
#### grammar-platform-condition-canImport {#grammar-platform-condition-canImport}
> *平台条件* → **canImport ( [模块名](#grammar-module-name) )**
#### grammar_platform-condition-targetEnvironment {#grammar-platform-condition-targetEnvironment}
> *平台条件* → **targetEnvironment ( [*环境*](#grammar_environment) )**
>
#### grammar-platform-condition-targetEnvironment {#grammar-platform-condition-targetEnvironment}
> *平台条件* → **targetEnvironment ( [环境](#grammar-environment) )**
#### operating-system {#operating-system}
> *操作系统***macOS** | **iOS** | **watchOS** | **tvOS**
@ -780,33 +785,28 @@ statements to compile if both compilation conditions are false
>
#### swift-version {#swift-version}
> *swift 版本* → [*十进制数字*](./02_Lexical_Structure.md#decimal-digit) ­**.** ­[*swift 版本延续*](#grammar_swift-version-continuation) <sub>可选</sub>
>
> *swift 版本* → [十进制数字](./02_Lexical_Structure.md#decimal-digit) ­**.** ­[swift 版本延续](#grammar-swift-version-continuation) <sub>可选</sub>
#### grammar_swift-version-continuation {#grammar-swift-version-continuation}
> *swift 版本延续***.** [*十进制数字*](./02_Lexical_Structure.md#decimal-digit) [*swift 版本延续*](#grammar_swift-version-continuation) <sub>可选</sub>
>
#### grammar-swift-version-continuation {#grammar-swift-version-continuation}
> *swift 版本延续***.** [十进制数字](./02_Lexical_Structure.md#decimal-digit) [swift 版本延续](#grammar-swift-version-continuation) <sub>可选</sub>
#### grammar_module-name {#grammar-module-name}
> *模块名* → [*identifier*](./02_Lexical_Structure.md#identifier)
>
#### grammar-module-name {#grammar-module-name}
> *模块名* → [identifier](./02_Lexical_Structure.md#identifier)
#### grammar_environment {#grammar-environment}
> *环境* → **模拟器**
>
#### grammar-environment {#grammar-environment}
> *环境***模拟器** | **macCatalyst**
### 行控制语句 {#line-control-statements}
行控制语句可以为被编译的源代码指定行号和文件名,从而改变源代码的定位信息,以便进行分析和调试。
行控制语句形式如下:
> \#sourceLocation(file: `filename` , line:`line number`)
> #sourceLocation(file: file path, line: line number)
>
> #sourceLocation()
> \#sourceLocation()
>
第一种的行控制语句会改变该语句之后的代码中的字面量表达式 `#line``#file` 所表示的值。`行号` 是一个大于 0 的整形字面量,会改变 `#line` 表达式的值。`文件名` 是一个字符串字面量,会改变 `#file` 表达式的值。
第一种的行控制语句会改变该语句之后的代码中的字面量表达式 `#line``#file``#filePath` 所表示的值,从行控制语句里行号的代码开始。`行号` 是一个大于 0 的整形字面量,会改变 `#line` 表达式的值。`文件名` 是一个字符串字面量,会改变 `#file``#filePath` 表达式的值。指定的字符串会变成 `#filePath` 的值,且字符串最后的路径部分会变成 `#file` 的值。
第二种的行控制语句,`#sourceLocation()`,会将源代码的定位信息重置回默认的行号和文件名。
@ -814,19 +814,17 @@ statements to compile if both compilation conditions are false
#### line-control-statement {#line-control-statement}
> 行控制语句语法
>
>*行控制语句* → **#sourceLocation(file:[文件名](#file-name),line:[行号](#line-number))**
>
> *行控制语句***#sourceLocation(file:[*文件名*](#file-name),line:[*行号*](#line-number))**
>
> *行控制语句***#sourceLocation()**
>
> *行控制语句***#sourceLocation( )**
#### line-number {#line-number}
> *行号* → 大于 0 的十进制整数
>
#### file-name {#file-name}
> *文件名* → [*静态字符串字面量*](./02_Lexical_Structure.md#static-string-literal)
>
> *文件名* → [静态字符串字面量](./02_Lexical_Structure.md#static-string-literal)
### 编译时诊断语句 {#compile-time-diagnostic-statement}
@ -843,15 +841,15 @@ statements to compile if both compilation conditions are false
>
>
#### grammar_compile-time-diagnostic-statement {#grammar-compile-time-diagnostic-statement}
> *诊断语句***#error** **(** [*diagnostic-message*](#grammar_diagnostic-message) **)**
#### grammar-compile-time-diagnostic-statement {#grammar-compile-time-diagnostic-statement}
> *诊断语句***#error** **(** [诊断消息](#grammar-diagnostic-message) **)**
>
> *诊断语句***#warning** **(** [*diagnostic-message*](#grammar_diagnostic-message) **)**
>
> *诊断语句* → [*静态字符串字面量*](./02_Lexical_Structure.md#static-string-literal)
> *诊断语句***#warning** **(** [诊断消息](#grammar-diagnostic-message) **)**
>
> *诊断语句* → [静态字符串字面量](./02_Lexical_Structure.md#static-string-literal)
## 可用性条件 {#availability-condition}
可用性条件可作为 `if``while``guard` 语句的条件,可以在运行时基于特定的平台参数来查询 API 的可用性。
可用性条件的形式如下:
@ -875,12 +873,10 @@ if #available(platform name version, ..., *) {
>
#### availability-condition {#availability-condition}
> *可用性条件***#available** **(** [*可用性参数列表*](#availability-arguments) **)**
>
> *可用性条件***#available** **(** [可用性参数列表](#availability-arguments) **)**
#### availability-arguments {#availability-arguments}
> *可用性参数列表* → [*可用性参数*](#availability-argument) | [*可用性参数*](#availability-argument) **,** [*可用性参数列表*](#availability-arguments)
>
> *可用性参数列表* → [可用性参数](#availability-argument) | [可用性参数](#availability-argument) **,** [可用性参数列表](#availability-arguments)
#### availability-argument {#availability-argument}
> *可用性参数* → [平台名称](#platform-name) [平台版本](#platform-version)
@ -905,4 +901,3 @@ if #available(platform name version, ..., *) {
> *平台版本* → [十进制数字](./02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](./02_Lexical_Structure.md#decimal-digits)
>
> *平台版本* → [十进制数字](./02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](./02_Lexical_Structure.md#decimal-digits) **.** [十进制数字](./02_Lexical_Structure.md#decimal-digits)
>

View File

@ -0,0 +1,505 @@
# 特性Attributes
在 Swift 中有两种特性,分别用于修饰声明和类型。特性提供了有关声明和类型的更多信息。例如,使用 `discardableResult` 特性声明的函数,表明该函数虽然有返回值,但如果没有使用该返回值,编译器不会产生警告。
您可以通过以下方式指定一个特性,通过符号 `@` 后跟特性的名称和特性接收的任何参数:
@`特性名`
@`特性名`(`特性参数`)
有些声明特性通过接收参数来指定特性的更多信息以及它是如何修饰某个特定的声明的。这些_特性的参数_写在圆括号内它们的格式由它们所属的特性来定义。
## 声明特性 {#declaration-attributes}
声明特性只能应用于声明。
### `available` {#available}
`available` 特性用于声明时,表示该声明的生命周期是相对于特定的平台和操作系统版本。
`available` 特性经常与参数列表一同出现,该参数列表至少有两个特性参数,参数之间由逗号分隔。这些参数由以下这些平台名字中的一个起头:
- `iOS`
- `iOSApplicationExtension`
- `macOS`
- `macOSApplicationExtension`
- `watchOS`
- `watchOSApplicationExtension`
- `tvOS`
- `tvOSApplicationExtension`
- `swift`
当然,你也可以用一个星号(`*`)来表示上面提到的所有平台。指定 Swift 版本的 `available` 特性参数,不能使用星号表示。
其余的参数,可以按照任何顺序出现,并且可以添加关于声明生命周期的附加信息,包括重要事件。
- `unavailable` 参数表示该声明在指定的平台上是无效的。当指定 Swift 版本可用性时不可使用该参数。
- `introduced` 参数表示指定平台从哪一版本开始引入该声明。格式如下:
`introduced`: `版本号`
*版本号*由一至三个正整数构成,由句点分隔的。
- `deprecated` 参数表示指定平台从哪一版本开始弃用该声明。格式如下:
`deprecated`: `版本号`
可选的*版本号*由一个或多个正整数构成,由句点分隔的。省略版本号表示该声明目前已弃用,而不提供有关弃用发生时间的任何信息。如果你省略了版本号,冒号(`:`)也可省略。
- `obsoleted` 参数表示指定平台或语言从哪一版本开始废弃该声明。当一个声明被废弃后,它就从平台或语言中移除,不能再被使用。格式如下:
`obsoleted`: `版本号`
*版本号*由一至三个正整数构成,由句点分隔的。
- `message` 参数用来提供文本信息。当使用被弃用或者被废弃的声明时,编译器会抛出该信息作为警告或错误。格式如下:
`message`: `信息内容`
_信息内容_由一个字符串构成。
- `renamed` 参数用来提供文本信息,用以表示被重命名声明的新名字。当使用声明的旧名字时,编译器会报错并提示新名字。格式如下:
`renamed`: `新名字`
_新名字_由一个字符串构成。
你可以将带有 `renamed``unavailable` 参数的 `available` 特性应用于类型别名声明,如下所示,来表明框架和库发行版本之间的声明名称已经被更改。这个组合会导致声明已重命名的编译时错误。
```swift
// 首发版本
protocol MyProtocol {
// 这里是协议定义
}
```
```swift
// 后续版本重命名了 MyProtocol
protocol MyRenamedProtocol {
// 这里是协议定义
}
@available(*, unavailable, renamed:"MyRenamedProtocol")
typealias MyProtocol = MyRenamedProtocol
```
你可以在某个声明上使用多个 `available` 特性,以指定该声明在不同平台和 Swift 版本上的可用性。如果当前目标与 `available` 特性中指定的平台或语言版本不匹配时,该声明将会被忽略。如果你使用了多个 `available` 特性,则最终效果是平台和 Swift 可用性的组合。
如果 `available` 特性除了平台名称或者语言版本参数之外,只指定了一个 `introduced` 参数,那么可以使用以下简写语法代替:
@available(`平台名称` `版本号`, *)
@available(swift `版本号`)
`available` 特性的简写语法简洁的表示了多个平台的可用性。尽管这两种形式在功能上是相同的,但请尽可能地使用简写语法形式。
```swift
@available(iOS 10.0, macOS 10.12, *)
class MyClass {
// 这里是类定义
}
```
当需要同时指定 Swift 版本和平台可用性,需要使用一个单独的 `available` 特性来指明 Swift 版本,以及一个或者多个 `available` 特性来声明平台可用性。
```swift
@available(swift 3.0.2)
@available(macOS 10.12, *)
struct MyStruct {
// 这里是结构体定义
}
```
### `discardableResult` {#discardableresult}
该特性用于的函数或方法声明,以抑制编译器中函数或方法被调用而其返回值没有被使用时的警告。
### `dynamicCallable` {#dynamiccallable}
该特性用于类、结构体、枚举或协议,以将该类型的实例视为可调用的函数。该类型必须实现 `dynamicallyCall(withArguments:)``dynamicallyCall(withKeywordArguments:)` 方法之一,或两者同时实现。
你可以调用 `dynamicCallable` 特性的实例,就像是调用一个任意数量参数的函数。
```swift
@dynamicCallable
struct TelephoneExchange {
func dynamicallyCall(withArguments phoneNumber: [Int]) {
if phoneNumber == [4, 1, 1] {
print("Get Swift help on forums.swift.org")
} else {
print("Unrecognized number")
}
}
}
let dial = TelephoneExchange()
// 使用动态方法调用
dial(4, 1, 1)
// 打印“Get Swift help on forums.swift.org”
dial(8, 6, 7, 5, 3, 0, 9)
// 打印“Unrecognized number”
// 直接调用底层方法
dial.dynamicallyCall(withArguments: [4, 1, 1])
```
`dynamicallyCall(withArguments:)` 方法的声明必须至少有一个参数遵循 [`ExpressibleByArrayLiteral`](https://developer.apple.com/documentation/swift/expressiblebyarrayliteral) 协议,如 `[Int]`,而返回值类型可以是任意类型。
如果实现 `dynamicallyCall(withKeywordArguments:)` 方法,则可以在动态方法调用中包含标签。
```swift
@dynamicCallable
struct Repeater {
func dynamicallyCall(withKeywordArguments pairs: KeyValuePairs<String, Int>) -> String {
return pairs
.map { label, count in
repeatElement(label, count: count).joined(separator: " ")
}
.joined(separator: "\n")
}
}
let repeatLabels = Repeater()
print(repeatLabels(a: 1, b: 2, c: 3, b: 2, a: 1))
// a
// b b
// c c c
// b b
// a
```
`dynamicallyCall(withKeywordArguments:)` 方法声明必须只有一个遵循 [`ExpressibleByDictionaryLiteral`](https://developer.apple.com/documentation/swift/expressiblebydictionaryliteral) 协议的参数,返回值可以任意类型。参数的 [`Key`](https://developer.apple.com/documentation/swift/expressiblebydictionaryliteral/2294108-key) 必须遵循 [`ExpressibleByStringLiteral`](https://developer.apple.com/documentation/swift/expressiblebystringliteral) 协议。上述的示例使用 [`KeyValuePairs`](https://developer.apple.com/documentation/swift/keyvaluepairs) 作为参数类型,以便调用者可以传入重复的参数标签,`a``b` 在调用 `repeat`中多次使用。
如果你同时实现两种 `dynamicallyCall` 方法,则当在方法调用中包含关键字参数时,会调用 `dynamicallyCall(withKeywordArguments:)` 方法,否则调用 `dynamicallyCall(withArguments:)` 方法。
你只能调用参数和返回值与 `dynamicallyCall` 方法实现匹配的动态调用实例。在下面示例的调用无法编译,因为其 `dynamicallyCall(withArguments:)` 实现不接受 `KeyValuePairs<String, String>` 参数。
```swift
repeatLabels(a: "four") // Error
```
### `dynamicMemberLookup` {#dynamicmemberlookup}
该特性用于类、结构体、枚举或协议,让其能在运行时查找成员。该类型必须实现 `subscript(dynamicMemberLookup:)` 下标。
在显式成员表达式中,如果指定成员没有相应的声明,则该表达式被理解为对该类型的 `subscript(dynamicMemberLookup:)` 下标调用,将有关该成员的信息作为参数传递。下标接收参数既可以是键路径,也可以是成员名称字符串;如果你同时实现这两种方式的下标调用,那么以键路径参数方式为准。
`subscript(dynamicMemberLookup:)` 实现允许接收 [`KeyPath`](https://developer.apple.com/documentation/swift/keypath)[`WritableKeyPath`](https://developer.apple.com/documentation/swift/writablekeypath) 或 [`ReferenceWritableKeyPath`](https://developer.apple.com/documentation/swift/referencewritablekeypath) 类型的键路径参数。它可以使用遵循 [`ExpressibleByStringLiteral`](https://developer.apple.com/documentation/swift/expressiblebystringliteral) 协议的类型作为参数来接受成员名 -- 通常情况下是 `String`。下标返回值类型可以为任意类型。
按成员名进行的动态成员查找可用于围绕编译时无法进行类型检查的数据创建包装类型,例如在将其他语言的数据桥接到 `Swift` 时。例如:
```swift
@dynamicMemberLookup
struct DynamicStruct {
let dictionary = ["someDynamicMember": 325,
"someOtherMember": 787]
subscript(dynamicMember member: String) -> Int {
return dictionary[member] ?? 1054
}
}
let s = DynamicStruct()
// 使用动态成员查找
let dynamic = s.someDynamicMember
print(dynamic)
// 打印“325”
// 直接调用底层下标
let equivalent = s[dynamicMember: "someDynamicMember"]
print(dynamic == equivalent)
// 打印“true”
```
根据键路径来动态地查找成员,可用于创建一个包裹数据的包装类型,该类型支持在编译时期进行类型检查。例如:
```swift
struct Point { var x, y: Int }
@dynamicMemberLookup
struct PassthroughWrapper<Value> {
var value: Value
subscript<T>(dynamicMember member: KeyPath<Value, T>) -> T {
get { return value[keyPath: member] }
}
}
let point = Point(x: 381, y: 431)
let wrapper = PassthroughWrapper(value: point)
print(wrapper.x)
```
### `frozen` {#frozen}
针对枚举或者结构体的声明使用该特性,可以限制你对该类型的修改。它只有在编译迭代库时被允许使用。未来版本的库不能通过添加、删除或重新排序枚举的 case 或结构的存储实例属性来更改声明。在未冻结的类型上,这些操作都是允许的,但是他们破坏了冻结类型的 ABI 兼容性。
> 注意
> 当编译器不处于迭代库的模式,所有的结构体和枚举都是隐性冻结,并且该特性会被忽视。
在迭代库的模式中,与未冻结结构体和枚举的成员进行交互的代码在被编译时,允许它在不重新编译的情况下继续工作,即使在新版本的库中添加、删除或重新排序该类型的成员。编译器用类似运行时查找信息和添加间接层的技术使之可能。将一个枚举或者结构体标记为冻结将以放弃这种灵活性为代价来获取性能上的提升:未来版本的库只能对类型进行有限的更改,但编译器可以对与类型成员交互的代码进行额外的优化。
使用冻结类型,结构体存储属性的类型以及枚举 case 的关联值必须是 `public` 或使用 `usablefrominline` 特性标记。冻结结构体的属性不能有属性观察器,为存储实例属性提供初始值的表达式必须遵循与 `inlinable` 函数相同的限制,如 [`inlinable`](#inlinable) 中所述。
要在命令行上启用迭代库模式,请将 `-enable-library-evolution` 选项传递给 Swift 编译器。要在 Xcode 中支持它,则将生成设置 “Build Libraries for Distribution”`BUILD_LIBRARY_FOR_DISTRIBUTION`)设置为 Yes详情查看 [`Xcode 帮助文档`](https://help.apple.com/xcode/mac/current/#/dev04b3a04ba)。
针对冻结枚举的 switch 语法,不需要 `default` case就像 [`对未来枚举的 case 进行 switch`](./05_Statements.md#future-case)。在针对冻结枚举使用 switch 语法时包含 `default``@unknown default` case 将生成警告,因为该代码永远不会执行。
### `GKInspectable` {#gkinspectable}
应用此特性可将自定义 GameplayKit 组件属性公开到 SpriteKit 编辑器 UI。应用此特性同时表示应用了 `objc` 特性。
### `inlinable` {#inlinable}
该特性用于函数、方法、计算属性、下标、便利构造器或析构器的声明,以将该声明的实现公开为模块公开接口的一部分。编译器允许在调用处把 `inlinable` 标记的符号替换为符号实现的副本。
内联代码可以与任意模块中 `public` 访问级别的符号进行交互,同时可以与在相同模块中标记 `usableFromInline` 特性的 `internal` 访问级别的符号进行交互。内联代码不能与 `private``fileprivate` 级别的符号进行交互。
该特性不能用于嵌套在函数内的声明,也不能用于 `fileprivate``private` 访问级别的声明。在内联函数内定义的函数和闭包都是隐式内联的,即使他们不能标记该特性。
### `nonobjc` {#nonobjc}
针对方法、属性、下标、或构造器的声明使用该特性将覆盖隐式的 `objc` 特性。`nonobjc` 特性告诉编译器该声明不能在 Objective-C 代码中使用,即便它能在 Objective-C 中表示。
该特性用在扩展中,与在没有明确标记为 `objc` 特性的扩展中给每个成员添加该特性具有相同效果。
可以使用 `nonobjc` 特性解决标有 `objc` 的类中桥接方法的循环问题,该特性还允许对标有 `objc` 的类中的构造器和方法进行重载。
标有 `nonobjc` 特性的方法不能重写标有 `objc` 特性的方法。然而,标有 `objc` 特性的方法可以重写标有 `nonobjc` 特性的方法。同样,标有 `nonobjc` 特性的方法不能满足标有 `@objc` 特性的协议中的方法要求。
### `NSApplicationMain` {#nsapplicationmain}
在类上使用该特性表示该类是应用程序委托类。使用该特性与调用 `NSApplicationMain(_:_:)` 函数的效果相同。
如果你不想使用这个特性,可以提供一个 `main.swift` 文件,并在代码顶层调用 `NSApplicationMain(_:_:)` 函数,如下所示:
```swift
import AppKit
NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
```
### `NSCopying` {#nscopying}
该特性用于修饰一个类的存储型变量属性。该特性将使属性的设值方法使用传入值的`副本`进行赋值,这个值由传入值的 `copyWithZone(_:)` 方法返回,而不是传入值本身。该属性的类型必需符合 `NSCopying` 协议。
`NSCopying` 特性的行为与 Objective-C 中的 `copy` 属性特性相似。
### `NSManaged` {#nsmanaged}
该特性用于修饰 `NSManagedObject` 子类中的实例方法或存储型变量属性,表明它们的实现由 `Core Data` 在运行时基于相关实体描述动态提供。对于标记了 `NSManaged` 特性的属性,`Core Data` 也会在运行时为其提供存储。应用这个特性也意味着 `objc` 特性。
### `objc` {#objc}
该特性用于修饰任何可以在 Objective-C 中表示的声明。比如,非嵌套类、协议、非泛型枚举(仅限原始值为整型的枚举)、类的属性和方法(包括存取方法)、协议以及协议中的可选成员、构造器以及下标运算符。`objc` 特性告诉编译器这个声明可以在 Objective-C 代码中使用。
该特性用在扩展中,与在没有明确标记为 `nonobjc` 特性的扩展中给每个成员添加该特性具有相同效果。
编译器隐式地将 `objc` 特性添加到 Objective-C 中定义的任何类的子类。但是,子类不能是泛型的,并且不能继承于任何泛型类。你可以将 `objc` 特性显式添加到满足这些条件的子类,来指定其 Objective-C 名称,如下所述。添加了 `objc` 的协议不能继承于没有此特性的协议。
在以下情况中同样会隐式的添加 `objc` 特性:
- 父类有 `objc` 特性,且重写为子类的声明。
- 遵循带有 `objc` 特性协议的声明。
- 带有 `IBAction``IBSegueAction``IBOutlet``IBDesignable``IBInspectable``NSManaged``GKInspectable` 特性的声明。
如果你将 `objc` 特性应用于枚举,每一个枚举 case 都会以枚举名称和 case 名称组合的方式暴露在 Objective-C 代码中。实例名称的首字母大写。例如,在 Swift 枚举类型 `Planet` 中有一个名为 `Venus` 的 case该 case 暴露在 Objective-C 代码中时叫做 `PlanetVenus`
`objc` 特性可以接受一个可选的特性参数,由标识符构成。当你想把 `objc` 所修饰的实体以一个不同的名字暴露给 Objective-C 时,你就可以使用这个特性参数。你可以使用这个参数来命名类、枚举类型、枚举 case、协议、方法、存取方法以及构造器。如果你要指定类、协议或枚举在 Objective-C 下的名称,请在名称上包含三个字母的前缀,就像 [Objective-C 编程](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html#//apple-ref/doc/uid/TP40011210) 中的 [约定](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Conventions/Conventions.html#//apple-ref/doc/uid/TP40011210-CH10-SW1)描述的一样。下面的例子把 `ExampleClass` 中的 `enabled` 属性的取值方法暴露给 Objective-C名字是 `isEnabled`,而不是它原来的属性名。
```swift
class ExampleClass: NSObject {
@objc var enabled: Bool {
@objc(isEnabled) get {
// 返回适当的值
}
}
}
```
更多相关信息,请参考 [把 Swift 导入 Objective-C](https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_swift_into_objective-c)。
> 注意
> 具有 `objc` 特性的实参也会改变那个声明的运行时名称。在调用与 Objective-C 运行时交互的函数时,比如 [NSClassFromString](https://developer.apple.com/documentation/foundation/1395135-nsclassfromstring),以及在应用程序的 info.plist 文件中指定类名时,你会用到运行时名称。如果你通过传递实参的方式来指定名称,这个名称会作为 Objective-C 代码中的名称和运行时名称。如果你不使用这个实参,在 Objective-C 代码中使用的名称会与 Swift 代码中的名称匹配,并且运行时名称会遵循标准 Swift 编译器名称管理的惯例。
### `objcMembers` {#objcmembers}
该特性用于类声明,以将 `objc` 特性应用于该类、扩展、子类以及子类的扩展的所有 Objective-C 兼容成员。
大多数代码应该使用 `objc` 特性,以暴露所需的声明。如果需要暴露多个声明,可以将其分组到添加 `objc` 特性的扩展中。`objcMembers` 特性为大量使用 Objective-C 运行时的内省工具的库提供了便利。添加不必要的 `objc` 特性会增加二进制体积并影响性能。
### `propertyWrapper` {#propertywrapper}
在类、结构体或者枚举的声明时使用该特性,可以让其成为一个属性包装器。如果将该特性应用在一个类型上,将会创建一个与该类型同名的自定义特性。将这个新的特性用于类、结构体、枚举的属性,则可以通过包装器的实例封装对该属性的访问。局部和全局变量不能使用属性包装器。
包装器必须定义一个 `wrappedValue` 实例属性。该属性 _wrapped value_ 是该属性存取方法暴露的值。大多数时候,`wrappedValue` 是一个计算属性,但它可以是一个存储属性。包装器负责定义和管理其包装值所需的任何底层存储。编译器通过在包装属性的名称前加下划线(`_`)来为包装器的实例提供同步存储。例如,`someProperty` 的包装器存储为 `_someProperty`。包装器的同步存储具有 `private` 的访问控制级别。
拥有属性包装器的属性可以包含 `willSet``didSet` 闭包,但是不能重写编译器合成的 `get``set` 闭包。
Swift 为属性包装器的构造函数提供了两种形式的语法糖。可以在包装值的定义中使用赋值语法,将赋值语句右侧的表达式作为值传递给属性包装器构造函数中的 `wrappedValue` 参数。同样的,你也可以为包装器提供一些参数,这些参数将会传递给包装器的构造函数。就像下面的例子,`SomeStruct` 中,定义 `SomeWrapper` 的地方各自调用了对应的构造函数。
```swift
@propertyWrapper
struct SomeWrapper {
var wrappedValue: Int
var someValue: Double
init() {
self.wrappedValue = 100
self.someValue = 12.3
}
init(wrappedValue: Int) {
self.wrappedValue = wrappedValue
self.someValue = 45.6
}
init(wrappedValue value: Int, custom: Double) {
self.wrappedValue = value
self.someValue = custom
}
}
struct SomeStruct {
// 使用 init()
@SomeWrapper var a: Int
// 使用 init(wrappedValue:)
@SomeWrapper var b = 10
// 两个都是使用 init(wrappedValue:custom:)
@SomeWrapper(custom: 98.7) var c = 30
@SomeWrapper(wrappedValue: 30, custom: 98.7) var d
}
```
属性包装器中 _projected value_ 是它可以用来暴露额外功能的第二个值。属性包装器的作者负责确认其映射值的含义并定义公开映射值的接口。若要通过属性包装器来映射值,请在包装器的类型上定义 `projectedValue` 实例属性。编译器通过在包装属性的名称前面加上美元符号(`$`)来合成映射值的标识符。例如,`someProperty` 的映射值是 `$someProperty`。映射值具有与原始包装属性相同的访问控制级别。
```swift
@propertyWrapper
struct WrapperWithProjection {
var wrappedValue: Int
var projectedValue: SomeProjection {
return SomeProjection(wrapper: self)
}
}
struct SomeProjection {
var wrapper: WrapperWithProjection
}
struct SomeStruct {
@WrapperWithProjection var x = 123
}
let s = SomeStruct()
s.x // Int value
s.$x // SomeProjection value
s.$x.wrapper // WrapperWithProjection value
```
### `requires-stored-property-inits` {#requires-stored-property-inits}
该特性用于类声明,以要求类中所有存储属性提供默认值作为其定义的一部分。对于从中继承的任何类都推断出 `NSManagedObject` 特性。
### `testable` {#testable}
将此特性应用于 `import` 声明以导入该模块,并更改其访问控制以简化对该模块代码的测试。这样就能访问被导入模块中的任何标有 `internal` 访问级别修饰符的实体,犹如它们被标记了 `public` 访问级别修饰符。测试也可以访问使用 `internal` 或者 `public` 访问级别修饰符标记的类和类成员,就像它们是 `open` 访问修饰符声明的。被导入的模块必须以允许测试的方式编译。
### `UIApplicationMain` {#uiapplicationmain}
在类上使用该特性表示该类是应用程序委托类。使用该特性与调用 `UIApplicationMain` 函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
如果你不想使用这个特性,可以提供一个 `main.swift` 文件,并在代码顶层调用 `UIApplicationMain(_:_:_:_:)` 函数。比如,如果你的应用程序使用一个继承于 `UIApplication` 的自定义子类作为主要类,你可以调用 `UIApplicationMain(_:_:_:_:)` 函数而不是使用该特性。
### `usableFromInline` {#usablefrominline}
该特性用于函数、方法、计算属性、下标、构造器或析构器的声明,以在同一模块中允许该符号用于内联代码的声明。声明必须具有 `internal` 访问级别修饰符。被标记为 `usableFromInline` 的结构体或类它们属性的类型只能是被标记为 public 或者 `usableFromInline` 的类型。被标记为 `usableFromInline` 的枚举,它 case 的真实值或者关联类型只能是被标记为 public 或者 `usableFromInline` 的类型。
`public` 访问修饰符相同的是,该特性将声明公开为模块公共接口的一部分。区别于 `public`,编译器不允许在模块外部的代码通过名称引用 `usableFromInline` 标记的声明,即使导出了声明符号也无法引用。但是,模块外的代码仍然可以通过运行时与声明符号进行交互。
标记为 `inlinable` 特性的声明,在内联代码中可以隐式使用。虽然 `inlinable``usableFromInline` 可以用于 `internal` 声明,但这两者不能同时使用。
### `warn-unqualified-access` {#warn-unqualified-access}
该特性应用于顶级函数、实例方法、类方法或静态方法,以在没有前置限定符(例如模块名称、类型名称、实例变量或常量)的情况下使用该函数或方法时触发警告。使用该特性可以减少在同一作用域里访问的同名函数之间的歧义。
例如Swift 标准库包含 [`min(_:_:)`](https://developer.apple.com/documentation/swift/1538339-min/) 顶级函数和用于序列比较元素的 [`min()`](https://developer.apple.com/documentation/swift/sequence/1641174-min) 方法。序列方法声明使用了 `warn_unqualified_access`,以减少在 `Sequence` 扩展中使用它们的歧义。
### Interface Builder 使用的声明特性 {#declaration-attributes-used-by-interface-builder}
Interface Builder 特性是 Interface Builder 用来与 Xcode 同步的声明特性。Swift 提供了以下的 Interface Builder 特性:`IBAction``IBSegueAction``IBOutlet``IBDesignable`,以及 `IBInspectable`。这些特性与 Objective-C 中对应的特性在概念上是相同的。
`IBOutlet``IBInspectable` 用于修饰一个类的属性声明。`IBAction``IBSegueAction` 特性用于修饰一个类的方法声明,`IBDesignable` 用于修饰类的声明。
应用 `IBAction``IBSegueAction``IBOutlet``IBDesignable` 或者 `IBInspectable` 特性都意味着同时应用 `objc` 特性。
## 类型特性 {#type-attributes}
类型特性只能用于修饰类型。
### `autoclosure` {#autoclosure}
这个特性通过把表达式自动封装成无参数的闭包来延迟表达式的计算。它可以修饰类型为返回表达式结果类型的无参数函数类型的函数参数。关于如何使用 `autoclosure` 特性的例子,请参阅 [自动闭包](../02_language_guide/07_Closures.md#autoclosures) 和 [函数类型](./03_Types.md#function-type)。
### `convention` {#convention}
该特性用于修饰函数类型,它指出了函数调用的约定。
`convention` 特性总是与下面的参数之一一起出现。
- `swift` 参数用于表示一个 Swift 函数引用。这是 Swift 中函数值的标准调用约定。
- `block` 参数用于表示一个 Objective-C 兼容的块引用。函数值会作为一个块对象的引用,块是一种 `id` 兼容的 Objective-C 对象,其中嵌入了调用函数。调用函数使用 C 的调用约定。
- `c` 参数用于表示一个 C 函数引用。函数值没有上下文,不具备捕获功能,并且使用 C 的调用约定。
除了少数例外,当函数需要任何其他调用约定时,可以转换成任意调用约定的函数。非范型全局函数、不捕获任何局部变量的局部函数或不捕获任何局部变量的闭包可以转换为 C 调用约定。其余的 Swift 函数不能转换成 C 调用约定。一个 Objective-C 块调用约定的函数不能转换成 C 调用约定。
### `escaping` {#escaping}
在函数或者方法声明上使用该特性,它表示参数将不会被存储以供延迟执行。这将确保参数不会超出函数调用的生命周期。在使用 `escaping` 特性声明的函数类型中访问属性和方法时需要显式地使用 `self.`。关于如何使用 `escaping` 特性的例子,请参阅 [逃逸闭包](../02_language_guide/07_Closures.md#escaping-closures)。
## Switch Case 特性 {#switch-case-attributes}
你只能在 switch cases 语句中使用 switch case 特性。
### `unknown` {#unknown}
该特性用于 switch case用于没有匹配上代码编译时已知 case 的情况。有关如何使用 `unknown` 特性的示例,可参阅 [对未来枚举的 `case` 进行 `switch`](./05_Statements.md#future-case)。
> 特性语法
>
>
>
#### attribute {#attribute}
>
> *特性* → @ [特性名](#attribute-name) [特性参数子句](#atribute-argument-clause)<sub>可选</sub>
>
>
#### attribute-name {#attribute-name}
>
> *特性名* → [标识符](./02_Lexical_Structure.md#identifier)
>
>
#### atribute-argument-clause {#atribute-argument-clause}
>
> *特性参数子句* → **(** [均衡令牌列表](#balanced-tokens)<sub>可选</sub> **)**
>
>
#### attributes {#attributes}
>
> *特性列表* → [特性](#attribute) [特性列表](#attributes)<sub>可选</sub>
>
>
>
#### balanced-tokens {#balanced-tokens}
>
> *均衡令牌列表* → [均衡令牌](#balanced-token) [均衡令牌列表](#balanced-tokens)<sub>可选</sub>
>
>
#### balanced-token {#balanced-token}
>
> *均衡令牌* → **(** [均衡令牌列表](#balanced-tokens)<sub>可选</sub> **)**
>
> *均衡令牌* → **\[** [均衡令牌列表](#balanced-tokens)<sub>可选</sub> **\]**
>
> *均衡令牌* → **{** [均衡令牌列表](#balanced-tokens)<sub>可选</sub> **}**
>
> *均衡令牌* → 任意标识符,关键字,字面量或运算符
>
> *均衡令牌* → 任意标点除了 **(****)****[****]****{**,或 **}**
>

View File

@ -4,7 +4,7 @@
Swift 中的模式分为两类:一种能成功匹配任何类型的值,另一种在运行时匹配某个特定值时可能会失败。
第一类模式用于解构简单变量、常量和可选绑定中的值。此类模式包括通配符模式、标识符模式,以及包含前两种模式的值绑定模式和元组模式。你可以为这类模式指定一个类型注,从而限制它们只能匹配某种特定类型的值。
第一类模式用于解构简单变量、常量和可选绑定中的值。此类模式包括通配符模式、标识符模式,以及包含前两种模式的值绑定模式和元组模式。你可以为这类模式指定一个类型注,从而限制它们只能匹配某种特定类型的值。
第二类模式用于全模式匹配,这种情况下你试图匹配的值在运行时可能不存在。此类模式包括枚举用例模式、可选模式、表达式模式和类型转换模式。你在 `switch` 语句的 `case` 标签中,`do` 语句的 `catch` 子句中,或者在 `if``while``guard``for-in` 语句的 `case` 条件句中使用这类模式。
@ -12,21 +12,21 @@ Swift 中的模式分为两类:一种能成功匹配任何类型的值,另
>
#### pattern {#pattern}
> *模式* → [*通配符模式*](#wildcard_pattern) [*类型标注*](03_Types.md#type-annotation)<sub>可选</sub>
> *模式* → [通配符模式](#wildcard-pattern) [类型注解](./03_Types.md#type-annotation)<sub>可选</sub>
>
> *模式* → [*标识符模式*](#identifier_pattern) [*类型标注*](03_Types.md#type-annotation)<sub>可选</sub>
> *模式* → [标识符模式](#identifier-pattern) [类型注解](./03_Types.md#type-annotation)<sub>可选</sub>
>
> *模式* → [*值绑定模式*](#value-binding-pattern)
> *模式* → [值绑定模式](#value-binding-pattern)
>
> *模式* → [*元组模式*](#tuple-pattern) [*类型标注*](03_Types.md#type-annotation)<sub>可选</sub>
> *模式* → [元组模式](#tuple-pattern) [类型注解](./03_Types.md#type-annotation)<sub>可选</sub>
>
> *模式* → [*枚举用例模式*](#enum-case-pattern)
> *模式* → [枚举用例模式](#enum-case-pattern)
>
> *模式* → [*可选模式*](#optional-pattern)
> *模式* → [可选模式](#optional-pattern)
>
> *模式* → [*类型转换模式*](#type-casting-pattern)
> *模式* → [类型转换模式](#type-casting-pattern)
>
> *模式* → [*表达式模式*](#expression-pattern)
> *模式* → [表达式模式](#expression-pattern)
>
## 通配符模式Wildcard Pattern {#wildcard-pattern}
@ -41,8 +41,7 @@ for _ in 1...3 {
> 通配符模式语法
>
#### wildcard-pattern {#wildcard-pattern}
> #### wildcard-pattern {#wildcard-pattern}
> *通配符模式* → **_**
>
@ -59,9 +58,8 @@ let someValue = 42
> 标识符模式语法
>
#### identifier-pattern {#identifier-pattern}
> *标识符模式* → [*标识符*](./02_Lexical_Structure.md#identifier)
> #### identifier-pattern {#identifier-pattern}
> *标识符模式* → [标识符](./02_Lexical_Structure.md#identifier)
>
## 值绑定模式Value-Binding Pattern {#value-binding-pattern}
@ -83,15 +81,14 @@ case let (x, y):
> 值绑定模式语法
>
#### value-binding-pattern {#value-binding-pattern}
> *值绑定模式***var** [*模式*](#pattern) | **let** [*模式*](#pattern)
> #### value-binding-pattern {#value-binding-pattern}
> *值绑定模式***var** [模式](#pattern) | **let** [模式](#pattern)
>
## 元组模式 {#tuple-pattern}
*元组模式*是由逗号分隔的,具有零个或多个模式的列表,并由一对圆括号括起来。元组模式匹配相应元组类型的值。
你可以使用类型注去限制一个元组模式能匹配哪种元组类型。例如,在常量声明 `let (x, y): (Int, Int) = (1, 2)` 中的元组模式 `(x, y): (Int, Int)` 只匹配两个元素都是 `Int` 类型的元组。
你可以使用类型注去限制一个元组模式能匹配哪种元组类型。例如,在常量声明 `let (x, y): (Int, Int) = (1, 2)` 中的元组模式 `(x, y): (Int, Int)` 只匹配两个元素都是 `Int` 类型的元组。
当元组模式被用于 `for-in` 语句或者变量和常量声明时,它仅可以包含通配符模式、标识符模式、可选模式或者其他包含这些模式的元组模式。比如下面这段代码就不正确,因为 `(x, 0)` 中的元素 `0` 是一个表达式模式:
@ -113,29 +110,42 @@ let (a): Int = 2 // a: Int = 2
> 元组模式语法
>
#### tuple-pattern {#tuple-pattern}
> *元组模式***(** [*元组模式元素列表*](#tuple-pattern-element-list)<sub>可选</sub> **)**
> #### tuple-pattern {#tuple-pattern}
> *元组模式***(** [元组模式元素列表](#tuple-pattern-element-list)<sub>可选</sub> **)**
>
#### tuple-pattern-element-list {#tuple-pattern-element-list}
> *元组模式元素列表* → [*元组模式元素*](#tuple-pattern-element) | [*元组模式元素*](#tuple-pattern-element) **,** [*元组模式元素列表*](#tuple-pattern-element-list)
> *元组模式元素列表* → [元组模式元素](#tuple-pattern-element) | [元组模式元素](#tuple-pattern-element) **,** [元组模式元素列表](#tuple-pattern-element-list)
>
#### tuple-pattern-element {#tuple-pattern-element}
> *元组模式元素* → [*模式*](#pattern)
> #### tuple-pattern-element {#tuple-pattern-element}
> *元组模式元素* → [模式](#pattern)
>
## 枚举用例模式Enumeration Case Pattern {#enumeration-case-pattern}
*枚举用例模式*匹配现有的某个枚举类型的某个用例。枚举用例模式出现在 `switch` 语句中的 `case` 标签中,以及 `if``while``guard``for-in` 语句的 `case` 条件中。
如果你准备匹配的枚举用例有任何关联的值,则相应的枚举用例模式必须指定一个包含每个关联值元素的元组模式。关于使用 `switch` 语句来匹配包含关联值的枚举用例的例子,请参阅 [关联值](../chapter2/08_Enumerations.md#associated_values)。
如果你准备匹配的枚举用例有任何关联的值,则相应的枚举用例模式必须指定一个包含每个关联值元素的元组模式。关于使用 `switch` 语句来匹配包含关联值的枚举用例的例子,请参阅 [关联值](../02_language_guide/08_Enumerations.md#associated-values)。
枚举用例模式同样会匹配那些被包装成可选值的用例。简化的语法能将可选模式过滤掉。注意,由于 `Optional` 是枚举实现的,`.none``.some` 都会作为枚举类型的用例出现在 switch 中。
```swift
enum SomeEnum { case left, right }
let x: SomeEnum? = .left
switch x {
case .left:
print("Turn left")
case .right:
print("Turn right")
case nil:
print("Keep going straight")
}
// 打印 "Turn left"
```
> 枚举用例模式语法
>
#### enum-case-pattern {#enum-case-pattern}
> *枚举用例模式* → [*类型标识*](./03_Types.md#type-identifier)<sub>可选</sub> **.** [*枚举用例名*](./06_Declarations.md#enum-case-name) [*元组模式*](#tuple-pattern)<sub>可选</sub>
> #### enum-case-pattern {#enum-case-pattern}
> *枚举用例模式* → [类型标识](./03_Types.md#type-identifier)<sub>可选</sub> **.** [枚举用例名](./06_Declarations.md#enum-case-name) [元组模式](#tuple-pattern)<sub>可选</sub>
>
## 可选模式Optional Pattern {#optional-pattern}
@ -171,9 +181,8 @@ for case let number? in arrayOfOptinalInts {
> 可选模式语法
>
#### optional-pattern {#optional-pattern}
> *可选模式* → [*标识符模式*](./03_Types.md#type-identifier) **?**
> #### optional-pattern {#optional-pattern}
> *可选模式* → [标识符模式](./03_Types.md#type-identifier) **?**
>
## 类型转换模式Type-Casting Patterns {#type-casting-patterns}
@ -188,21 +197,18 @@ for case let number? in arrayOfOptinalInts {
`as` 模式仅当一个值的类型在运行时和 `as` 模式右边的指定类型一致,或者是其子类的情况下,才会匹配这个值。如果匹配成功,被匹配的值的类型被转换成 `as` 模式右边指定的类型。
关于使用 `switch` 语句配合 `is` 模式和 `as` 模式来匹配值的例子,请参阅 [Any 和 AnyObject 的类型转换](../chapter2/18_Type_Casting.md#type_casting_for_any_and_anyobject)。
关于使用 `switch` 语句配合 `is` 模式和 `as` 模式来匹配值的例子,请参阅 [Any 和 AnyObject 的类型转换](../02_language_guide/18_Type_Casting.md#type-casting-for-any-and-anyobject)。
> 类型转换模式语法
>
#### type-casting-pattern {#type-casting-pattern}
> *类型转换模式* → [*is 模式*](#is-pattern) | [*as 模式*](#as-pattern)
> #### type-casting-pattern {#type-casting-pattern}
> *类型转换模式* → [is 模式](#is-pattern) | [as 模式](#as-pattern)
>
#### is-pattern {#is-pattern}
> *is 模式***is** [*类型*](./03_Types.md#type)
> #### is-pattern {#is-pattern}
> *is 模式***is** [类型](./03_Types.md#type)
>
#### as-pattern {#as-pattern}
> *as 模式* → [*模式*](#pattern) **as** [*类型*](03_Types.md#type)
> #### as-pattern {#as-pattern}
> *as 模式* → [模式](#pattern) **as** [类型](./03_Types.md#type)
>
## 表达式模式Expression Pattern {#expression-pattern}
@ -228,7 +234,6 @@ default:
```swift
// 重载 ~= 运算符对字符串和整数进行比较
func ~=(pattern: String, value: Int) -> Bool {
>
return pattern == "\(value)"
}
@ -243,7 +248,6 @@ default:
> 表达式模式语法
>
#### expression-pattern {#expression-pattern}
> *表达式模式* → [*表达式*](./04_Expressions.md#expression)
> #### expression-pattern {#expression-pattern}
> *表达式模式* → [表达式](./04_Expressions.md#expression)
>

View File

@ -2,7 +2,7 @@
本节涉及泛型类型、泛型函数以及泛型构造器的参数,包括形参和实参。声明泛型类型、函数或构造器时,须指定相应的类型参数。类型参数相当于一个占位符,当实例化泛型类型、调用泛型函数或泛型构造器时,就用具体的类型实参替代之。
关于 Swift 语言的泛型概述,请参阅 [泛型](../chapter2/22_Generics.md)。
关于 Swift 语言的泛型概述,请参阅 [泛型](../02_language_guide/22_Generics.md)。
## 泛型形参子句 {#generic-parameter}
*泛型形参子句*指定泛型类型或函数的类型形参,以及这些参数相关的约束和要求。泛型形参子句用尖括号(`<>`)包住,形式如下:
@ -21,7 +21,6 @@
```swift
func simpleMax<T: Comparable>(_ x: T, _ y: T) -> T {
>
if x < y {
return y
}
@ -50,51 +49,63 @@ simpleMax(3.14159, 2.71828) // T 被推断为 Double 类型
当然,替代类型形参的类型实参必须满足所有的约束和要求。
`where` 子句可以存在于包含类型参数的声明中,或作为声明的一部分,被嵌套另一个在含有类型参数的声明中。被嵌套的 `where` 子句依然可以指向包围它的声明中的类型参数,此时 `where` 子句需要满足的条件仅用于它被声明的地方。
如果外层的声明也有一个 `where` 子句,两个子句的条件都需要满足。下面的例子中,`startsWithZero()` 只有在 `Element` 同时满足 `SomeProtocol``Numeric` 才有效。
```swift
extension Collection where Element: SomeProtocol {
func startsWithZero() -> Bool where Element: Numeric {
return first == .zero
}
}
```
泛型函数或构造器可以重载,但在泛型形参子句中的类型形参必须有不同的约束或要求,抑或二者皆不同。当调用重载的泛型函数或构造器时,编译器会根据这些约束来决定调用哪个重载函数或构造器。
更多关于泛型 where 从句的信息和关于泛型函数声明的例子,可以看一看 [泛型 where 子句](../chapter2/22_Generics.md#where_clauses)
更多关于泛型 where 从句的信息和关于泛型函数声明的例子,可以看一看 [泛型 where 子句](../02_language_guide/22_Generics.md#where-clauses)
> 泛型形参子句语法
>
#### generic-parameter-clause {#generic-parameter-clause}
> *泛型形参子句***<** [*泛型形参列表*](#generic-parameter-list) [*约束子句*](#requirement-clause)<sub>可选</sub> **>**
> *泛型形参子句***<** [泛型形参列表](#generic-parameter-list) [约束子句](#requirement-clause)<sub>可选</sub> **>**
>
#### generic-parameter-list {#generic-parameter-list}
> *泛型形参列表* → [*泛形形参*](#generic-parameter) | [*泛形形参*](#generic-parameter) **,** [*泛型形参列表*](#generic-parameter-list)
> *泛型形参列表* → [泛形形参](#generic-parameter) | [泛形形参](#generic-parameter) **,** [泛型形参列表](#generic-parameter-list)
>
#### generic-parameter {#generic-parameter}
> *泛形形参* → [*类型名称*](./03_Types.md#type-name)
> *泛形形参* → [类型名称](./03_Types.md#type-name)
>
> *泛形形参* → [*类型名称*](./03_Types.md#type-name) **:** [*类型标识符*](./03_Types.md#type-identifier)
> *泛形形参* → [类型名称](./03_Types.md#type-name) **:** [类型标识符](./03_Types.md#type-identifier)
>
> *泛形形参* → [*类型名称*](./03_Types.md#type-name) **:** [*协议合成类型*](./03_Types.md#protocol-composition-type)
> *泛形形参* → [类型名称](./03_Types.md#type-name) **:** [协议合成类型](./03_Types.md#protocol-composition-type)
>
>
#### requirement-clause {#requirement-clause}
>
> *约束子句***where** [*约束列表*](#requirement-list)
> *约束子句***where** [约束列表](#requirement-list)
>
#### requirement-list {#requirement-list}
> *约束列表* → [*约束*](#requirement) | [*约束*](#requirement) **,** [*约束列表*](#requirement-list)
> *约束列表* → [约束](#requirement) | [约束](#requirement) **,** [约束列表](#requirement-list)
>
#### requirement {#requirement}
> *约束* → [*一致性约束*](#conformance-requirement) | [*同类型约束*](#same-type-requirement)
> *约束* → [一致性约束](#conformance-requirement) | [同类型约束](#same-type-requirement)
>
>
#### conformance-requirement {#conformance-requirement}
>
> *一致性约束* → [*类型标识符*](./03_Types.md#type-identifier) **:** [*类型标识符*](./03_Types.md#type-identifier)
> *一致性约束* → [类型标识符](./03_Types.md#type-identifier) **:** [类型标识符](./03_Types.md#type-identifier)
>
> *一致性约束* → [*类型标识符*](./03_Types.md#type-identifier) **:** [*协议合成类型*](./03_Types.md#protocol-composition-type)
> *一致性约束* → [类型标识符](./03_Types.md#type-identifier) **:** [协议合成类型](./03_Types.md#protocol-composition-type)
>
#### same-type-requirement {#same-type-requirement}
> *同类型约束* → [*类型标识符*](./03_Types.md#type-identifier) **==** [*类型*](./03_Types.md#type)
> *同类型约束* → [类型标识符](./03_Types.md#type-identifier) **==** [类型](./03_Types.md#type)
>
## 泛型实参子句 {#generic-argument}
@ -117,22 +128,21 @@ struct Dictionary<Key: Hashable, Value>: CollectionType, DictionaryLiteralConver
```swift
let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>
```
如 [泛型形参子句](#generic_parameter) 所述,不能用泛型实参子句来指定泛型函数或构造器的类型实参。
如 [泛型形参子句](#generic-parameter) 所述,不能用泛型实参子句来指定泛型函数或构造器的类型实参。
> 泛型实参子句语法
>
#### generic-argument-clause {#generic-argument-clause}
> *泛型实参子句***<** [*泛型实参列表*](#generic-argument-list) **>**
> *泛型实参子句***<** [泛型实参列表](#generic-argument-list) **>**
>
#### generic-argument-list {#generic-argument-list}
> *泛型实参列表* → [*泛型实参*](#generic-argument) | [*泛型实参*](#generic-argument) **,** [*泛型实参列表*](#generic-argument-list)
> *泛型实参列表* → [泛型实参](#generic-argument) | [泛型实参](#generic-argument) **,** [泛型实参列表](#generic-argument-list)
>
#### generic-argument {#generic-argument}
> *泛型实参* → [*类型*](./03_Types.md#type)
> *泛型实参* → [类型](./03_Types.md#type)
>

View File

@ -1,3 +1,3 @@
# Swift 教程
# Swift 语言参考
本章描述了 Swift 的语言参考。

View File

@ -0,0 +1,278 @@
# Swift 文档修订历史
### 2020-02-05
* 更新至 Swift 5.2。
* 在 [特殊名称方法](../03_language_reference/06_Declarations.md#methods-with-special-names) 章节中新增了有关让类、结构体和枚举的实例作为函数调用语法糖的内容。
* 更新 [下标选项](../02_language_guide/12_Subscripts.md#subscript-options) 章节,现在下标支持形参默认值。
* 更新 [自身类型](../03_language_reference/03_Types.md#self-type-h) 章节,现在 `Self` 可以在更多上下文中使用。
### 2019-09-10
* 更新至 Swift 5.1。
* 在 [不透明类型](../02_language_guide/23_Opaque_Types.md) 篇章中新增了有关函数返回值遵循指定协议,而不需要提供指定返回类型的内容。
* 在 [属性包装器](../02_language_guide/10_Properties.md#property-wrappers) 章节中新增了有关属性包装器的内容。
* 在 [冻结](../03_language_reference/07_Attributes.md#frozen) 章节中新增了有关因库演变而需要的枚举和结构体冻结。
* 新增 [隐式返回的函数](../02_language_guide/06_Functions.md#functions-with-an-implicit-return) 和 [简化 Getter 声明](../02_language_guide/10_Properties.md#shorthand-getter-declaration) 章节,其中包含函数省略 `return` 的内容。
* 在 [类型下标](../02_language_guide/12_Subscripts.md#type-subscripts) 章节中新增了有关在类型中使用下标的内容。
* 更新 [枚举 Case 模式匹配](../03_language_reference/08_Patterns.md#enumeration-case-pattern) 章节,现在枚举 case 模式匹配支持匹配可选值。
* 更新 [结构体的逐一成员构造器](../02_language_guide/14_Initialization.md#memberwise-initializers-for-structure-types) 章节,现在逐一成员构造器支持在属性有默认值时省略形参。
* 在 [动态查找成员](../03_language_reference/07_Attributes.md#dynamicmemberlookup) 章节中新增了有关在运行时用 key path 查找动态成员的内容。
* 在 [条件编译代码块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 中的目标环境里添加了 `macCatalyst`
* 更新 [自身类型](../03_language_reference/03_Types.md#self-type-h) 章节,现在 `Self` 可以指向当前类,结构体或者枚举声明时的类型。
### 2019-03-25
* 更新至 Swift 5。
* 新增 [拓展字符串分隔符](../02_language_guide/03_Strings_and_Characters.md#extended-string-delimiters) 章节。更新 [字符串字面量](../03_language_reference/02_Lexical_Structure.md#string-literal) 章节,拓展有关字符串分隔符的内容。
* 新增 [动态调用](../03_language_reference/07_Attributes.md#dynamiccallable) 章节,其中包含使用 `dynamicCallable` 属性动态调用实例作为函数的内容。
* 新增 [unknown](../03_language_reference/07_Attributes.md#unknown) 和 [未来枚举匹配](../03_language_reference/05_Statements.md#future-case) 章节,其中包含了使用 `unknown` 来处理未来枚举可能发生改变的情形。
* 在 [Key-Path 表达式](../03_language_reference/04_Expressions.md#key-path-expression) 章节新增了有关标示 key path (\\.self) 的内容。
* 在 [可选编译块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节新增了有关小于比较符 `<` 的内容。
### 2018-09-17
* 更新至 Swift 4.2。
* 在 [遍历枚举情形](../02_language_guide/08_Enumerations.md#iterating-over-enumeration-cases) 章节新增了有关访问所有枚举情形的内容。
* 在 [编译诊断](../03_language_reference/05_Statements.md#compile-time-diagnostic-statement) 章节新增了有关 `#error``#warning` 的内容。
* 在 [属性声明](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关 `inlinable``usableFromInline` 属性的内容。
* 在 [属性声明](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关 `requires-stored-property-inits``warn-unqualified-access` 属性的内容。
* 在 [可选编译块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节新增了有关如何根据 Swift 编译器版本对代码进行对应编译处理的内容。
* 在 [字面量语法](../03_language_reference/04_Expressions.md#literal-expression) 章节新增了有关 `#dsohandle` 的内容。
### 2018-03-29
* 更新至 Swift 4.1。
* 在 [等价运算符](../02_language_guide/27_Advanced_Operators.md#equivalence-operators) 章节新增了有关等价运算符的合成实现的内容。
* 在 [声明](../03_language_reference/06_Declarations.md) 篇章中 [声明拓展](../03_language_reference/06_Declarations.md#extension-declaration) 章节和 [协议](../02_language_guide/21_Protocols.md) 篇章中 [有条件地遵循协议](../02_language_guide/21_Protocols.md#Conditionally-Conforming-to-a-Protocol) 章节新增了有关协议有条件遵循的内容。
* 在 [关联类型约束中使用协议](../02_language_guide/22_Generics.md#using-a-protocol-in-its-associated-types-constraints) 章节中新增了有关递归协议约束的内容。
* 在 [条件编译块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节中新增了有关 `canImport()``targetEnvironment()` 平台条件的内容。
### 2017-12-04
* 更新至 Swift 4.0.3。
* 更新 [Key-Path 表达式](../03_language_reference/04_Expressions.md#key-path-expression) 章节,现在 key path 支持下标子路径。
### 2017-09-19
* 更新至 Swift 4.0。
* 在 [内存安全](../02_language_guide/25_Memory_Safety.md) 章节新增了有关内存互斥访问的内容。
* 新增 [带有泛型 Where 子句联类型](../02_language_guide/22_Generics.md#associated-types-with-a-generic-where-clause) 章节,现在可以使用泛型 `where` 子句约束关联类型。
* 在 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 篇章中 [字面量](../02_language_guide/03_Strings_and_Characters.md#string-literals) 章节以及 [词法结构](../03_language_reference/02_Lexical_Structure.md) 篇章的 [字符串字面量](../03_language_reference/02_Lexical_Structure.md#string-literal) 章节中新增了有关多行字符串字面量的内容。
* 更新 [声明属性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 中 `objc` 属性的讨论,现在该属性会在更少的位置被推断出来。
* 新增 [范型下标](../02_language_guide/22_Generics.md#generic-subscripts) 章节,现在下标也支持范型特性了。
* 更新 [协议](../02_language_guide/21_Protocols.md) 篇章中 [协议组合](../02_language_guide/21_Protocols.md#protocol-composition) 章节和 [类型](../03_language_reference/03_Types.md) 篇章中 [协议组合类型](../03_language_reference/03_Types.md#protocol-composition-type-h) 章节的讨论,现在协议组合类型支持进行父类约束了。
* 更新 [拓展声明](../03_language_reference/06_Declarations.md#extension-declaration) 中有关协议扩展的讨论,现在它们不支持 `final` 特性了。
* 在 [断言和前置条件](../02_language_guide/01_The_Basics.md#assertions-and-preconditions) 章节中新增了部分前置条件和致命错误的内容。
### 2017-03-27
* 更新至 Swift 3.1。
* 新增 [范型 Where 子句扩展](../02_language_guide/22_Generics.md#extensions-with-a-generic-where-clause) 章节,包含需要的扩展内容。
* 在 [For-In 循环](../02_language_guide/05_Control_Flow.md#for-in-loops) 章节中新增了区间迭代的例子。
* 在 [可失败构造器](../02_language_guide/14_Initialization.md#failable-initializers) 章节中新增了可失败数值转换的例子。
* 在 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关使用 Swift 语言版本的 `available` 特性的内容 。
* 更新 [函数类型](../03_language_reference/03_Types.md#function-type-h) 章节中的讨论,注意在写函数类型时不允许使用参数标签。
* 更新 [条件编译块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节中的 Swift 语言版本号的讨论,现在可以使用可选的补丁版本号。
* 更新 [函数类型](../03_language_reference/03_Types.md#function-type-h) 章节的讨论,现在 Swift 区分了采用多参数的函数和采用元组类型的单个参数的函数。
* 在 [表达式](../03_language_reference/04_Expressions.md) 篇章中删除了动态表达式的章节,现在 `type(of:)` 是 Swift 标准库函数。
### 2016-10-27
* 更新至 Swift 3.0.1。
* 更新 [自动引用计数](../02_language_guide/24_Automatic_Reference_Counting.md) 章节中有关 weak 和 unowned 引用的讨论。
* 在 [声明标识符](../03_language_reference/06_Declarations.md#declaration-modifiers) 章节中新增了有关新的标识符 `unowned``unowend(safe)``unowned(unsafe)` 的内容。
* 在 [Any 和 AnyObject 的类型转换](../02_language_guide/18_Type_Casting.md#type-casting-for-any-and-anyobject) 章节中新增了一处说明,有关使用类型 `Any` 作为可选值。
* 更新 [表达式](../03_language_reference/04_Expressions.md) 章节,把括号表达式和元组表达式的描述分开。
### 2016-09-13
* 更新至 Swift 3.0。
* 更新 [函数](../02_language_guide/06_Functions.md) 篇章和 [函数声明](../03_language_reference/06_Declarations.md#function-declaration) 章节中有关函数的讨论,所有函数参数默认都有函数标签。
* 更新 [高级操作符](../02_language_guide/27_Advanced_Operators.md) 篇章中有关操作符的讨论,现在你可以作为类型函数来实现,替代之前的全局函数实现方式。
* 在 [访问控制](../02_language_guide/26_Access_Control.md) 章节中新增有关对新的访问级别描述符 `open``fileprivate` 的内容。
* 更新 [函数声明](../03_language_reference/06_Declarations.md#function-declaration) 章节中有关 `inout` 的讨论,注意它现在出现在参数类型的前面,而不是在参数名称的前面。
* 更新 [逃逸闭包](../02_language_guide/07_Closures.md#escaping-closures) 和 [自动闭包](../02_language_guide/07_Closures.md#autoclosures) 章节还有 [属性](../03_language_reference/07_Attributes.md) 篇章中有关 `@noescape``@autoclosure` 的讨论,现在他们是类型属性,而不是定义属性。
* 在 [高级操作符](../02_language_guide/27_Advanced_Operators.md) 篇章中 [自定义中缀操作符的优先级](./02_language_guide/27_Advanced_Operators.md#precedence-and-associativity-for-custom-infix-operators) 章节和 [定义](../03_language_reference/06_Declarations.md) 篇章中 [优先级组声明](../03_language_reference/06_Declarations.md#precedence-group-declaration-modifiers) 章节中新增了有关操作符优先级组的内容。
* 更新一些讨论,使用 macOS 替换掉 OS X Error 替换掉 ErrorProtocol。更新一些协议名称比如使用 ExpressibleByStringLiteral 替换掉 StringLiteralConvertible。
* 更新 [泛型](../02_language_guide/22_Generics.md) 篇章中 [泛型 Where 语句](../02_language_guide/22_Generics.md#extensions-with-a-generic-where-clause) 章节和 [泛型形参和实参](../03_language_reference/09_Generic_Parameters_and_Arguments.md) 篇章的讨论,现在泛型的 where 语句写在一个声明的最后。
* 更新 [逃逸闭包](../02_language_guide/07_Closures.md#escaping-closures) 章节中的讨论,现在闭包默认为非逃逸的。
* 更新 [基础部分](../02_language_guide/01_The_Basics.md) 篇章中 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding) 章节和 [语句](../03_language_reference/05_Statements.md) 篇章中 [While 语句](../03_language_reference/05_Statements.md#while-statement) 章节中的讨论,现在 if`while``guard` 语句使用逗号分隔条件列表,不需要使用 `where` 语句。
* 在 [控制流](../02_language_guide/05_Control_Flow.md) 篇章中 [Switch](../02_language_guide/05_Control_Flow.md#switch) 章节和 [语句](../03_language_reference/05_Statements.md) 篇章中 [Switch 语句](../03_language_reference/05_Statements.md#switch-statement) 章节中新增了 switch cases 可以使用多模式的内容。
* 更新 [函数类型](../03_language_reference/03_Types.md#function-type-h) 章节有关现在函数参数标签不包含在函数类型中的讨论。
* 更新 [协议](../02_language_guide/21_Protocols.md) 篇章中 [协议组合](../02_language_guide/21_Protocols.md#protocol-composition) 章节和 [类型](../03_language_reference/03_Types.md) 篇章中 [协议组合类型](../03_language_reference/03_Types.md#protocol-composition-type-h) 章节中有关使用新的 Protocol1 & Protocol2 语法的内容。
* 更新动态类型表达式章节中使用新的 `type(of:)` 表达式的讨论。
* 更新 [行控制表达式](../03_language_reference/05_Statements.md#line-control-statement) 章节中使用 `#sourceLocation(file:line:)` 表达式的讨论。
* 更新 [永不返回函数](../03_language_reference/06_Declarations.md#functions-that-never-return) 章节中使用 新的 `Never` 类型的讨论。
* 在 [字面量表达式](../03_language_reference/04_Expressions.md#literal-expression) 章节中新增了有关 `playground` 字面量的内容。
* 更新 [In-Out 参数](../03_language_reference/06_Declarations.md#in-out-parameters) 章节,标明只有非逃逸闭包能捕获 `in-out` 参数。
* 更新 [默认参数值](../02_language_guide/06_Functions.md#default-parameter-values) 章节,现在默认参数不能在调用时候重新排序。
* 更新 [属性](../03_language_reference/07_Attributes.md) 篇章中有关属性参数使用分号的说明。
* 在 [重新抛出函数和方法](../03_language_reference/06_Declarations.md#rethrowing-functions-and-methods) 章节中新增了有关在 catch 代码块中抛出错误的重新抛出函数的内容。
* 在 [Selector 表达式](../03_language_reference/04_Expressions.md#selector-expression7) 章节中新增了中有关访问 Objective-C 中 Selector 的 getter 和 setter 的内容。
* 在 [类型别名声明](../03_language_reference/06_Declarations.md#type-alias-declaration) 章节中中新增了有关泛型类型别名和在协议内使用类型别名的内容。
* 更新 [函数类型](../03_language_reference/03_Types.md#function-type-h) 章节中有关函数类型的讨论,标明函数类型作为参数类型必须使用括号包裹。
* 更新 [属性](../03_language_reference/07_Attributes.md) 篇章,标明 `@IBAction``@IBOutlet``@NSManaged` 隐式含有 `@objc` 属性。
* 在 [声明属性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关 `@GKInspectable` 的内容。
* 更新 [可选协议要求](../02_language_guide/21_Protocols.md#optional-protocol-requirements) 章节中有关只能在与 `Objective-C` 交互的代码中才能使用可选协议要求的内容。
* 删除 [函数声明](../03_language_reference/06_Declarations.md#function-declaration) 章节中有关显式使用 `let` 关键字作为函数参数的内容。
* 删除 [语句](../03_language_reference/05_Statements.md) 章节中有关 `Boolean` 协议的内容, 现在这个协议已经被 Swift 标准库删除。
* 更正 [声明属性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中有关 `@NSApplicationMain` 协议的内容。
### 2016-03-21
* 更新至 Swift 2.2。
* 在 [编译配置语句](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节新增了中有关如何根据 Swift 版本进行条件编译。
* 在 [显示成员表达式](../03_language_reference/04_Expressions.md#explicit-member-expression) 章节中新增了有关如何区分只有参数名不同的方法和构造器的内容。
* 在 [选择器表达式](../03_language_reference/04_Expressions.md#selector-expression7) 章节中新增了了针对 Objective-C 选择器的 `#selector` 语法。
* 更新 [关联类型](../02_language_guide/22_Generics.md#associated-types) 和 [协议关联类型声明](../03_language_reference/06_Declarations.md#protocol-associated-type-declaration) 章节中有关使用 `associatedtype` 关键词修饰关联类型的讨论。
* 更新 [可失败构造器](../02_language_guide/14_Initialization.md#failable-initializers) 章节中有关当构造器在实例完全初始化之前返回 `nil` 的相关内容。
* 在 [比较运算符](../02_language_guide/02_Basic_Operators.md#comparison-operators) 章节中新增了比较元组的内容。
* 在 [关键字和标点符号](../03_language_reference/02_Lexical_Structure.md#keywords-and-punctuation) 章节中新增了使用关键字作为外部参数名的内容。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中有关 `@objc` 特性的讨论,并指出枚举和枚举用例。
* 更新 [操作符](../03_language_reference/02_Lexical_Structure.md#operator) 章节中对于自定义运算符的包含了 `.` 的讨论。
* 在 [重新抛出错误的函数和方法](../03_language_reference/06_Declarations.md#rethrowing-functions-and-methods) 章节中新增了一处说明,重新抛出错误函数不能直接抛出错误。
* 在 [属性观察器](../02_language_guide/10_Properties.md#property-observers) 章节中新增了一处说明,当作为 in-out 参数传递属性时,属性观察器的调用行为。
* 在 [Swift 初见](../01_welcome_to_swift/03_a_swift_tour.md) 篇章中新增了错误处理的章节。
* 更新 [弱引用](../02_language_guide/24_Automatic_Reference_Counting.md#weak-references) 章节中的图片用以更清楚的展示重新分配过程。
* 删除 C 语言风格的 `for` 循环,`++` 前缀和后缀运算符,以及 `--` 前缀和后缀运算符。
* 删除对变量函数参数和柯里化函数的特殊语法的讨论。
### 2015-10-20
* 更新至 Swift 2.1。
* 更新 [字符串插值](../02_language_guide/03_Strings_and_Characters.md#string-interpolation) 和 [字符串字面量](../03_language_reference/02_Lexical_Structure.md#string-literal) 章节,现在字符串插值可包含字符串字面量。
* 在 [逃逸闭包](../02_language_guide/07_Closures.md#escaping-closures) 章节中新增了有关 `@noescape` 属性的相关内容。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 和 [编译配置语句](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节中与 tvOS 相关的内容。
* 在 [In-Out 参数](../03_language_reference/06_Declarations.md#in-out-parameters) 章节中新增了与 in-out 参数行为相关的内容。
* 在 [捕获列表](../03_language_reference/04_Expressions.md#capture-lists) 章节新增了有关指定闭包捕获列表被捕获时捕获值的相关内容。
* 更新 [可选链式调用访问属性](../02_language_guide/16_Optional_Chaining.md#accessing-properties-through-optional-chaining) 章节,阐明了如何通过可选链式调用进行赋值。
* 改进 [自动闭包](../02_language_guide/07_Closures.md#autoclosures) 章节中对自闭包的讨论。
* 在 [Swift 初见](../01_welcome_to_swift/03_a_swift_tour.md) 篇章中新增了一个使用 `??` 操作符的例子。
### 2015-09-16
* 更新至 Swift 2.0。
* 在 [错误处理](../02_language_guide/17_Error_Handling.md) 篇章中新增了有关错误处理的相关内容,包括 [Do 语句](../03_language_reference/05_Statements.md#do-statement)、 [Throw 语句](../03_language_reference/05_Statements.md#throw-statement)、 [Defer 语句](../03_language_reference/05_Statements.md##defer-statements) 以及 [try 运算符](../03_language_reference/04_Expressions.md#try-operator)。
* 更新 [错误表示和抛出](../02_language_guide/17_Error_Handling.md#representing-and-throwing-errors) 章节,现在所有类型都可以遵循 `ErrorType` 协议了。
* 在 [将错误装换成可选值](../02_language_guide/17_Error_Handling.md#converting-errors-to-optional-values) 篇章增加了 `try?` 关键字相关内容。
* 在 [枚举](../02_language_guide/08_Enumerations.md) 篇章的 [递归枚举](../02_language_guide/08_Enumerations.md#recursive-enumerations) 章节以及以及 [声明](../03_language_reference/06_Declarations.md) 篇章的 [任意类型用例的枚举](../03_language_reference/06_Declarations.md#enumerations-with-cases-of-any-type) 章节中新增了递归枚举相关内容。
* 在 [控制流](../02_language_guide/05_Control_Flow.md) 篇章的 [API 可用性检查](../02_language_guide/05_Control_Flow.md#checking-api-availability) 章节和 [语句](../03_language_reference/05_Statements.md) 篇章的 [可用性条件](../03_language_reference/05_Statements.md#availability-condition) 章节中新增了有关 API 可用性检查相关的内容。
* 在 [控制流](../02_language_guide/05_Control_Flow.md) 篇章的 [尽早退出](../02_language_guide/05_Control_Flow.md#early-exit) 章节和 [语句](../03_language_reference/05_Statements.md) 篇章的 [Guard 语句](../03_language_reference/05_Statements.md#guard-statement) 章节新增了与 `guard` 语句相关的内容。
* 在 [协议](../02_language_guide/21_Protocols.md) 篇章中 [协议扩展](../02_language_guide/21_Protocols.md#protocol-extensions) 章节中新增了有关协议扩展的内容。
* 在 [访问控制](../02_language_guide/26_Access_Control.md) 篇章的 [单元测试 target 的访问级别](../02_language_guide/26_Access_Control.md#access-levels-for-unit-test-targets) 章节中新增了有关单元测试访问控制相关的内容。
* 在 [模式](../03_language_reference/08_Patterns.md) 篇章的 [可选模式](../03_language_reference/08_Patterns.md#optional-pattern) 章节中新增了可选模式相关内容。
* 更新 [Repeat-While](../02_language_guide/05_Control_Flow.md#repeat-while) 章节中有关 `repeat-while` 循环相关的内容。
* 更新 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 章节,现在 `String` 类型在 Swift 标准库中不再遵循 `CollectionType` 协议。
* 在 [常量与变量打印](../02_language_guide/01_The_Basics.md#printing) 章节中新增了新 Swift 标准库中有关 `print(-:separator:terminator) ` 相关内容。
* 在 [枚举](../02_language_guide/08_Enumerations.md) 篇章的 [原始值的隐式赋值](../02_language_guide/08_Enumerations.md#implicitly-assigned-raw-values) 章节和 [声明](../03_language_reference/06_Declarations.md) 篇章的 [包含原始值类型的枚举](../03_language_reference/06_Declarations.md#enumerations-with-cases-of-a-raw-value-type) 章节中新增了有关包含 `String` 原始值的枚举用例的行为相关内容。
* 在 [自动闭包](../02_language_guide/07_Closures.md#autoclosures) 章节中新增了有关 `@autoclosure` 特性的相关内容,包括它的 `@autoclosure(escaping)` 形式。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中有关 `@avaliable``warn-unused-result` 特性的相关内容。
* 更新 [类型特性](../03_language_reference/07_Attributes.md#type-attributes) 章节中有关 `@convention` 特性的相关内容。
* 在 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding) 章节中新增了有关使用 `where` 子句进行多可选绑定的相关内容。
* 在 [字符串字面量](../03_language_reference/02_Lexical_Structure.md#string-literal) 章节中新增了有关在编译时使用 `+` 运算符拼接字符串字面量的相关内容。
* 在 [元类型](../03_language_reference/03_Types.md#metatype-type-h) 章节中新增了有关元类型值的比较和使用它们通过构造器表达式构造实例相关内容。
* 在 [断言调试](../02_language_guide/01_The_Basics.md#debugging-with-assertions) 章节中新增了一处说明,有关用户定义断言何时会失效。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中对 `@NSManaged` 特性的讨论,现在这个特性可以被应用到一个确定实例方法。
* 更新 [可变参数](../02_language_guide/06_Functions.md#variadic-parameters) 章节,现在可变参数可以声明在函数参数列表的任意位置中。
* 在 [重写可失败构造器](../02_language_guide/14_Initialization.md#overriding-a-failable-initializer) 章节中新增了有关非可失败构造器相当于一个可失败构造器通过父类构造器的结果进行强制拆包的相关内容。
* 在 [任意类型用例的枚举](../03_language_reference/06_Declarations.md#enumerations-with-cases-of-any-type) 章节中新增了有关枚举用例作为函数的内容。
* 在 [构造器表达式](../03_language_reference/04_Expressions.md#initializer-expression) 章节中新增了有关显式引用一个构造器相关内容。
* 在 [编译控制语句](../03_language_reference/05_Statements.md#compiler-control-statements) 章节中新增了有关编译内容以及行控制语句相关内容。
* 在 [元类型](../03_language_reference/03_Types.md#metatype-type-h) 章节新增了一处说明,有关如何从元类型值中构造类实例相关内容。
* 在 [弱引用](../02_language_guide/24_Automatic_Reference_Counting.md#weak-references) 章节新增了一处说明,有关弱引用作为缓存所存在的不足。
* 更新 [类型特性](../02_language_guide/10_Properties.md#type-properties) 章节,提到了存储型特性其实是懒加载。
* 更新 [捕获类型](../02_language_guide/07_Closures.md#capturing-values) 章节,阐明了变量和常量在闭包中如何被捕获。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节,用以描述何时在类中使用 `@objc` 关键字。
* 在 [错误处理](../02_language_guide/17_Error_Handling.md#handling-errors) 章节中新增了一处说明,有关执行 `throw` 语句的性能。在 [Do 语句](../03_language_reference/05_Statements.md#do-statement) 章节的 do 语句部分也新增了类似内容。
* 更新 [类型特性](../02_language_guide/10_Properties.md#type-properties) 章节中有关类、结构体和枚举的存储型和计算型特性相关的内容。
* 更新 [Break 语句](../03_language_reference/05_Statements.md#break-statement) 章节中有关带标签的 break 语句相关内容。
* 在 [属性观察器](../02_language_guide/10_Properties.md#property-observers) 章节更新了一处说明,用来明确 `willSet``didSet` 观察器的行为。
* 在 [访问级别](../02_language_guide/26_Access_Control.md#access-levels) 章节新增了有关 `private` 作用域的相关内容说明。
* 在 [弱引用](../02_language_guide/24_Automatic_Reference_Counting.md#weak-references) 章节新增了有关弱应用在垃圾回收系统和 ARC 之间的区别的说明。
* 更新 [字符串字面量中特殊字符](../02_language_guide/03_Strings_and_Characters.md#special-characters-in-string-literals) 章节,对 Unicode 标量更精确定义。
### 2015-04-08
* 更新至 Swift 1.2。
* Swift 现在自身提供了一个 `Set` 集合类型,更多内容,请看 [Sets](../02_language_guide/04_Collection_Types.md#sets) 。
* `@autoclosure` 现在是一个参数声明的属性,而不是参数类型的属性。这里还有一个新的参数声明属性 `@noescape`。更多内容,请看 [属性声明](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 。
* 对于类型属性和方法现在可以使用 `static` 关键字作为声明描述符,更多内容,请看 [类型变量属性](../03_language_reference/06_Declarations.md#type-variable-properties)。
* Swift 现在包含一个 `as?``as!` 的向下可失败类型转换运算符。更多内容,请看 [协议遵循性检查](../02_language_guide/21_Protocols.md#checking-for-protocol-conformance)。
* 新增 [字符串索引](../02_language_guide/03_Strings_and_Characters.md#string-indices) 的新指导章节。
* 在 [溢出运算符](../02_language_guide/27_Advanced_Operators.md#overflow-operators) 一节中删除了溢出除运算符(`&/`)和求余溢出运算符(`&%`)。
* 更新常量和常量属性在声明和构造时的规则,更多内容,请看 [常量声明](../03_language_reference/06_Declarations.md#constant-declaration) 。
* 更新字符串字面量中 Unicode 标量集的定义,请看 [字符串字面量中的特殊字符](../02_language_guide/03_Strings_and_Characters.md#special-characters-in-string-literals) 。
* 更新 [区间运算符](../02_language_guide/02_Basic_Operators.md#range-operators) 章节,注意当半开区间运算符含有相同的起止索引时,其区间为空。
* 更新 [闭包引用类型](../02_language_guide/07_Closures.md#closures-are-reference-types) 章节,对于变量的捕获规则进行了阐明。
* 更新 [值溢出](../02_language_guide/27_Advanced_Operators.md#value-overflow) 章节,对有符号整数和无符号整数的溢出行为进行了阐明。
* 更新 [协议声明](../03_language_reference/06_Declarations.md#protocol-declaration) 章节,对协议声明时的作用域和成员等内容进行了阐明。
* 更新 [捕获列表](../02_language_guide/24_Automatic_Reference_Counting.md#defining-a-capture-list) 章节,对于闭包捕获列表中的弱引用和无主引用的使用语法进行了阐明。
* 更新 [运算符](../03_language_reference/02_Lexical_Structure.md#operator) 章节明确指明一些例子来说明自定义运算符所支持的特性如数学运算符各种符号Unicode 符号块等。
* 在函数作用域中的常量声明时可以不被初始化,它必须在第一次使用前被赋值。更多的内容,请看 [常量声明](../03_language_reference/06_Declarations.md#constant-declaration)。
* 在构造器中,常量属性有且仅能被赋值一次。更多内容,请看 [在构造过程中给常量属性赋值](../02_language_guide/14_Initialization.md#assigning-constant-properties-during-initialization)。
* 多个可选绑定现在可以在`if`语句后面以逗号分隔的赋值列表的方式出现,更多内容,请看 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding)。
* 一个 [可选链表达式](../03_language_reference/04_Expressions.md#optional-chaining-expression) 必须出现在后缀表达式中。
* 协议类型转换不再局限于 `@obj` 修饰的协议了。
* 在运行时可能会失败的类型转换可以使用 `as?``as!` 运算符,而确保不会失败的类型转换现在使用 `as` 运算符。更多内容,请看 [类型转换运算符](../03_language_reference/04_Expressions.md#type-casting-operator)。
### 2014-10-16
* 更新至 Swift 1.1。
* 新增 [失败构造器](../02_language_guide/14_Initialization.md#failable-initializers) 的完整指引。
* 在协议中新增了 [失败构造器要求](../02_language_guide/21_Protocols.md#failable-initializer-requirements) 的描述。
* 常量和变量的 `Any` 类型现可以包含函数实例。更新了有关 `Any` 相关的示例来展示如何在 `switch` 语句中如何检查并转换到一个函数类型。
* 带有原始值的枚举类型增加了一个 `rawValue` 属性替代 `toRaw()` 方法,同时使用了一个以 `rawValue` 为参数的失败构造器来替代 `fromRaw()` 方法。更多的内容,请看 [原始值](../02_language_guide/08_Enumerations.md#raw-values) 和 [带原始值的枚举类型](../03_language_reference/06_Declarations.md#enumerations-with-cases-of-a-raw-value-type)。
* 新增 [Failable Initializer](../03_language_reference/06_Declarations.md#failable-initializers) 的参考章节,它可以触发初始化失败。
* 自定义运算符现在可以包含 `?` 字符,更新了 [运算符](../03_language_reference/02_Lexical_Structure.md#operator) 涉及改进后的规则的部分,并且在 [自定义运算符](../02_language_guide/27-Advanced-Operators.md#custom-operators) 章节中删除了重复的运算符有效字符集合。
### 2014-08-18
* 描述 Swift 1.0 的新文档。Swift 是苹果公司发布的全新编程语言,用于 iOS 和 OS X 应用开发。
* 在协议中新增了 [对构造器的规定](../02_language_guide/21_Protocols.md#initializer-requirements) 章节。
* 新增 [类专属协议](../02_language_guide/21_Protocols.md#class-only-protocol) 章节。
* [断言](../02_language_guide/01_The_Basics.md#assertions-and-preconditions) 现在可以使用字符串内插语法,并删除了文档中有冲突的注释。
* 更新 [连接字符串和字符](../02_language_guide/03_Strings_and_Characters.md#concatenating-strings-and-characters) 章节来说明字符串和字符不能再用 `+` 号运算符或者复合加法运算符 `+=` 相互连接,这两种运算符现在只能用于字符串之间相连。请使用 `String` 类型的 `append` 方法在一个字符串的尾部增加单个字符。
* 在 [属性声明](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节增加了有关 `availability` 特性的一些内容。
* [可选类型](../02_language_guide/01_The_Basics.md#optionals) 若有值时,不再隐式的转换为 `true`,同样,若无值时,也不再隐式的转换为 `false`,这是为了避免在判别 optional `Bool` 的值时产生困惑。 替代的方案是,用`==``!=` 运算符显式地去判断 Optinal 是否是 `nil`,以确认其是否包含值。
* Swift 新增了一个 [Nil 合并运算符](../02_language_guide/02_Basic_Operators.md#nil-coalescing-operator) (`a ?? b`) , 该表达式中,如果 Optional `a` 的值存在,则取得它并返回,若 Optional `a``nil`,则返回默认值 `b`
* 更新和扩展 [字符串的比较](../02_language_guide/03_Strings_and_Characters.md#comparing-strings) ,用以反映和展示'字符串和字符的比较',以及'前缀prefix/后缀postfix比较'都开始基于扩展字符集extended grapheme clusters规范的等价比较。
* 现在,你可以通过下标赋值或者 [可选调用链](../02_language_guide/16_Optional_Chaining.md) 中的可变方法和操作符来给属性设值。相应地更新了有关 [通过可选链接访问属性](../02_language_guide/16_Optional_Chaining.md#accessing-properties-through-optional-chaining) 的内容,并扩展了 [通过可选链接调用方法](../02_language_guide/16_Optional_Chaining.md#calling-methods-through-optional-chaining) 时检查方法调用成功的示例,以显示如何检查属性设置是否成功。
* 在可选链中新增了 [访问可选类型的下标脚注](../02_language_guide/16_Optional_Chaining.md#accessing-subscripts-through-optional-chaining) 章节。
* 更新 [访问和修改数组](../02_language_guide/04_Collection_Types.md#accessing-and-modifying-a-dictionary) 章节以标示,从该版本起,不能再通过 `+=` 运算符给一个数组新增一个新的项。对应的替代方案是,使 `append` 方法,或者通过 `+=` 运算符来新增一个只有一个项的数组。
* 新增一处说明,在 [范围运算符](../02_language_guide/02_Basic_Operators.md#range-operators) 中,比如, `a..b``a..<b` ,起始值 `a` 不能大于结束值 `b`
* 重写 [继承](../02_language_guide/13_Inheritance.md) 篇章删除了本章中有关构造器重写的介绍性报道转而将更多的注意力放到新增的部分——子类的新功能以及如何通过重写overrides修改已有的功能。另外 [重写属性的 Getters 和 Setters](../02_language_guide/13_Inheritance.md#overriding-property-etters-and-setters) 中的例子已经被替换为展示如何重写一个 `description` 属性。 (而有关如何在子类的构造器中修改继承属性的默认值的例子,已经被移到 [构造过程](../02_language_guide/14_Initialization.md) 篇章。)
* 更新 [构造器的继承与重写](../02_language_guide/14_Initialization.md#initializer-inheritance-and-overriding) 章节以标示: 重写一个特定的构造器必须使用 `override` 修饰符。
* 更新 [Required 构造器](../02_language_guide/14_Initialization.md#required-initializers) 章节以标示:`required` 修饰符现在需要出现在所有子类的 required 构造器的声明中,而 required 构造器的实现,现在可以仅从父类自动继承。
* 中置Infix的 [运算符函数](../02_language_guide/27_Advanced_Operators.md#operator-functions) 不再需要 `@infix` 属性。
* [前置和后置运算符](../02_language_guide/27_Advanced_Operators.md#prefix-and-postfix-operators) 的 `@prefix``@postfix` 属性,已变更为 `prefix``postfix` 声明修饰符。
* 新增一处说明,在 Prefix 和 postfix 运算符被作用于同一个操作数时 [前置和后置运算符](../02_language_guide/27_Advanced_Operators.md#prefix-and-postfix-operators) 的执行顺序。
* [组合赋值运算符](../02_language_guide/27_Advanced_Operators.md#compound-assignment-operators) 的运算符函数不再使用 `@assignment` 属性来定义函数。
* 在定义 [自定义操作符](../02_language_guide/27_Advanced_Operators.md#custom-operators) 时,`修饰符Modifiers的出现顺序发生变化`。比如现在,你该编写 `prefix operator` 而不是 `operator prefix`
* 在 [声明修饰符](../03_language_reference/06_Declarations.md#declaration-modifiers) 章节新增了有关 `dynamic` 声明修饰符的内容。
* 新增有关 [字面量](../03_language_reference/02_Lexical_Structure.md#literal) 类型推导内容的内容。
* 新增更多有关柯里化函数的内容。
* 新增 [权限控制](../02_language_guide/26_Access_Control.md) 篇章。
* 更新 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 章节,在 Swift 中现在 `Character` 类型代表的是扩展字符集extended grapheme cluster中的一个 Unicode为此新增了 [Extended Grapheme Clusters](../02_language_guide/03_Strings_and_Characters.md#extended-grapheme-clusters) 章节。同时,[Unicode 标量](../02_language_guide/03-Strings-And-Characters.md#unicode-scalars-representation) 和 [字符串比较](../02_language_guide/03-Strings-And-Characters.md#comparing-strings) 章节新增了更多内容。
* 更新 [字符串字面量](../02_language_guide/03_Strings_and_Characters.md#string-literals) 章节在一个字符串中Unicode 标量Unicode scalars`\u{n}`的形式来表示,`n` 是一个最大可以有8位的16进制数。
* `NSString` `length` 属性已被映射到 Swift 的内建 `String`类型。(注意,这两属性的类型是`utf16Count`,而非 `utf16count`)。
* Swift 的内建 `String` 类型不再拥有 `uppercaseString``lowercaseString` 属性。在 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 章节中删除了对应部分,并更新了各种对应的代码用例。
* 新增 [没有外部名的构造器参数](../02_language_guide/14_Initialization.md#initializer-parameters-without-external-names) 章节。
* 新增 [Required 构造器](../02_language_guide/14_Initialization.md#required-initializers) 章节。
* 新增 [可选元组返回类型](../02_language_guide/06_Functions.md#optional-tuple-return-types) 章节。
* 更新 [类型注解](../02_language_guide/01_The_Basics.md#type-annotations) 章节,多个相关变量可以用"类型注解”在同一行中声明为同一类型。
* `@optional`, `@lazy`, `@final`, `@required` 等关键字被更新为 `optional`, `lazy`, `final`, `required` 参见 [声明修饰符](../03_language_reference/06_Declarations.md#declaration-modifiers)。
* 更新了整本书中有关 `..<` 的引用,从半闭区间改为了 [半开区间](../02_language_guide/02_Basic_Operators.md#half-open-range-operator)。
* 更新 [读取和修改字典](../02_language_guide/04_Collection_Types.md#accessing-and-modifying-a-dictionary) 章节, `Dictionary` 现在增加了一个 Boolean 型的属性:`isEmpty`
* 解释了哪些字符(集)可被用来定义 [自定义操作符](../02_language_guide/27_Advanced_Operators.md#custom-operators)。
* `nil` 和布尔运算中的 `true``false` 现在被定义为 [字面量](../03_language_reference/02_Lexical_Structure.md#literal)。
* Swift 中的数组 `Array` 类型从现在起具备了完整的值语义。具体内容被更新到 [集合的可变性](../02_language_guide/04_Collection_Types.md#mutability-of-collections) 和 [数组](../02_language_guide/04_Collection_Types.md#arrays) 两小节,以反映这个新的变化。 此外,还解释了如何给 Strings, Arrays 和 Dictionaries 进行赋值和拷贝。
* [数组类型速记语法](../02_language_guide/04_Collection_Types.md#array-type-shorthand-syntax) 从 `SomeType []` 更新为 ` [SomeType]`
* 新增 [字典类型的速记语法](../02_language_guide/04_Collection_Types.md#dictionary-type-shorthand-syntax) 章节,现在书写格式为: ` [KeyType: ValueType]`
* 新增 [字典键类型的哈希值](../02_language_guide/04_Collection_Types.md#hash-values-for-set-types) 章节。
* [闭包表达式](../02_language_guide/07_Closures.md#closure-expressions) 示例中使用新的全局函数 `sorted` 取代原先的全局函数 `sort` 去展示如何返回一个全新的数组。
* 更新 [结构体逐一成员构造器](../02_language_guide/14_Initialization.md#memberwise-initializers-for-structure-types) 章节,即使结构体的成员 `没有默认值`,逐一成员构造器也可以自动获得。
* [半开区间运算符](../02_language_guide/02_Basic_Operators.md#half-open-range-operator) 中`..` 更新为 `..<`
* 新增 [泛型拓展](../02_language_guide/22_Generics.md#extending-a-generic-type) 的示例。

View File

@ -0,0 +1,3 @@
# Swift 文档修订历史
本章描述了 Swift 文档修订历史。

View File

@ -1,22 +1,278 @@
> 2016.9.23: 已经更新到 Swift 3.0。
# 3.0 更新说明
Swift 3.0 是自 Swift 开源以来第一个大的版本更新。从语言角度不兼容之前的 Swift 2.2 和 Swift 2.3 版本。Swift 3.0 的更新说明,大家可以查看[官方 blog 的说明](https://swift.org/blog/swift-3-0-released/),也可以关注 [SwiftGG](http://swift.gg) 最新的文章。学习官方文档,是掌握语言特性点的最佳途径,感谢翻译的小伙伴们为 Swift 社区所做贡献!
# Swift 文档修订历史
# 3.0 译者记录
相关[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)
### 2020-02-05
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)
* 更新至 Swift 5.2。
* 在 [特殊名称方法](../03_language_reference/06_Declarations.md#methods-with-special-names) 章节中新增了有关让类、结构体和枚举的实例作为函数调用语法糖的内容。
* 更新 [下标选项](../02_language_guide/12_Subscripts.md#subscript-options) 章节,现在下标支持形参默认值。
* 更新 [自身类型](../03_language_reference/03_Types.md#self-type-h) 章节,现在 `Self` 可以在更多上下文中使用。
### 2019-09-10
* 更新至 Swift 5.1。
* 在 [不透明类型](../02_language_guide/23_Opaque_Types.md) 篇章中新增了有关函数返回值遵循指定协议,而不需要提供指定返回类型的内容。
* 在 [属性包装器](../02_language_guide/10_Properties.md#property-wrappers) 章节中新增了有关属性包装器的内容。
* 在 [冻结](../03_language_reference/07_Attributes.md#frozen) 章节中新增了有关因库演变而需要的枚举和结构体冻结。
* 新增 [隐式返回的函数](../02_language_guide/06_Functions.md#functions-with-an-implicit-return) 和 [简化 Getter 声明](../02_language_guide/10_Properties.md#shorthand-getter-declaration) 章节,其中包含函数省略 `return` 的内容。
* 在 [类型下标](../02_language_guide/12_Subscripts.md#type-subscripts) 章节中新增了有关在类型中使用下标的内容。
* 更新 [枚举 Case 模式匹配](../03_language_reference/08_Patterns.md#enumeration-case-pattern) 章节,现在枚举 case 模式匹配支持匹配可选值。
* 更新 [结构体的逐一成员构造器](../02_language_guide/14_Initialization.md#memberwise-initializers-for-structure-types) 章节,现在逐一成员构造器支持在属性有默认值时省略形参。
* 在 [动态查找成员](../03_language_reference/07_Attributes.md#dynamicmemberlookup) 章节中新增了有关在运行时用 key path 查找动态成员的内容。
* 在 [条件编译代码块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 中的目标环境里添加了 `macCatalyst`
* 更新 [自身类型](../03_language_reference/03_Types.md#self-type-h) 章节,现在 `Self` 可以指向当前类,结构体或者枚举声明时的类型。
### 2019-03-25
* 更新至 Swift 5。
* 新增 [拓展字符串分隔符](../02_language_guide/03_Strings_and_Characters.md#extended-string-delimiters) 章节。更新 [字符串字面量](../03_language_reference/02_Lexical_Structure.md#string-literal) 章节,拓展有关字符串分隔符的内容。
* 新增 [动态调用](../03_language_reference/07_Attributes.md#dynamiccallable) 章节,其中包含使用 `dynamicCallable` 属性动态调用实例作为函数的内容。
* 新增 [unknown](../03_language_reference/07_Attributes.md#unknown) 和 [未来枚举匹配](../03_language_reference/05_Statements.md#future-case) 章节,其中包含了使用 `unknown` 来处理未来枚举可能发生改变的情形。
* 在 [Key-Path 表达式](../03_language_reference/04_Expressions.md#key-path-expression) 章节新增了有关标示 key path (\\.self) 的内容。
* 在 [可选编译块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节新增了有关小于比较符 `<` 的内容。
### 2018-09-17
* 更新至 Swift 4.2。
* 在 [遍历枚举情形](../02_language_guide/08_Enumerations.md#iterating-over-enumeration-cases) 章节新增了有关访问所有枚举情形的内容。
* 在 [编译诊断](../03_language_reference/05_Statements.md#compile-time-diagnostic-statement) 章节新增了有关 `#error``#warning` 的内容。
* 在 [属性声明](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关 `inlinable``usableFromInline` 属性的内容。
* 在 [属性声明](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关 `requires-stored-property-inits``warn-unqualified-access` 属性的内容。
* 在 [可选编译块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节新增了有关如何根据 Swift 编译器版本对代码进行对应编译处理的内容。
* 在 [字面量语法](../03_language_reference/04_Expressions.md#literal-expression) 章节新增了有关 `#dsohandle` 的内容。
### 2018-03-29
* 更新至 Swift 4.1。
* 在 [等价运算符](../02_language_guide/27_Advanced_Operators.md#equivalence-operators) 章节新增了有关等价运算符的合成实现的内容。
* 在 [声明](../03_language_reference/06_Declarations.md) 篇章中 [声明拓展](../03_language_reference/06_Declarations.md#extension-declaration) 章节和 [协议](../02_language_guide/21_Protocols.md) 篇章中 [有条件地遵循协议](../02_language_guide/21_Protocols.md#Conditionally-Conforming-to-a-Protocol) 章节新增了有关协议有条件遵循的内容。
* 在 [关联类型约束中使用协议](../02_language_guide/22_Generics.md#using-a-protocol-in-its-associated-types-constraints) 章节中新增了有关递归协议约束的内容。
* 在 [条件编译块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节中新增了有关 `canImport()``targetEnvironment()` 平台条件的内容。
### 2017-12-04
* 更新至 Swift 4.0.3。
* 更新 [Key-Path 表达式](../03_language_reference/04_Expressions.md#key-path-expression) 章节,现在 key path 支持下标子路径。
### 2017-09-19
* 更新至 Swift 4.0。
* 在 [内存安全](../02_language_guide/25_Memory_Safety.md) 章节新增了有关内存互斥访问的内容。
* 新增 [带有泛型 Where 子句联类型](../02_language_guide/22_Generics.md#associated-types-with-a-generic-where-clause) 章节,现在可以使用泛型 `where` 子句约束关联类型。
* 在 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 篇章中 [字面量](../02_language_guide/03_Strings_and_Characters.md#string-literals) 章节以及 [词法结构](../03_language_reference/02_Lexical_Structure.md) 篇章的 [字符串字面量](../03_language_reference/02_Lexical_Structure.md#string-literal) 章节中新增了有关多行字符串字面量的内容。
* 更新 [声明属性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 中 `objc` 属性的讨论,现在该属性会在更少的位置被推断出来。
* 新增 [范型下标](../02_language_guide/22_Generics.md#generic-subscripts) 章节,现在下标也支持范型特性了。
* 更新 [协议](../02_language_guide/21_Protocols.md) 篇章中 [协议组合](../02_language_guide/21_Protocols.md#protocol-composition) 章节和 [类型](../03_language_reference/03_Types.md) 篇章中 [协议组合类型](../03_language_reference/03_Types.md#protocol-composition-type-h) 章节的讨论,现在协议组合类型支持进行父类约束了。
* 更新 [拓展声明](../03_language_reference/06_Declarations.md#extension-declaration) 中有关协议扩展的讨论,现在它们不支持 `final` 特性了。
* 在 [断言和前置条件](../02_language_guide/01_The_Basics.md#assertions-and-preconditions) 章节中新增了部分前置条件和致命错误的内容。
### 2017-03-27
* 更新至 Swift 3.1。
* 新增 [范型 Where 子句扩展](../02_language_guide/22_Generics.md#extensions-with-a-generic-where-clause) 章节,包含需要的扩展内容。
* 在 [For-In 循环](../02_language_guide/05_Control_Flow.md#for-in-loops) 章节中新增了区间迭代的例子。
* 在 [可失败构造器](../02_language_guide/14_Initialization.md#failable-initializers) 章节中新增了可失败数值转换的例子。
* 在 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关使用 Swift 语言版本的 `available` 特性的内容 。
* 更新 [函数类型](../03_language_reference/03_Types.md#function-type-h) 章节中的讨论,注意在写函数类型时不允许使用参数标签。
* 更新 [条件编译块](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节中的 Swift 语言版本号的讨论,现在可以使用可选的补丁版本号。
* 更新 [函数类型](../03_language_reference/03_Types.md#function-type-h) 章节的讨论,现在 Swift 区分了采用多参数的函数和采用元组类型的单个参数的函数。
* 在 [表达式](../03_language_reference/04_Expressions.md) 篇章中删除了动态表达式的章节,现在 `type(of:)` 是 Swift 标准库函数。
### 2016-10-27
* 更新至 Swift 3.0.1。
* 更新 [自动引用计数](../02_language_guide/24_Automatic_Reference_Counting.md) 章节中有关 weak 和 unowned 引用的讨论。
* 在 [声明标识符](../03_language_reference/06_Declarations.md#declaration-modifiers) 章节中新增了有关新的标识符 `unowned``unowend(safe)``unowned(unsafe)` 的内容。
* 在 [Any 和 AnyObject 的类型转换](../02_language_guide/18_Type_Casting.md#type-casting-for-any-and-anyobject) 章节中新增了一处说明,有关使用类型 `Any` 作为可选值。
* 更新 [表达式](../03_language_reference/04_Expressions.md) 章节,把括号表达式和元组表达式的描述分开。
### 2016-09-13
* 更新至 Swift 3.0。
* 更新 [函数](../02_language_guide/06_Functions.md) 篇章和 [函数声明](../03_language_reference/06_Declarations.md#function-declaration) 章节中有关函数的讨论,所有函数参数默认都有函数标签。
* 更新 [高级操作符](../02_language_guide/27_Advanced_Operators.md) 篇章中有关操作符的讨论,现在你可以作为类型函数来实现,替代之前的全局函数实现方式。
* 在 [访问控制](../02_language_guide/26_Access_Control.md) 章节中新增有关对新的访问级别描述符 `open``fileprivate` 的内容。
* 更新 [函数声明](../03_language_reference/06_Declarations.md#function-declaration) 章节中有关 `inout` 的讨论,注意它现在出现在参数类型的前面,而不是在参数名称的前面。
* 更新 [逃逸闭包](../02_language_guide/07_Closures.md#escaping-closures) 和 [自动闭包](../02_language_guide/07_Closures.md#autoclosures) 章节还有 [属性](../03_language_reference/07_Attributes.md) 篇章中有关 `@noescape``@autoclosure` 的讨论,现在他们是类型属性,而不是定义属性。
* 在 [高级操作符](../02_language_guide/27_Advanced_Operators.md) 篇章中 [自定义中缀操作符的优先级](./02_language_guide/27_Advanced_Operators.md#precedence-and-associativity-for-custom-infix-operators) 章节和 [定义](../03_language_reference/06_Declarations.md) 篇章中 [优先级组声明](../03_language_reference/06_Declarations.md#precedence-group-declaration-modifiers) 章节中新增了有关操作符优先级组的内容。
* 更新一些讨论,使用 macOS 替换掉 OS X Error 替换掉 ErrorProtocol。更新一些协议名称比如使用 ExpressibleByStringLiteral 替换掉 StringLiteralConvertible。
* 更新 [泛型](../02_language_guide/22_Generics.md) 篇章中 [泛型 Where 语句](../02_language_guide/22_Generics.md#extensions-with-a-generic-where-clause) 章节和 [泛型形参和实参](../03_language_reference/09_Generic_Parameters_and_Arguments.md) 篇章的讨论,现在泛型的 where 语句写在一个声明的最后。
* 更新 [逃逸闭包](../02_language_guide/07_Closures.md#escaping-closures) 章节中的讨论,现在闭包默认为非逃逸的。
* 更新 [基础部分](../02_language_guide/01_The_Basics.md) 篇章中 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding) 章节和 [语句](../03_language_reference/05_Statements.md) 篇章中 [While 语句](../03_language_reference/05_Statements.md#while-statement) 章节中的讨论,现在 if`while``guard` 语句使用逗号分隔条件列表,不需要使用 `where` 语句。
* 在 [控制流](../02_language_guide/05_Control_Flow.md) 篇章中 [Switch](../02_language_guide/05_Control_Flow.md#switch) 章节和 [语句](../03_language_reference/05_Statements.md) 篇章中 [Switch 语句](../03_language_reference/05_Statements.md#switch-statement) 章节中新增了 switch cases 可以使用多模式的内容。
* 更新 [函数类型](../03_language_reference/03_Types.md#function-type-h) 章节有关现在函数参数标签不包含在函数类型中的讨论。
* 更新 [协议](../02_language_guide/21_Protocols.md) 篇章中 [协议组合](../02_language_guide/21_Protocols.md#protocol-composition) 章节和 [类型](../03_language_reference/03_Types.md) 篇章中 [协议组合类型](../03_language_reference/03_Types.md#protocol-composition-type-h) 章节中有关使用新的 Protocol1 & Protocol2 语法的内容。
* 更新动态类型表达式章节中使用新的 `type(of:)` 表达式的讨论。
* 更新 [行控制表达式](../03_language_reference/05_Statements.md#line-control-statement) 章节中使用 `#sourceLocation(file:line:)` 表达式的讨论。
* 更新 [永不返回函数](../03_language_reference/06_Declarations.md#functions-that-never-return) 章节中使用 新的 `Never` 类型的讨论。
* 在 [字面量表达式](../03_language_reference/04_Expressions.md#literal-expression) 章节中新增了有关 `playground` 字面量的内容。
* 更新 [In-Out 参数](../03_language_reference/06_Declarations.md#in-out-parameters) 章节,标明只有非逃逸闭包能捕获 `in-out` 参数。
* 更新 [默认参数值](../02_language_guide/06_Functions.md#default-parameter-values) 章节,现在默认参数不能在调用时候重新排序。
* 更新 [属性](../03_language_reference/07_Attributes.md) 篇章中有关属性参数使用分号的说明。
* 在 [重新抛出函数和方法](../03_language_reference/06_Declarations.md#rethrowing-functions-and-methods) 章节中新增了有关在 catch 代码块中抛出错误的重新抛出函数的内容。
* 在 [Selector 表达式](../03_language_reference/04_Expressions.md#selector-expression7) 章节中新增了中有关访问 Objective-C 中 Selector 的 getter 和 setter 的内容。
* 在 [类型别名声明](../03_language_reference/06_Declarations.md#type-alias-declaration) 章节中中新增了有关泛型类型别名和在协议内使用类型别名的内容。
* 更新 [函数类型](../03_language_reference/03_Types.md#function-type-h) 章节中有关函数类型的讨论,标明函数类型作为参数类型必须使用括号包裹。
* 更新 [属性](../03_language_reference/07_Attributes.md) 篇章,标明 `@IBAction``@IBOutlet``@NSManaged` 隐式含有 `@objc` 属性。
* 在 [声明属性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关 `@GKInspectable` 的内容。
* 更新 [可选协议要求](../02_language_guide/21_Protocols.md#optional-protocol-requirements) 章节中有关只能在与 `Objective-C` 交互的代码中才能使用可选协议要求的内容。
* 删除 [函数声明](../03_language_reference/06_Declarations.md#function-declaration) 章节中有关显式使用 `let` 关键字作为函数参数的内容。
* 删除 [语句](../03_language_reference/05_Statements.md) 章节中有关 `Boolean` 协议的内容, 现在这个协议已经被 Swift 标准库删除。
* 更正 [声明属性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中有关 `@NSApplicationMain` 协议的内容。
### 2016-03-21
* 更新至 Swift 2.2。
* 在 [编译配置语句](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节新增了中有关如何根据 Swift 版本进行条件编译。
* 在 [显示成员表达式](../03_language_reference/04_Expressions.md#explicit-member-expression) 章节中新增了有关如何区分只有参数名不同的方法和构造器的内容。
* 在 [选择器表达式](../03_language_reference/04_Expressions.md#selector-expression7) 章节中新增了了针对 Objective-C 选择器的 `#selector` 语法。
* 更新 [关联类型](../02_language_guide/22_Generics.md#associated-types) 和 [协议关联类型声明](../03_language_reference/06_Declarations.md#protocol-associated-type-declaration) 章节中有关使用 `associatedtype` 关键词修饰关联类型的讨论。
* 更新 [可失败构造器](../02_language_guide/14_Initialization.md#failable-initializers) 章节中有关当构造器在实例完全初始化之前返回 `nil` 的相关内容。
* 在 [比较运算符](../02_language_guide/02_Basic_Operators.md#comparison-operators) 章节中新增了比较元组的内容。
* 在 [关键字和标点符号](../03_language_reference/02_Lexical_Structure.md#keywords-and-punctuation) 章节中新增了使用关键字作为外部参数名的内容。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中有关 `@objc` 特性的讨论,并指出枚举和枚举用例。
* 更新 [操作符](../03_language_reference/02_Lexical_Structure.md#operator) 章节中对于自定义运算符的包含了 `.` 的讨论。
* 在 [重新抛出错误的函数和方法](../03_language_reference/06_Declarations.md#rethrowing-functions-and-methods) 章节中新增了一处说明,重新抛出错误函数不能直接抛出错误。
* 在 [属性观察器](../02_language_guide/10_Properties.md#property-observers) 章节中新增了一处说明,当作为 in-out 参数传递属性时,属性观察器的调用行为。
* 在 [Swift 初见](../01_welcome_to_swift/03_a_swift_tour.md) 篇章中新增了错误处理的章节。
* 更新 [弱引用](../02_language_guide/24_Automatic_Reference_Counting.md#weak-references) 章节中的图片用以更清楚的展示重新分配过程。
* 删除 C 语言风格的 `for` 循环,`++` 前缀和后缀运算符,以及 `--` 前缀和后缀运算符。
* 删除对变量函数参数和柯里化函数的特殊语法的讨论。
### 2015-10-20
* 更新至 Swift 2.1。
* 更新 [字符串插值](../02_language_guide/03_Strings_and_Characters.md#string-interpolation) 和 [字符串字面量](../03_language_reference/02_Lexical_Structure.md#string-literal) 章节,现在字符串插值可包含字符串字面量。
* 在 [逃逸闭包](../02_language_guide/07_Closures.md#escaping-closures) 章节中新增了有关 `@noescape` 属性的相关内容。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 和 [编译配置语句](../03_language_reference/05_Statements.md#Conditional-Compilation-Block) 章节中与 tvOS 相关的内容。
* 在 [In-Out 参数](../03_language_reference/06_Declarations.md#in-out-parameters) 章节中新增了与 in-out 参数行为相关的内容。
* 在 [捕获列表](../03_language_reference/04_Expressions.md#capture-lists) 章节新增了有关指定闭包捕获列表被捕获时捕获值的相关内容。
* 更新 [可选链式调用访问属性](../02_language_guide/16_Optional_Chaining.md#accessing-properties-through-optional-chaining) 章节,阐明了如何通过可选链式调用进行赋值。
* 改进 [自动闭包](../02_language_guide/07_Closures.md#autoclosures) 章节中对自闭包的讨论。
* 在 [Swift 初见](../01_welcome_to_swift/03_a_swift_tour.md) 篇章中新增了一个使用 `??` 操作符的例子。
### 2015-09-16
* 更新至 Swift 2.0。
* 在 [错误处理](../02_language_guide/17_Error_Handling.md) 篇章中新增了有关错误处理的相关内容,包括 [Do 语句](../03_language_reference/05_Statements.md#do-statement)、 [Throw 语句](../03_language_reference/05_Statements.md#throw-statement)、 [Defer 语句](../03_language_reference/05_Statements.md##defer-statements) 以及 [try 运算符](../03_language_reference/04_Expressions.md#try-operator)。
* 更新 [错误表示和抛出](../02_language_guide/17_Error_Handling.md#representing-and-throwing-errors) 章节,现在所有类型都可以遵循 `ErrorType` 协议了。
* 在 [将错误装换成可选值](../02_language_guide/17_Error_Handling.md#converting-errors-to-optional-values) 篇章增加了 `try?` 关键字相关内容。
* 在 [枚举](../02_language_guide/08_Enumerations.md) 篇章的 [递归枚举](../02_language_guide/08_Enumerations.md#recursive-enumerations) 章节以及以及 [声明](../03_language_reference/06_Declarations.md) 篇章的 [任意类型用例的枚举](../03_language_reference/06_Declarations.md#enumerations-with-cases-of-any-type) 章节中新增了递归枚举相关内容。
* 在 [控制流](../02_language_guide/05_Control_Flow.md) 篇章的 [API 可用性检查](../02_language_guide/05_Control_Flow.md#checking-api-availability) 章节和 [语句](../03_language_reference/05_Statements.md) 篇章的 [可用性条件](../03_language_reference/05_Statements.md#availability-condition) 章节中新增了有关 API 可用性检查相关的内容。
* 在 [控制流](../02_language_guide/05_Control_Flow.md) 篇章的 [尽早退出](../02_language_guide/05_Control_Flow.md#early-exit) 章节和 [语句](../03_language_reference/05_Statements.md) 篇章的 [Guard 语句](../03_language_reference/05_Statements.md#guard-statement) 章节新增了与 `guard` 语句相关的内容。
* 在 [协议](../02_language_guide/21_Protocols.md) 篇章中 [协议扩展](../02_language_guide/21_Protocols.md#protocol-extensions) 章节中新增了有关协议扩展的内容。
* 在 [访问控制](../02_language_guide/26_Access_Control.md) 篇章的 [单元测试 target 的访问级别](../02_language_guide/26_Access_Control.md#access-levels-for-unit-test-targets) 章节中新增了有关单元测试访问控制相关的内容。
* 在 [模式](../03_language_reference/08_Patterns.md) 篇章的 [可选模式](../03_language_reference/08_Patterns.md#optional-pattern) 章节中新增了可选模式相关内容。
* 更新 [Repeat-While](../02_language_guide/05_Control_Flow.md#repeat-while) 章节中有关 `repeat-while` 循环相关的内容。
* 更新 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 章节,现在 `String` 类型在 Swift 标准库中不再遵循 `CollectionType` 协议。
* 在 [常量与变量打印](../02_language_guide/01_The_Basics.md#printing) 章节中新增了新 Swift 标准库中有关 `print(-:separator:terminator) ` 相关内容。
* 在 [枚举](../02_language_guide/08_Enumerations.md) 篇章的 [原始值的隐式赋值](../02_language_guide/08_Enumerations.md#implicitly-assigned-raw-values) 章节和 [声明](../03_language_reference/06_Declarations.md) 篇章的 [包含原始值类型的枚举](../03_language_reference/06_Declarations.md#enumerations-with-cases-of-a-raw-value-type) 章节中新增了有关包含 `String` 原始值的枚举用例的行为相关内容。
* 在 [自动闭包](../02_language_guide/07_Closures.md#autoclosures) 章节中新增了有关 `@autoclosure` 特性的相关内容,包括它的 `@autoclosure(escaping)` 形式。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中有关 `@avaliable``warn-unused-result` 特性的相关内容。
* 更新 [类型特性](../03_language_reference/07_Attributes.md#type-attributes) 章节中有关 `@convention` 特性的相关内容。
* 在 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding) 章节中新增了有关使用 `where` 子句进行多可选绑定的相关内容。
* 在 [字符串字面量](../03_language_reference/02_Lexical_Structure.md#string-literal) 章节中新增了有关在编译时使用 `+` 运算符拼接字符串字面量的相关内容。
* 在 [元类型](../03_language_reference/03_Types.md#metatype-type-h) 章节中新增了有关元类型值的比较和使用它们通过构造器表达式构造实例相关内容。
* 在 [断言调试](../02_language_guide/01_The_Basics.md#debugging-with-assertions) 章节中新增了一处说明,有关用户定义断言何时会失效。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节中对 `@NSManaged` 特性的讨论,现在这个特性可以被应用到一个确定实例方法。
* 更新 [可变参数](../02_language_guide/06_Functions.md#variadic-parameters) 章节,现在可变参数可以声明在函数参数列表的任意位置中。
* 在 [重写可失败构造器](../02_language_guide/14_Initialization.md#overriding-a-failable-initializer) 章节中新增了有关非可失败构造器相当于一个可失败构造器通过父类构造器的结果进行强制拆包的相关内容。
* 在 [任意类型用例的枚举](../03_language_reference/06_Declarations.md#enumerations-with-cases-of-any-type) 章节中新增了有关枚举用例作为函数的内容。
* 在 [构造器表达式](../03_language_reference/04_Expressions.md#initializer-expression) 章节中新增了有关显式引用一个构造器相关内容。
* 在 [编译控制语句](../03_language_reference/05_Statements.md#compiler-control-statements) 章节中新增了有关编译内容以及行控制语句相关内容。
* 在 [元类型](../03_language_reference/03_Types.md#metatype-type-h) 章节新增了一处说明,有关如何从元类型值中构造类实例相关内容。
* 在 [弱引用](../02_language_guide/24_Automatic_Reference_Counting.md#weak-references) 章节新增了一处说明,有关弱引用作为缓存所存在的不足。
* 更新 [类型特性](../02_language_guide/10_Properties.md#type-properties) 章节,提到了存储型特性其实是懒加载。
* 更新 [捕获类型](../02_language_guide/07_Closures.md#capturing-values) 章节,阐明了变量和常量在闭包中如何被捕获。
* 更新 [声明特性](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节,用以描述何时在类中使用 `@objc` 关键字。
* 在 [错误处理](../02_language_guide/17_Error_Handling.md#handling-errors) 章节中新增了一处说明,有关执行 `throw` 语句的性能。在 [Do 语句](../03_language_reference/05_Statements.md#do-statement) 章节的 do 语句部分也新增了类似内容。
* 更新 [类型特性](../02_language_guide/10_Properties.md#type-properties) 章节中有关类、结构体和枚举的存储型和计算型特性相关的内容。
* 更新 [Break 语句](../03_language_reference/05_Statements.md#break-statement) 章节中有关带标签的 break 语句相关内容。
* 在 [属性观察器](../02_language_guide/10_Properties.md#property-observers) 章节更新了一处说明,用来明确 `willSet``didSet` 观察器的行为。
* 在 [访问级别](../02_language_guide/26_Access_Control.md#access-levels) 章节新增了有关 `private` 作用域的相关内容说明。
* 在 [弱引用](../02_language_guide/24_Automatic_Reference_Counting.md#weak-references) 章节新增了有关弱应用在垃圾回收系统和 ARC 之间的区别的说明。
* 更新 [字符串字面量中特殊字符](../02_language_guide/03_Strings_and_Characters.md#special-characters-in-string-literals) 章节,对 Unicode 标量更精确定义。
### 2015-04-08
* 更新至 Swift 1.2。
* Swift 现在自身提供了一个 `Set` 集合类型,更多内容,请看 [Sets](../02_language_guide/04_Collection_Types.md#sets) 。
* `@autoclosure` 现在是一个参数声明的属性,而不是参数类型的属性。这里还有一个新的参数声明属性 `@noescape`。更多内容,请看 [属性声明](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 。
* 对于类型属性和方法现在可以使用 `static` 关键字作为声明描述符,更多内容,请看 [类型变量属性](../03_language_reference/06_Declarations.md#type-variable-properties)。
* Swift 现在包含一个 `as?``as!` 的向下可失败类型转换运算符。更多内容,请看 [协议遵循性检查](../02_language_guide/21_Protocols.md#checking-for-protocol-conformance)。
* 新增 [字符串索引](../02_language_guide/03_Strings_and_Characters.md#string-indices) 的新指导章节。
* 在 [溢出运算符](../02_language_guide/27_Advanced_Operators.md#overflow-operators) 一节中删除了溢出除运算符(`&/`)和求余溢出运算符(`&%`)。
* 更新常量和常量属性在声明和构造时的规则,更多内容,请看 [常量声明](../03_language_reference/06_Declarations.md#constant-declaration) 。
* 更新字符串字面量中 Unicode 标量集的定义,请看 [字符串字面量中的特殊字符](../02_language_guide/03_Strings_and_Characters.md#special-characters-in-string-literals) 。
* 更新 [区间运算符](../02_language_guide/02_Basic_Operators.md#range-operators) 章节,注意当半开区间运算符含有相同的起止索引时,其区间为空。
* 更新 [闭包引用类型](../02_language_guide/07_Closures.md#closures-are-reference-types) 章节,对于变量的捕获规则进行了阐明。
* 更新 [值溢出](../02_language_guide/27_Advanced_Operators.md#value-overflow) 章节,对有符号整数和无符号整数的溢出行为进行了阐明。
* 更新 [协议声明](../03_language_reference/06_Declarations.md#protocol-declaration) 章节,对协议声明时的作用域和成员等内容进行了阐明。
* 更新 [捕获列表](../02_language_guide/24_Automatic_Reference_Counting.md#defining-a-capture-list) 章节,对于闭包捕获列表中的弱引用和无主引用的使用语法进行了阐明。
* 更新 [运算符](../03_language_reference/02_Lexical_Structure.md#operator) 章节明确指明一些例子来说明自定义运算符所支持的特性如数学运算符各种符号Unicode 符号块等。
* 在函数作用域中的常量声明时可以不被初始化,它必须在第一次使用前被赋值。更多的内容,请看 [常量声明](../03_language_reference/06_Declarations.md#constant-declaration)。
* 在构造器中,常量属性有且仅能被赋值一次。更多内容,请看 [在构造过程中给常量属性赋值](../02_language_guide/14_Initialization.md#assigning-constant-properties-during-initialization)。
* 多个可选绑定现在可以在`if`语句后面以逗号分隔的赋值列表的方式出现,更多内容,请看 [可选绑定](../02_language_guide/01_The_Basics.md#optional-binding)。
* 一个 [可选链表达式](../03_language_reference/04_Expressions.md#optional-chaining-expression) 必须出现在后缀表达式中。
* 协议类型转换不再局限于 `@obj` 修饰的协议了。
* 在运行时可能会失败的类型转换可以使用 `as?``as!` 运算符,而确保不会失败的类型转换现在使用 `as` 运算符。更多内容,请看 [类型转换运算符](../03_language_reference/04_Expressions.md#type-casting-operator)。
### 2014-10-16
* 更新至 Swift 1.1。
* 新增 [失败构造器](../02_language_guide/14_Initialization.md#failable-initializers) 的完整指引。
* 在协议中新增了 [失败构造器要求](../02_language_guide/21_Protocols.md#failable-initializer-requirements) 的描述。
* 常量和变量的 `Any` 类型现可以包含函数实例。更新了有关 `Any` 相关的示例来展示如何在 `switch` 语句中如何检查并转换到一个函数类型。
* 带有原始值的枚举类型增加了一个 `rawValue` 属性替代 `toRaw()` 方法,同时使用了一个以 `rawValue` 为参数的失败构造器来替代 `fromRaw()` 方法。更多的内容,请看 [原始值](../02_language_guide/08_Enumerations.md#raw-values) 和 [带原始值的枚举类型](../03_language_reference/06_Declarations.md#enumerations-with-cases-of-a-raw-value-type)。
* 新增 [Failable Initializer](../03_language_reference/06_Declarations.md#failable-initializers) 的参考章节,它可以触发初始化失败。
* 自定义运算符现在可以包含 `?` 字符,更新了 [运算符](../03_language_reference/02_Lexical_Structure.md#operator) 涉及改进后的规则的部分,并且在 [自定义运算符](../02_language_guide/27-Advanced-Operators.md#custom-operators) 章节中删除了重复的运算符有效字符集合。
### 2014-08-18
* 描述 Swift 1.0 的新文档。Swift 是苹果公司发布的全新编程语言,用于 iOS 和 OS X 应用开发。
* 在协议中新增了 [对构造器的规定](../02_language_guide/21_Protocols.md#initializer-requirements) 章节。
* 新增 [类专属协议](../02_language_guide/21_Protocols.md#class-only-protocol) 章节。
* [断言](../02_language_guide/01_The_Basics.md#assertions-and-preconditions) 现在可以使用字符串内插语法,并删除了文档中有冲突的注释。
* 更新 [连接字符串和字符](../02_language_guide/03_Strings_and_Characters.md#concatenating-strings-and-characters) 章节来说明字符串和字符不能再用 `+` 号运算符或者复合加法运算符 `+=` 相互连接,这两种运算符现在只能用于字符串之间相连。请使用 `String` 类型的 `append` 方法在一个字符串的尾部增加单个字符。
* 在 [属性声明](../03_language_reference/07_Attributes.md#Ideclaration-attributes) 章节增加了有关 `availability` 特性的一些内容。
* [可选类型](../02_language_guide/01_The_Basics.md#optionals) 若有值时,不再隐式的转换为 `true`,同样,若无值时,也不再隐式的转换为 `false`,这是为了避免在判别 optional `Bool` 的值时产生困惑。 替代的方案是,用`==``!=` 运算符显式地去判断 Optinal 是否是 `nil`,以确认其是否包含值。
* Swift 新增了一个 [Nil 合并运算符](../02_language_guide/02_Basic_Operators.md#nil-coalescing-operator) (`a ?? b`) , 该表达式中,如果 Optional `a` 的值存在,则取得它并返回,若 Optional `a``nil`,则返回默认值 `b`
* 更新和扩展 [字符串的比较](../02_language_guide/03_Strings_and_Characters.md#comparing-strings) ,用以反映和展示'字符串和字符的比较',以及'前缀prefix/后缀postfix比较'都开始基于扩展字符集extended grapheme clusters规范的等价比较。
* 现在,你可以通过下标赋值或者 [可选调用链](../02_language_guide/16_Optional_Chaining.md) 中的可变方法和操作符来给属性设值。相应地更新了有关 [通过可选链接访问属性](../02_language_guide/16_Optional_Chaining.md#accessing-properties-through-optional-chaining) 的内容,并扩展了 [通过可选链接调用方法](../02_language_guide/16_Optional_Chaining.md#calling-methods-through-optional-chaining) 时检查方法调用成功的示例,以显示如何检查属性设置是否成功。
* 在可选链中新增了 [访问可选类型的下标脚注](../02_language_guide/16_Optional_Chaining.md#accessing-subscripts-through-optional-chaining) 章节。
* 更新 [访问和修改数组](../02_language_guide/04_Collection_Types.md#accessing-and-modifying-a-dictionary) 章节以标示,从该版本起,不能再通过 `+=` 运算符给一个数组新增一个新的项。对应的替代方案是,使 `append` 方法,或者通过 `+=` 运算符来新增一个只有一个项的数组。
* 新增一处说明,在 [范围运算符](../02_language_guide/02_Basic_Operators.md#range-operators) 中,比如, `a..b``a..<b` ,起始值 `a` 不能大于结束值 `b`
* 重写 [继承](../02_language_guide/13_Inheritance.md) 篇章删除了本章中有关构造器重写的介绍性报道转而将更多的注意力放到新增的部分——子类的新功能以及如何通过重写overrides修改已有的功能。另外 [重写属性的 Getters 和 Setters](../02_language_guide/13_Inheritance.md#overriding-property-etters-and-setters) 中的例子已经被替换为展示如何重写一个 `description` 属性。 (而有关如何在子类的构造器中修改继承属性的默认值的例子,已经被移到 [构造过程](../02_language_guide/14_Initialization.md) 篇章。)
* 更新 [构造器的继承与重写](../02_language_guide/14_Initialization.md#initializer-inheritance-and-overriding) 章节以标示: 重写一个特定的构造器必须使用 `override` 修饰符。
* 更新 [Required 构造器](../02_language_guide/14_Initialization.md#required-initializers) 章节以标示:`required` 修饰符现在需要出现在所有子类的 required 构造器的声明中,而 required 构造器的实现,现在可以仅从父类自动继承。
* 中置Infix的 [运算符函数](../02_language_guide/27_Advanced_Operators.md#operator-functions) 不再需要 `@infix` 属性。
* [前置和后置运算符](../02_language_guide/27_Advanced_Operators.md#prefix-and-postfix-operators) 的 `@prefix``@postfix` 属性,已变更为 `prefix``postfix` 声明修饰符。
* 新增一处说明,在 Prefix 和 postfix 运算符被作用于同一个操作数时 [前置和后置运算符](../02_language_guide/27_Advanced_Operators.md#prefix-and-postfix-operators) 的执行顺序。
* [组合赋值运算符](../02_language_guide/27_Advanced_Operators.md#compound-assignment-operators) 的运算符函数不再使用 `@assignment` 属性来定义函数。
* 在定义 [自定义操作符](../02_language_guide/27_Advanced_Operators.md#custom-operators) 时,`修饰符Modifiers的出现顺序发生变化`。比如现在,你该编写 `prefix operator` 而不是 `operator prefix`
* 在 [声明修饰符](../03_language_reference/06_Declarations.md#declaration-modifiers) 章节新增了有关 `dynamic` 声明修饰符的内容。
* 新增有关 [字面量](../03_language_reference/02_Lexical_Structure.md#literal) 类型推导内容的内容。
* 新增更多有关柯里化函数的内容。
* 新增 [权限控制](../02_language_guide/26_Access_Control.md) 篇章。
* 更新 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 章节,在 Swift 中现在 `Character` 类型代表的是扩展字符集extended grapheme cluster中的一个 Unicode为此新增了 [Extended Grapheme Clusters](../02_language_guide/03_Strings_and_Characters.md#extended-grapheme-clusters) 章节。同时,[Unicode 标量](../02_language_guide/03-Strings-And-Characters.md#unicode-scalars-representation) 和 [字符串比较](../02_language_guide/03-Strings-And-Characters.md#comparing-strings) 章节新增了更多内容。
* 更新 [字符串字面量](../02_language_guide/03_Strings_and_Characters.md#string-literals) 章节在一个字符串中Unicode 标量Unicode scalars`\u{n}`的形式来表示,`n` 是一个最大可以有8位的16进制数。
* `NSString` `length` 属性已被映射到 Swift 的内建 `String`类型。(注意,这两属性的类型是`utf16Count`,而非 `utf16count`)。
* Swift 的内建 `String` 类型不再拥有 `uppercaseString``lowercaseString` 属性。在 [字符串和字符](../02_language_guide/03_Strings_and_Characters.md) 章节中删除了对应部分,并更新了各种对应的代码用例。
* 新增 [没有外部名的构造器参数](../02_language_guide/14_Initialization.md#initializer-parameters-without-external-names) 章节。
* 新增 [Required 构造器](../02_language_guide/14_Initialization.md#required-initializers) 章节。
* 新增 [可选元组返回类型](../02_language_guide/06_Functions.md#optional-tuple-return-types) 章节。
* 更新 [类型注解](../02_language_guide/01_The_Basics.md#type-annotations) 章节,多个相关变量可以用"类型注解”在同一行中声明为同一类型。
* `@optional`, `@lazy`, `@final`, `@required` 等关键字被更新为 `optional`, `lazy`, `final`, `required` 参见 [声明修饰符](../03_language_reference/06_Declarations.md#declaration-modifiers)。
* 更新了整本书中有关 `..<` 的引用,从半闭区间改为了 [半开区间](../02_language_guide/02_Basic_Operators.md#half-open-range-operator)。
* 更新 [读取和修改字典](../02_language_guide/04_Collection_Types.md#accessing-and-modifying-a-dictionary) 章节, `Dictionary` 现在增加了一个 Boolean 型的属性:`isEmpty`
* 解释了哪些字符(集)可被用来定义 [自定义操作符](../02_language_guide/27_Advanced_Operators.md#custom-operators)。
* `nil` 和布尔运算中的 `true``false` 现在被定义为 [字面量](../03_language_reference/02_Lexical_Structure.md#literal)。
* Swift 中的数组 `Array` 类型从现在起具备了完整的值语义。具体内容被更新到 [集合的可变性](../02_language_guide/04_Collection_Types.md#mutability-of-collections) 和 [数组](../02_language_guide/04_Collection_Types.md#arrays) 两小节,以反映这个新的变化。 此外,还解释了如何给 Strings, Arrays 和 Dictionaries 进行赋值和拷贝。
* [数组类型速记语法](../02_language_guide/04_Collection_Types.md#array-type-shorthand-syntax) 从 `SomeType []` 更新为 ` [SomeType]`
* 新增 [字典类型的速记语法](../02_language_guide/04_Collection_Types.md#dictionary-type-shorthand-syntax) 章节,现在书写格式为: ` [KeyType: ValueType]`
* 新增 [字典键类型的哈希值](../02_language_guide/04_Collection_Types.md#hash-values-for-set-types) 章节。
* [闭包表达式](../02_language_guide/07_Closures.md#closure-expressions) 示例中使用新的全局函数 `sorted` 取代原先的全局函数 `sort` 去展示如何返回一个全新的数组。
* 更新 [结构体逐一成员构造器](../02_language_guide/14_Initialization.md#memberwise-initializers-for-structure-types) 章节,即使结构体的成员 `没有默认值`,逐一成员构造器也可以自动获得。
* [半开区间运算符](../02_language_guide/02_Basic_Operators.md#half-open-range-operator) 中`..` 更新为 `..<`
* 新增 [泛型拓展](../02_language_guide/22_Generics.md#extending-a-generic-type) 的示例。
感谢阅读!

View File

@ -1,47 +1,49 @@
# Summary
* 欢迎使用 Swift
* [关于 Swift](chapter1/01_about_swift.md)
* [版本兼容性](chapter1/02_version_compatibility.md)
* [Swift 初见](chapter1/03_a_swift_tour.md)
* [Swift 版本历史记录](chapter1/04_revision_history.md)
* [关于 Swift](01_welcome_to_swift/01_about_swift.md)
* [版本兼容性](01_welcome_to_swift/02_version_compatibility.md)
* [Swift 初见](01_welcome_to_swift/03_a_swift_tour.md)
* [Swift 版本历史记录](04_revision_history/04_revision_history.md)
* Swift 教程
* [基础部分](chapter2/01_The_Basics.md)
* [基本运算符](chapter2/02_Basic_Operators.md)
* [字符串和字符](chapter2/03_Strings_and_Characters.md)
* [集合类型](chapter2/04_Collection_Types.md)
* [控制流](chapter2/05_Control_Flow.md)
* [函数](chapter2/06_Functions.md)
* [闭包](chapter2/07_Closures.md)
* [枚举](chapter2/08_Enumerations.md)
* [类和结构体](chapter2/09_Structures_And_Classes.md)
* [属性](chapter2/10_Properties.md)
* [方法](chapter2/11_Methods.md)
* [下标](chapter2/12_Subscripts.md)
* [继承](chapter2/13_Inheritance.md)
* [构造过程](chapter2/14_Initialization.md)
* [析构过程](chapter2/15_Deinitialization.md)
* [可选链](chapter2/16_Optional_Chaining.md)
* [错误处理](chapter2/17_Error_Handling.md)
* [类型转换](chapter2/18_Type_Casting.md)
* [嵌套类型](chapter2/19_Nested_Types.md)
* [扩展](chapter2/20_Extensions.md)
* [协议](chapter2/21_Protocols.md)
* [泛型](chapter2/22_Generics.md)
* [自动引用计数](chapter2/23_Automatic_Reference_Counting.md)
* [内存安全](chapter2/24_Memory_Safety.md)
* [访问控制](chapter2/25_Access_Control.md)
* [高级运算符](chapter2/26_Advanced_Operators.md)
* [基础部分](02_language_guide/01_The_Basics.md)
* [基本运算符](02_language_guide/02_Basic_Operators.md)
* [字符串和字符](02_language_guide/03_Strings_and_Characters.md)
* [集合类型](02_language_guide/04_Collection_Types.md)
* [控制流](02_language_guide/05_Control_Flow.md)
* [函数](02_language_guide/06_Functions.md)
* [闭包](02_language_guide/07_Closures.md)
* [枚举](02_language_guide/08_Enumerations.md)
* [类和结构体](02_language_guide/09_Structures_And_Classes.md)
* [属性](02_language_guide/10_Properties.md)
* [方法](02_language_guide/11_Methods.md)
* [下标](02_language_guide/12_Subscripts.md)
* [继承](02_language_guide/13_Inheritance.md)
* [构造过程](02_language_guide/14_Initialization.md)
* [析构过程](02_language_guide/15_Deinitialization.md)
* [可选链](02_language_guide/16_Optional_Chaining.md)
* [错误处理](02_language_guide/17_Error_Handling.md)
* [并发](02_language_guide/28_Concurrency.md)
* [类型转换](02_language_guide/18_Type_Casting.md)
* [嵌套类型](02_language_guide/19_Nested_Types.md)
* [扩展](02_language_guide/20_Extensions.md)
* [协议](02_language_guide/21_Protocols.md)
* [泛型](02_language_guide/22_Generics.md)
* [不透明类型](02_language_guide/23_Opaque_Types.md)
* [自动引用计数](02_language_guide/24_Automatic_Reference_Counting.md)
* [内存安全](02_language_guide/25_Memory_Safety.md)
* [访问控制](02_language_guide/26_Access_Control.md)
* [高级运算符](02_language_guide/27_Advanced_Operators.md)
* 语言参考
* [关于语言参考](chapter3/01_About_the_Language_Reference.md)
* [词法结构](chapter3/02_Lexical_Structure.md)
* [类型](chapter3/03_Types.md)
* [表达式](chapter3/04_Expressions.md)
* [语句](chapter3/05_Statements.md)
* [声明](chapter3/06_Declarations.md)
* [特性](chapter3/07_Attributes.md)
* [模式](chapter3/08_Patterns.md)
* [泛型参数](chapter3/09_Generic_Parameters_and_Arguments.md)
* [语法总结](chapter3/10_Summary_of_the_Grammar.md)
* [关于语言参考](03_language_reference/01_About_the_Language_Reference.md)
* [词法结构](03_language_reference/02_Lexical_Structure.md)
* [类型](03_language_reference/03_Types.md)
* [表达式](03_language_reference/04_Expressions.md)
* [语句](03_language_reference/05_Statements.md)
* [声明](03_language_reference/06_Declarations.md)
* [特性](03_language_reference/07_Attributes.md)
* [模式](03_language_reference/08_Patterns.md)
* [泛型参数](03_language_reference/09_Generic_Parameters_and_Arguments.md)
* [语法总结](03_language_reference/10_Summary_of_the_Grammar.md)
* 翻译贡献者
* [翻译贡献者](contributors.md)

View File

@ -1,10 +0,0 @@
# 版本兼容性
本书描述的是在 Xcode 10.2 中的默认 Swift 版本 Swift 5。你可以使用 Xcode10.2 来构建 Swift 5、Swift 4.2 或 Swift 写的项目
当您使用 Xcode 10.2 构建 Swift 4 和 Swift 4.2 代码时,除了下面的功能仅支持 Swift 5其他大多数功能都依然可用。
* **try?** 表达式不会为已返回可选类型的代码引入额外的可选类型层级。
* 大数字的整型字面量初始化代码的类型将会被正确推导,例如 **UInt64(0xffff_ffff_ffff_ffff)** 将会被推导为整型类型而非溢出。
用 Swift 5 写的项目可以依赖用 Swift 4.2 或 Swift 4 写的项目反之亦然。这意味着如果你将一个大的项目分解成多个框架framework你可以每次一个框架地迁移 Swift 4 代码到 Swift 5。

View File

@ -1,256 +0,0 @@
# Swift 文档修订历史
### 2019-01-24
* 更新到 Swift 5。
* 增加了[拓展字符串分隔符](../chapter2/03_Strings_And_Characters.md#extended-string-delimiters)部分,另外在[字符串字面量](../chapter3/03_Lexical_Structure.md#string-literal)部分更新了拓展字符串分隔符相关内容。
* 添加了[动态调用](../chapter3/07_Attributes.md#dynamiccallable)章节,其中包含有关使用 `dynamicCallable` 属性动态调用实例作为函数的信息。
* 添加了[unknown](../chapter3/07_Attributes.md#unknown)和[未来枚举匹配](../chapter3/05_Statements.md#future-case2)章节,其中包含了使用 `unknown` 来处理未来枚举可能发生改变的情形。
* 在[Key-Path](../chapter3/04_Expressions.md#key-path-expression)表达式章节添加了标示 key path (\.self) 相关内容。
* 在[可选编译块](../chapter3/05_Statements.md#Conditional-Compilation-Block)章节新增了小于比较符 `<` 相关内容。
### 2018-09-17
* 更新至 Swift 4.2。
* 在[遍历枚举情形](../chapter2/08_Enumerations.md#iterating-over-enumeration-cases)章节添加了访问所有枚举情形的内容。
* 在[编译诊断](../chapter3/05_Statements.md#compile-time-diagnostic-statement)章节添加了有关 `#error``#warning` 相关内容。
* 在[属性声明](../chapter3/07_Attributes.md#Ideclaration-attributes)章节中补充了 `inlinable``usableFromInline` 属性相关的内联信息。
* 在[属性声明](../chapter3/07_Attributes.md#Ideclaration-attributes)章节中添加了 `requires_stored_property_inits``warn_unqualified_access` 属性相关的信息。
* 在[可选编译块](../chapter3/05_Statements.md#Conditional-Compilation-Block)章节新增了如何根据 Swift 编译器版本对代码进行对应编译处理的内容。
* 在[字面量语法](../chapter3/04_Expressions.md#literal-expression)章节补充了 `#dsohandle` 相关内容。
### 2018-03-29
* 更新至 Swift 4.1。
* 在[等价运算符](../chapter2/26_Advanced_Operators.md#equivalence-operators)章节添加了等价运算符的合成实现信息。
* 在[声明](../chapter3/06_Declarations.md)一章的[申明拓展](../chapter3/06_Declarations.md#extension-declaration)部分和[协议](../chapter2/21_Protocols.md)一章的[有条件地遵循协议](../chapter2/21_Protocols.md#Conditionally-Conforming-to-a-Protocol)部分添加了协议的有条件遵循相关内容。
* 在[关联类型约束中使用协议](../chapter2/22_Generics.md##using-a-protocol-in-its-associated-types-constraints)章节中添加了递归协议约束的内容。
* 在[条件编译块](../chapter3/05_Statements.md#Conditional-Compilation-Block)章节中添加了 `canImport()``targetEnvironment()` 平台条件相关内容。
### 2017-12-04
* 更新至 Swift 4.0.3。
* 更新[Key-Path](../chapter3/04_Expressions.md#key-path-expression)表达式章节,现在 key path 支持下标子路径。
### 2017-09-19
* 更新至 Swift 4.0。
* 在[内存安全](../chapter2/24_MemorySafety.md)章节补充了内存互斥访问相关的内容。
* 添加了[带有泛型 Where 子句联类型](../chapter2/22_Generics.md#associated-types-with-a-generic-where-clause)章节,现在可以使用泛型 `where` 子句约束关联类型。
* 在[字符串和字符](../chapter2/03_Strings_And_Characters.md)的[字面量](../chapter2/03_Strings_And_Characters.md#string-literals)一节以及[词法结构](../chapter3/02_Lexical_Structure.md)的[字符串字面量](../chapter3/02_Lexical_Structure.md#string-literal)一节中新增了多行字符串字面量相关内容。
* 更新了[声明属性](../chapter3/07_Attributes.md#Ideclaration-attributes)中 `objc` 属性的讨论,现在该属性是在更少的位置推断出来的。
* 添加了[范型下标](../chapter2/22_Generics.md#generic-subscripts)章节,现在下标也支持范型特性了。
* 更新了[协议](../chapter2/21_Protocols.md)一章中[协议组合](../chapter2/21_Protocols.md#protocol-composition)部分以及[类型](../chapter3/03_Types.md)一章中[协议组合类型](../chapter3/03_Types.md#protocol-composition)部分的讨论,现在协议组合类型支持进行父类约束了。
* 更新了[拓展声明](../chapter3/06_Declarations.md#extension-declaration)中关于协议的讨论,现在它们不支持 `final` 特性了。
* 在[断言和前置条件](../chapter2/01_TheBasics.md#assertions-and-preconditions)部分增加了部分前置条件和致命错误的内容。
### 2017-03-27
* 更新至 Swift 3.1。
* 增加[范型 Where 子句扩展](../chapter2/22_Generics.md#extensions-with-a-generic-where-clause),其中包含需要的扩展信息。
* 增加了一个区间迭代的例子到[For-In 循环](../chapter2/05_Control_Flow.md#for-in-loops)。
* 增加一个可失败数值转换的例子[到可失败构造器](../chapter2/14_Initialization.md#failable-initializers)章节。
* 增加关于使用 Swift 语言版本的 `available` 特性内容到[声明特性](../chapter3/07_Attributes.md#Ideclaration-attributes)章节。
* 更新了[函数类型](../chapter3/03_Types.md#function_type)章节中的描述,注意在写函数类型时不允许使用参数标签。
* 更新了[条件编译块](../chapter3/05_Statements.md#Conditional-Compilation-Block)章节中的 Swift 语言版本号的描述,现在可以使用可选的补丁版本号。
* 更新了[函数类型](../chapter3/03_Types.md#function_type)>章节的描述,现在 Swift 区分了采用多参数的函数和采用元组类型的单个参数的函数。
* 从[表达式](../chapter3/04_Expressions.md)章节中删除了动态表达式的部分,现在 `type(of:)` 是 Swift 标准库函数。
### 2016-10-27
* 更新至 Swift 3.0.1。
* 更新[自动引用计数](../chapter2/23_Automatic_Reference_Counting.md)章节中关于 weak 和 unowned 引用的讨论。
* 增加[声明标识符](../chapter3/06_Declarations.md#declaration-modifiers)章节中关于新的标识符 `unowned``unowend(safe)``unowned(unsafe)` 的描述。
* 增加[Any 和 AnyObject 的类型转换](../chapter2/18_Type_Casting.md#type-casting-for-any-and-anyobject)一节中关于使用类型 `Any` 作为可选值的描述。
* 更新[表达式](../chapter3/04_Expressions.md)章节,把括号表达式和元组表达式的描述分开。
### 2016-09-13
* 更新至 Swift 3.0。
* 更新[函数](../chapter2/06_Functions.md)一章和[函数声明](../chapter3/06_Declarations.md#function-declaration)部分关于函数的讨论,在一节中,标明所有函数参数默认都有函数标签。
* 更新[高级操作符](../chapter2/26_Advanced_Operators.md)章节中关于操作符的讨论,现在你可以作为类型函数来实现,替代之前的全局函数实现方式。
* 增加[访问控制](../chapter2/25_Access_Control.md)章节中关于对新的访问级别描述符 `open``fileprivate` 的信息。
* 更新[函数声明](../chapter3/06_Declarations.md#function-declaration)中关于 `inout` 的讨论,注意它现在出现在参数类型的前面,而不是在参数名称的前面。
* 更新[逃逸闭包](../chapter2/07_Closures.md#escaping-closures)和[自动闭包](../chapter2/07_Closures.md#autoclosures)还有[属性](../chapter3/07_Attributes.md)章节中关于 `@noescape``@autoclosure` 的讨论,现在他们是类型属性,而不是定义属性。
* 增加[高级操作符](../chapter2/26_Advanced_Operators.md)一章中[自定义中缀操作符的优先级](./chapter2/26_Advanced_Operators.md#precedence-and-associativity-for-custom-infix-operators)部分和[定义](../chapter3/06_Declarations.md)一章中[优先级组声明](../chapter3/06_Declarations.md#precedence-group-declaration-modifiers)部分中关于操作符优先级组的信息。
* 更新一些讨论:使用 macOS 替换掉 OS X Error 替换掉 ErrorProtocol和更新一些协议名称比如使用 ExpressibleByStringLiteral 替换掉 StringLiteralConvertible。
* 更新[泛型](../chapter2/22_Generics.md)和[泛型形参和实参](../chapter3/09_Generic_Parameters_And_Arguments.md)章节中[泛型 Where 语句](../chapter2/22_Generics.md#extensions-with-a-generic-where-clause)部分,现在泛型的 where 语句写在一个声明的最后。
* 更新[逃逸闭包](../chapter2/07_Closures.md#escaping-closures)一节现在闭包默认为非逃逸的noescaping
* 更新[基础部分](../chapter2/01_TheBasics.md)一章中[可选绑定](../chapter2/01_TheBasics.md#optional-binding)部分和[语句](../chapter3/05_Statements.md)一章中[While 语句](../chapter3/05_Statements.md#while-statement)部分,现在 if`while``guard` 语句使用逗号分隔条件列表,不需要使用 `where` 语句。
* 在[控制流](../chapter2/05_Control_Flow.md)一章的[Switch](../chapter2/05_Control_Flow.md#switch)和[语句](../chapter3/05_Statements.md)一章的[Switch 语句](../chapter3/05_Statements.md#switch-statement)部分中增加关于 switch cases 可以使用多模式的信息。
* 更新[函数类型](../chapter3/03_Types.md#function_type)一节,现在函数参数标签不包含在函数类型中。
* 更新[协议](../chapter2/21_Protocols.md)一章[协议组合](../chapter2/21_Protocols.md#protocol-composition)部分和[类型](../chapter3/03_Types.md)一章[协议组合类型](../chapter3/03_Types.md#protocol-composition)部分关于使用新的 Protocol1 & Protocol2 语法的信息。
* 更新动态类型表达式一节使用新的 `type(of:)` 表达式的信息。
* 更新[行控制表达式](../chapter3/05_Statements.md#line-control-statement)一节使用 `#sourceLocation(file:line:)` 表达式的信息。
* 更新[永不返回函数](../chapter3/06_Declarations.md#functions-that-never-return)一节使用 新的 `Never` 类型的信息。
* 增加[字面量表达式](../chapter3/04_Expressions.md#literal-expression)一节关于 `playground` 字面量的信息。
* 更新[In-Out 参数](../chapter3/06_Declarations.md#in-out_parameters)一节,标明只有非逃逸闭包能捕获 `in-out` 参数。
* 更新[默认参数值](../chapter2/06_Functions.md#default-parameter-values)一节,现在默认参数不能在调用时候重新排序。
* 更新[属性](../chapter3/07_Attributes.md)章节中关于属性参数使用分号的说明。
* 增加[重新抛出函数和方法](../chapter3/06_Declarations.md#rethrowing-functions-and-methods)一节中关于在 catch 代码块中抛出错误的重新抛出函数的信息。
* 增加[Selector 表达式](../chapter3/04_Expressions.md#selector-expression7)一节中关于访问 Objective-C 中 Selector 的 getter 和 setter 的信息。
* 增加[类型别名声明](../chapter3/06_Declarations.md#type-alias-declaration)一节,标明函数类型作为参数类型必须使用括号包裹。
* 增加[函数类型](../chapter3/03_Types.md#function_type)一节中关于泛型类型别名和在协议内使用类型别名的信息。
* 更新[属性](../chapter3/07_Attributes.md)章节,标明 `@IBAction``@IBOutlet``@NSManaged` 隐式含有 `@objc` 属性。
* 增加[声明属性](../chapter3/07_Attributes.md#Ideclaration-attributes)一节中关于 `@GKInspectable` 的信息。
* 更新[可选协议要求](../chapter2/21_Protocols.md#optional-protocol-requirements)一节中关于只能在与 `Objective-C` 交互的代码中才能使用可选协议要求的信息。
* 删除[函数声明](../chapter3/06_Declarations.md#function-declaration)一节中关于显式使用 `let` 关键字作为函数参数的信息。
* 删除[语句](../chapter3/05_Statements.md)一节中关于 `Boolean` 协议的信息, 现在这个协议已经被 Swift 标准库删除。
* 更正[声明属性](../chapter3/07_Attributes.md#Ideclaration-attributes)一节中关于 `@NSApplicationMain` 协议的信息。
### 2016-03-21
* 更新至 Swift 2.2。
* 增加了[编译配置语句](../chapter3/05_Statements.md#Conditional-Compilation-Block)一节中关于如何根据 Swift 版本进行条件编译。
* 增加了[显示成员表达式](../chapter3/04_Expressions.md#explicit-member-expression)一节中关于如何区分只有参数名不同的方法和构造器的信息。
* 增加了[选择器表达式](../chapter3/04_Expressions.md#selector-expression7)一节中针对 Objective-C 选择器的 `#selector` 语法。
* 更新了[关联类型](../chapter2/22_Generics.md#associated-types)和[协议关联类型声明](../chapter3/06_Declarations.md#protocol_associated_type_declaration)中的关于使用 `associatedtype` 关键词修饰关联类型的讨论。
* 更新了[可失败构造器](../chapter2/14_Initialization.md#failable-initializers)一节中关于当构造器在实例完全初始化之前返回 `nil` 的相关信息。
* 增加了[比较运算符](../chapter2/BasicOperators.md#comparison-operators)一节中关于比较元组的信息。
* 增加了[关键字和标点符号](../chapter3/02_Lexical_Structure.md#keywords-and-punctuation)一节中关于使用关键字作为外部参数名的信息。
* 增加了[声明特性](../chapter3/07_Attributes.md#Ideclaration-attributes)一节中关于 `@objc` 特性的讨论,并指出枚举和枚举用例。
* 增加了[操作符](../chapter3/02_Lexical_Structure.md#operator)一节中对于自定义运算符的讨论包含了 `.`
* 增加了[重新抛出错误的函数和方法](../chapter3/06_Declarations.md#rethrowing-functions-and-methods)一节中关于重新抛出错误函数不能直接抛出错误的笔记。
* 增加了[属性观察器](../chapter2/10_Properties.md#property-observers)一节中关于当作为 in-out 参数传递属性时,属性观察器的调用行为。
* 增加了[Swift 初见](./03_a_swift_tour.md)一节中关于错误处理的内容。
* 更新了[弱引用](../chapter2/23_Automatic_Reference_Counting.md#weak-references)一节中的图片用以更清楚的展示重新分配过程。
* 删除了 C 语言风格的 `for` 循环,`++` 前缀和后缀运算符,以及 `--` 前缀和后缀运算符。
* 删除了对变量函数参数和柯里化函数的特殊语法的讨论。
### 2015-10-20
* 更新至 Swift 2.1。
* 更新了[字符串插值](../chapter2/03_Strings_And_Characters.md#string-interpolation)和[字符串字面量](../chapter3/02_Lexical_Structure.md#string-literal)小节,现在字符串插值可包含字符串字面量。
* 增加了在[逃逸闭包](../chapter2/07_Closures.md#escaping-closures)一节中关于 `@noescape` 属性的相关内容。
* 更新了[声明特性]((../chapter3/07_Attributes.md#Ideclaration-attributes)和[编译配置语句](../chapter3/05_Statements.md#Conditional-Compilation-Block)小节中与 tvOS 相关的信息。
* 增加了[In-Out 参数](../chapter3/06_Declarations.md#in-out_parameters)小节中与 in-out 参数行为相关的信息。
* 增加了在[捕获列表](../chapter3/04_Expressions.md#capture-lists)一节中关于指定闭包捕获列表被捕获时捕获值的相关内容。
* 更新了使用[可选链式调用访问属性](../chapter2/16_Optional_Chaining.md#accessing-properties-through-optional-chaining)一节,阐明了如何通过可选链式调用进行赋值。
* 改进了[自动闭包](../chapter2/07_Closures.md#autoclosures)一节中对自闭包的讨论。
* 在[Swift 初见](./03_a_swift_tour.md)一节中更新了一个使用 `??` 操作符的例子。
### 2015-09-16
* 更新至 Swift 2.0。
* 在[错误处理](../chapter2/17_Error_Handling.md)一章中增加了关于错误处理的相关内容,包括[Do 语句](../chapter3/05_Statements.md#do-statement)、[Throw 语句](../chapter3/05_Statements.md#throw-statement)、[Defer 语句](../chapter3/05_Statements.md##defer-statements)以及[try 运算符](../chapter3/04_Expressions.md#try-operator)。
* 更新了[错误表示和抛出](../chapter2/17_Error_Handling.md#representing-and-throwing-errors)一节,现在所有类型都可以遵循 `ErrorType` 协议了。
* 在[将错误装换成可选值](../chapter2/17_Error_Handling.md#converting_errors_to_optional_values)一节增加了 `try` 关键字相关信息。
* 在[枚举](../chapter2/08_Enumerations.md)一章的[递归枚举](../chapter2/08_Enumerations.md#recursive-enumerations)部分以及以及[声明](../chapter3/06_Declarations.md)一章的[任意类型用例的枚举](../chapter3/06_Declarations.md#enumerations-with-cases-of-any-type)一节中新增了递归枚举相关信息。
* 在[控制流](../chapter2/05_Control_Flow.md)一章的[API 可用性检查](../chapter2/05_Control_Flow.md#checking-api-availability)一节和[语句](../chapter3/05_Statements.md)一章的[可用性条件一节](../chapter3/05_Statements.md#availability-condition)中增加了关于 API 可用性检查相关的内容。
* 在[控制流](../chapter2/05_Control_Flow.md)一章的[尽早退出](../chapter2/05_Control_Flow.md#early-exit)一节和[语句](../chapter3/05_Statements.md)一章的[Guard 语句](../chapter3/05_Statements.md#guard-statement)部分新增了与 `guard` 语句相关的内容。
* 在[协议](../chapter2/21_Protocols.md)一章中[协议扩展](../chapter2/21_Protocols.md#protocol-extensions)一节中新增了关于协议扩展的内容。
* 在[访问控制](../chapter2/25_Access_Control.md)一章的[单元测试 target 的访问级别](../chapter2/25_Access_Control.md#access-levels-for-unit-test-targets)一节中新增了关于单元测试访问控制相关的内容。
* 在[模式](../chapter3/08_Patterns.md)一章的[可选模式](../chapter3/08_Patterns.md#optional-pattern)一节中增加了可选模式相关内容。
* 更新了[Repeat-While](../chapter2/05_Control_Flow.md#repeat-while)一节中关于 `repeat-while` 循环相关的内容。
* 更新了[字符串和字符](../chapter2/03_Strings_And_Characters.md)一章,现在 `String` 类型在 Swift 标准库中不再遵循 `CollectionType` 协议。
* 在[常量与变量打印](../chapter2/01_TheBasics.md#printing)一节中增加了新 Swift 标准库中关于 `print(_:separator:terminator)` 相关内容。
* 在[枚举](../chapter2/08_Enumerations.md)一章中的[原始值的隐式赋值](../chapter2/08_Enumerations.md#implicitly-assigned-raw-values)一节和[声明](../chapter3/06_Declarations.md)一章的[包含原始值类型的枚举](../chapter3/06_Declarations.md#enumerations-with-cases-of-a-raw-value-type)一节中增加了关于包含 `String` 原始值的枚举用例的行为相关内容。
* 在[自动闭包](../chapter2/07_Closures.md#autoclosures)一节中增加了关于 `@autoclosure` 特性的相关信息,包括它的 `@autoclosure(escaping)` 形式。
* 更新了[声明特性](../chapter3/07_Attributes.md#Ideclaration-attributes)一节中关于 `@avaliable``warn_unused_result` 特性的相关内容。
* 更新了[类型特性](../chapter3/07_Attributes.md#type-attributes)一节中关于 `@convention` 特性的相关信息。
* 在[可选绑定](../chapter2/01_TheBasics.md#optional-binding)一节中增加了关于使用 `where` 子句进行多可选绑定的相关内容。
* 在[字符串字面量](../chapter3/02_Lexical_Structure.md#string-literal)一节中新增了关于在编译时使用 `+` 运算符拼接字符串字面量的相关信息。
* 在[元类型](../chapter3/03_Types.md#metatype-type)一节中新增了关于元类型值的比较和使用它们通过构造器表达式构造实例相关内容。
* 在[断言调试](../chapter2/01_TheBasics.md#debugging-with-assertions)一节中新增了关于用户定义断言何时会失效的注释内容。
* 更新了[声明特性](../chapter3/07_Attributes.md#Ideclaration-attributes)一节中对 `@NSManaged` 特性的讨论,现在这个特性可以被应用到一个确定实例方法。
* 更新了[可变参数](../chapter2/06_Functions.md#variadic-parameters)一节,现在可变参数可以声明在函数参数列表的任意位置中。
* 在[重写可失败构造器](../chapter2/14_Initialization.md#overriding-a-failable-initializer)一节中新增了关于非可失败构造器相当于一个可失败构造器通过父类构造器的结果进行强制拆包的相关内容。
* 在[任意类型用例的枚举](../chapter3/06_Declarations.md#enumerations-with-cases-of-any-type)一节中增加了关于枚举用例作为函数的内容。
* 在[构造器表达式](../chapter3/04_Expressions.md#initializer-expression)一节中新增关于显式引用一个构造器相关内容。
* 在[编译控制语句](../chapter3/05_Statements.md#compiler-control-statements)一节中新增了关于编译信息以及行控制语句相关信息。
* 在[元类型](../chapter3/03_Types.md#metatype-type)一节中新增了关于如何从元类型值中构造类实例相关内容。
* 在[弱引用](../chapter2/23_Automatic_Reference_Counting.md#weak-references)一节中关于弱引用作为缓存所存在的不足的注释说明。
* 更新了[类型特性](../chapter2/10_Properties.md#type-properties)一节,提到了存储型特性其实是懒加载。
* 更新了[捕获类型](../chapter2/07_Closures.md#capturing_values)一节,阐明了变量和常量在闭包中如何被捕获。
* 更新了[声明特性](../chapter3/07_Attributes.md#Ideclaration-attributes)一节,用以描述何时在类中使用 `@objc` 关键字。
* 在[错误处理](../chapter2/17_Error_Handling.md#handling-errors)一节中增加了关于执行 `throw` 语句的性能的讨论。在[Do 语句](../chapter3/05_Statements.md#do-statement)一节的 do 语句部分也增加了类似内容。
* 更新了[类型特性](../chapter2/10_Properties.md#type-properties)一节中关于类、结构体和枚举的存储型和计算型特性相关的内容。
* 更新了[Break 语句](../chapter3/05_Statements.md#break_statement)一节中关于带标签的 break 语句相关内容。
* 在[属性观察器](../chapter2/10_Properties.md#property-observers)一节中更新了一处注释,用来明确 `willSet``didSet` 观察器的行为。
* 在[访问级别](../chapter2/25_Access_Control.md#access-levels)一节中新增了关于 `private` 作用域的相关信息说明。
* 在[弱引用](../chapter2/23_Automatic_Reference_Counting.md#weak-references)一节中增加了关于弱应用在垃圾回收系统和 ARC 之间的区别的说明。
* 更新了[字符串字面量中特殊字符](../chapter2/03_Strings_And_Characters.md#special-characters-in-string-literals)一节中对 Unicode 标量更精确的定义。
### 2015-4-8
* 更新至 Swift 1.2。
* Swift 现在自身提供了一个 `Set` 集合类型,更多信息请看[Sets](../chapter2/CollectionTypes.md#sets)。
* `@autoclosure` 现在是一个参数声明的属性,而不是参数类型的属性。这里还有一个新的参数声明属性 `@noescape`。更多信息,请看[属性声明](../chapter3/07_Attributes.md#Ideclaration-attributes)。
* 对于类型属性和方法现在可以使用 `static` 关键字作为声明描述符,更多信息,请看[类型变量属性](../chapter3/06_Declarations.md#type-variable-properties)。
* Swift 现在包含一个 `as?``as!` 的向下可失败类型转换运算符。更多信息,请看[协议遵循性检查](../chapter2/21_Protocols.md#checking-for-protocol-conformance)。
* 增加了一个关于[字符串索引](../chapter2/03_Strings_And_Characters.md#string-indices)的新指导章节。
* 从[溢出运算符](../chapter2/26_Advanced_Operators.md#overflow-operators)一节中移除了溢出除运算符(&/)和求余溢出运算符(&%)。
* 更新了常量和常量属性在声明和构造时的规则,更多信息,请看[常量声明](../chapter3/06_Declarations.md#constant-declaration)。
* 更新了字符串字面量中 Unicode 标量集的定义,请看[字符串字面量中的特殊字符](../chapter2/03_Strings_And_Characters.md#special-characters-in-string-literals)。
* 更新了[区间运算符](../chapter2/BasicOperators.md#range-operators)章节来提示当半开区间运算符含有相同的起止索引时,其区间为空。
* 更新了[闭包引用类型](../chapter2/07_Closures.md#closures-are-reference-types)章节来阐明对于变量的捕获规则。
* 更新了[值溢出](../chapter2/26_Advanced_Operators.md#value-overflow)章节堆有符号整数和无符号整数的溢出行为进行了阐明。
* 更新了[协议声明](../chapter3/06_Declarations.md#protocol-declaration)章节对协议声明时的作用域和成员等内容进行了阐明。
* 更新了[捕获列表](../chapter2/23_Automatic_Reference_Counting.md#defining-a-capture-list)章节对于闭包捕获列表中的弱引用和无主引用的使用语法进行了阐明。
* 更新了[运算符](../chapter3/02_Lexical_Structure.md#operator)章节明确指明一些例子来说明自定义运算符所支持的特性如数学运算符各种符号Unicode 符号块等。
* 在函数作用域中的常量声明时可以不被初始化,它必须在第一次使用前被赋值。更多的信息,请看[常量声明](../chapter3/06_Declarations.md#constant-declaration)。
* 在构造器中,常量属性有且仅能被赋值一次。更多信息,请看[在构造过程中给常量属性赋值](../chapter2/14_Initialization.md{#assigning-constant-properties-during-initialization)。
* 多个可选绑定现在可以在`if`语句后面以逗号分隔的赋值列表的方式出现,更多信息,请看[可选绑定](../chapter2/01_TheBasics.md#optional-binding)。
* 一个[可选链表达式](../chapter3/04_Expressions.md#optional-chaining-expression)必须出现在后缀表达式中。
* 协议类型转换不再局限于 `@obj` 修饰的协议了。
* 在运行时可能会失败的类型转换可以使用 `as?``as!` 运算符,而确保不会失败的类型转换现在使用 `as` 运算符。更多信息,请看[类型转换运算符](../chapter3/04_Expressions.md#type-casting-operator)。
### 2014-10-16
* 更新至 Swift 1.1。
* 增加了关于[失败构造器](../chapter2/14_Initialization.md#failable-initializers)的完整章节。
* 增加了协议中关于[失败构造器要求](../chapter2/21_Protocols.md#failable-initializer-requirements)的描述。
* 常量和变量的 `Any` 类型现可以包含函数实例。更新了关于 `Any` 相关的示例来展示如何在 `switch` 语句中如何检查并转换到一个函数类型。
* 带有原始值的枚举类型增加了一个 `rawValue` 属性替代 `toRaw()` 方法,同时使用了一个以 `rawValue` 为参数的失败构造器来替代 `fromRaw()` 方法。更多的信息,请看[原始值](../chapter2/08_Enumerations.md#raw-values)和[带原始值的枚举类型](../chapter3/06_Declarations.md#enumerations-with-cases-of-a-raw-value-type)部分。
* 添加了一个关于 [Failable Initializer](../chapter3/06_Declarations.md#failable-initializers) 的新参考章节,它可以触发初始化失败。
* 自定义运算符现在可以包含 `?` 字符,更新[运算符](../chapter3/02_Lexical_Structure.md#operator)章节描述了改进后的规则,并且从[自定义运算符](../chapter2/26_Advanced_Operators.md#custom-operators)章节删除了重复的运算符有效字符集合。
### 2014-08-18
* 发布新的文档用以详述 Swift 1.0,苹果公司针对 iOS 和 OS X 应用的全新开发语言。
* 在章节协议中,增加新的小节:[对构造器的规定](../chapter2/21_Protocols.md#initializer-requirements)。
* 在章节协议中,增加新的小节:[类专属协议](../chapter2/21_Protocols.md#class-only-protocol)。
* [断言](../chapter2/01_TheBasics.md#assertions-and-preconditions)现在可以使用字符串内插语法,并删除了文档中有冲突的注释。
* 更新了[连接字符串和字符](../chapter2/03_Strings_And_Characters.md#concatenating-strings-and-characters)小节来说明一个事实,那就是字符串和字符不能再用 `+` 号运算符或者复合加法运算符 `+=` 相互连接,这两种运算符现在只能用于字符串之间相连。请使用 `String` 类型的 `append` 方法在一个字符串的尾部增加单个字符。
* 在[属性申明](../chapter3/07_Attributes.md#Ideclaration-attributes)章节增加了关于 `availability` 特性的一些信息。
* [可选类型](../chapter2/01_TheBasics.md#optionals)若有值时,不再隐式的转换为 `true`,同样,若无值时,也不再隐式的转换为 `false`,这是为了避免在判别 optional `Bool` 的值时产生困惑。 替代的方案是,用`==``!=` 运算符显式地去判断 Optinal 是否是 `nil`,以确认其是否包含值。
* Swift 新增了一个[Nil 合并运算符](../chapter2/BasicOperators.md#nil-coalescing-operator)(`a ?? b`), 该表达式中,如果 Optional `a` 的值存在,则取得它并返回,若 Optional `a``nil`,则返回默认值 `b`
* 更新和扩展[字符串的比较](../chapter2/03_Strings_And_Characters.md#comparing-strings),用以反映和展示'字符串和字符的比较',以及'前缀prefix/后缀postfix比较'都开始基于扩展字符集extended grapheme clusters规范的等价比较。
* 现在,你可以通过下标赋值或者[可选调用链](../chapter2/16_Optional_Chaining.md)中的可变方法和操作符来给属性设值。相应地更新了有关[通过可选链接访问属性](../chapter2/16_Optional_Chaining.md#accessing-properties-through-optional-chaining)的信息,并扩展了[通过可选链接调用方法](../chapter2/16_Optional_Chaining.md#calling-methods-through-optional-chaining)时检查方法调用成功的示例,以显示如何检查属性设置是否成功。
* 在章节可选链中,增加一个新的小节[访问可选类型的下标脚注](../chapter2/16_Optional_Chaining.md#accessing-subscripts-through-optional-chaining)。
* 更新章节[访问和修改数组](../chapter2/CollectionTypes.md#accessing-and-modifying-a-dictionary)以标示:从该版本起,不能再通过 `+=` 运算符给一个数组添加一个新的项。对应的替代方案是,使 `append` 方法,或者通过 `+=` 运算符来添加一个只有一个项的数组。
* 添加了一个提示:在[范围运算符](../chapter2/BasicOperators.md#range-operators)中,比如, `a..b``a..<b` ,起始值 `a` 不能大于结束值 `b`
* 重写了[继承](../chapter2/13_Inheritance.md)这一章删除了本章中关于构造器重写的介绍性报道转而将更多的注意力放到新增的部分——子类的新功能以及如何通过重写overrides修改已有的功能。另外[重写属性的 Getters 和 Setters](../chapter2/13_Inheritance.md#overriding-property-etters-and-setters)中的例子已经被替换为展示如何重写一个 `description` 属性。 (而关于如何在子类的构造器中修改继承属性的默认值的例子,已经被移到[构造过程](../chapter2/14_Initialization.md)这一章。)
* 更新了[构造器的继承与重写](../chapter2/14_Initialization.md#initializer-inheritance-and-overriding)小节以标示: 重写一个特定的构造器必须使用 `override` 修饰符。
* 更新[Required 构造器](../chapter2/14_Initialization.md#required-initializers)小节以标示:`required` 修饰符现在需要出现在所有子类的 required 构造器的声明中,而 required 构造器的实现,现在可以仅从父类自动继承。
* 中置Infix的[运算符函数](../chapter2/26_Advanced_Operators.md#operator-functions)不再需要 `@infix` 属性。
* [前置和后置运算符](../chapter2/26_Advanced_Operators.md#prefix-and-postfix-operators)的 `@prefix``@postfix` 属性,已变更为 `prefix``postfix` 声明修饰符。
* 增加一条注解:当 Prefix 和 postfix 运算符被作用于同一个操作数时,关于[前置和后置运算符](../chapter2/26_Advanced_Operators.md#prefix-and-postfix-operators)的执行顺序。
* 在运算符函数中,[组合赋值运算符](../chapter2/26_Advanced_Operators.md#compound-assignment-operators)不再使用 `@assignment` 属性来定义函数。
* 在这个版本中,在定义[自定义操作符](../chapter2/26_Advanced_Operators.md#custom-operators)时,`修饰符Modifiers的出现顺序发生变化`。比如现在,你该编写 `prefix operator` 而不是 `operator prefix`
* 在[声明修饰符](../chapter3/06_Declarations.md#declaration-modifiers)章节增加关于 `dynamic` 声明修饰符的信息。
* 增加[字面量](../chapter3/02_Lexical_Structure.md#literal)的类型推导内容。
* 为章节 Curried Functions 添加了更多的信息。
* 加入新的章节[权限控制(../chapter2/25_Access_Control.md)。
* 更新了[字符串和字符](../chapter2/03_Strings_And_Characters.md)章节用以表明,在 Swift 中,`Character` 类型现在代表的是扩展字符集extended grapheme cluster中的一个 Unicode为此新增了小节[Extended Grapheme Clusters](../chapter2/03_Strings_And_Characters.md#extended-grapheme-clusters)。同时,为小节[Unicode 标量](../chapter2/03_Strings_And_Characters.md#unicode-scalars-representation)和[字符串比较](../chapter2/03_Strings_And_Characters.md#comparing-strings)增加了更多内容。
* 更新[字符串字面量](../chapter2/03_Strings_And_Characters.md#string-literals)章节在一个字符串中Unicode 标量Unicode scalars`\u{n}`的形式来表示,`n` 是一个最大可以有8位的16进制数。
* `NSString` `length` 属性已被映射到 Swift 的内建 `String`类型。(注意,这两属性的类型是`utf16Count`,而非 `utf16count`)。
* Swift 的内建 `String` 类型不再拥有 `uppercaseString``lowercaseString` 属性。其对应部分在章节[字符串和字符](../chapter2/03_Strings_And_Characters.md)已经被删除,并且各种对应的代码用例也已被更新。
* 加入新的章节[没有外部名的构造器参数](../chapter2/14_Initialization.md#initializer-parameters-without-external-names)。
* 加入新的章节[Required 构造器](../chapter2/14_Initialization.md#required-initializers)。
* 加入新的章节[可选元组返回类型](../chapter2/06_Functions.md#optional-tuple-return-types)。
* 更新了[类型标注](../chapter2/01_TheBasics.md#type-annotations)章节:多个相关变量可以用"类型标注”在同一行中声明为同一类型。
* `@optional`, `@lazy`, `@final`, `@required` 等关键字被更新为 `optional`, `lazy`, `final`, `required` 参见[声明修饰符](../chapter3/06_Declarations.md#declaration-modifiers)。
* 更新整本书中关于 `..<` 的引用,从半闭区间改为了[半开区间](../chapter2/BasicOperators.md#half-open-range-operator)。
* 更新了小节[读取和修改字典](../chapter2/CollectionTypes.md#accessing-and-modifying-a-dictionary) `Dictionary` 现在增加了一个 Boolean 型的属性:`isEmpty`
* 解释了哪些字符(集)可被用来定义[自定义操作符](../chapter2/26_Advanced_Operators.md#custom-operators)。
* `nil` 和布尔运算中的 `true``false` 现在被定义为[字面量](../chapter3/02_Lexical_Structure.md#literal)。
* Swift 中的数组 `Array` 类型从现在起具备了完整的值语义。具体信息被更新到[集合的可变性](../chapter2/CollectionTypes.md#mutability-of-collections)和[数组](../chapter2/CollectionTypes.md#arrays)两小节,以反映这个新的变化。 此外,还解释了如何给 Strings, Arrays 和 Dictionaries 进行赋值和拷贝。
* [数组类型速记语法](../chapter2/CollectionTypes.md#array-type-shorthand-syntax)从 `SomeType[]` 更新为 `[SomeType]`
* 新增关于[字典类型的速记语法](../chapter2/CollectionTypes.md#dictionary-type-shorthand-syntax)的内容,现在书写格式为: `[KeyType: ValueType]`
* 加入新的小节:[字典键类型的哈希值](../chapter2/CollectionTypes.md#hash-values-for-set-types)。
* [闭包表达式](../chapter2/07_Closures.md#closure-expressions)示例中使用新的全局函数 `sorted` 取代原先的全局函数 `sort` 去展示如何返回一个全新的数组。
* 更新关于[结构体逐一成员构造器](../chapter2/14_Initialization.md#memberwise-initializers-for-structure-types)的描述:即使结构体的成员 `没有默认值`,逐一成员构造器也可以自动获得。
* [半开区间运算符](../chapter2/BasicOperators.md#half-open-range-operator)由`..` 改为 `..<`
* 添加一个[泛型拓展](../chapter2/22_Generics.md#extending-a-generic-type)的示例。

View File

@ -1,646 +0,0 @@
# 集合类型
Swift 语言提供 `Arrays``Sets``Dictionaries` 三种基本的*集合类型*用来存储集合数据。数组Arrays是有序数据的集。集合Sets是无序无重复数据的集。字典Dictionaries是无序的键值对的集。
![](https://docs.swift.org/swift-book/_images/CollectionTypes_intro_2x.png)
Swift 语言中的 `Arrays``Sets``Dictionaries` 中存储的数据值类型必须明确。这意味着我们不能把错误的数据类型插入其中。同时这也说明你完全可以对取回值的类型非常放心。
> 注意
>
> Swift 的 `Arrays`、`Sets` 和 `Dictionaries` 类型被实现为*泛型集合*。更多关于泛型类型和集合,参见 [泛型](./23_Generics.md)章节。
## 集合的可变性 {#mutability-of-collections}
如果创建一个 `Arrays``Sets``Dictionaries` 并且把它分配成一个变量,这个集合将会是*可变的*。这意味着你可以在创建之后添加更多或移除已存在的数据项,或者改变集合中的数据项。如果我们把 `Arrays``Sets``Dictionaries` 分配成常量,那么它就是*不可变的*,它的大小和内容都不能被改变。
> 注意
>
> 在我们不需要改变集合的时候创建不可变集合是很好的实践。如此 Swift 编译器可以优化我们创建的集合。
## 数组Arrays {#arrays}
*数组*使用有序列表存储同一类型的多个值。相同的值可以多次出现在一个数组的不同位置中。
> 注意
>
> Swift 的 `Array` 类型被桥接到 `Foundation` 中的 `NSArray` 类。更多关于在 `Foundation` 和 `Cocoa` 中使用 `Array` 的信息,参见 [*Using Swift with Cocoa and Obejective-C(Swift 4.1)*](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。
### 数组的简单语法 {#array-type-shorthand-syntax}
写 Swift 数组应该遵循像 `Array<Element>` 这样的形式,其中 `Element` 是这个数组中唯一允许存在的数据类型。我们也可以使用像 `[Element]` 这样的简单语法。尽管两种形式在功能上是一样的,但是推荐较短的那种,而且在本文中都会使用这种形式来使用数组。
### 创建一个空数组 {#creating-an-empty-array}
我们可以使用构造语法来创建一个由特定数据类型构成的空数组:
```swift
var someInts = [Int]()
print("someInts is of type [Int] with \(someInts.count) items.")
// 打印“someInts is of type [Int] with 0 items.”
```
注意,通过构造函数的类型,`someInts` 的值类型被推断为 `[Int]`
或者,如果代码上下文中已经提供了类型信息,例如一个函数参数或者一个已经定义好类型的常量或者变量,我们可以使用空数组语句创建一个空数组,它的写法很简单:`[]`(一对空方括号):
```swift
someInts.append(3)
// someInts 现在包含一个 Int 值
someInts = []
// someInts 现在是空数组,但是仍然是 [Int] 类型的。
```
### 创建一个带有默认值的数组 {#creating-an-array-with-a-default-value}
Swift 中的 `Array` 类型还提供一个可以创建特定大小并且所有数据都被默认的构造方法。我们可以把准备加入新数组的数据项数量(`count`)和适当类型的初始值(`repeating`)传入数组构造函数:
```swift
var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles 是一种 [Double] 数组,等价于 [0.0, 0.0, 0.0]
```
### 通过两个数组相加创建一个数组 {#creating-an-array-by-adding-two-arrays-together}
我们可以使用加法操作符(`+`)来组合两种已存在的相同类型数组。新数组的数据类型会被从两个数组的数据类型中推断出来:
```swift
var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles 被推断为 [Double],等价于 [2.5, 2.5, 2.5]
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles 被推断为 [Double],等价于 [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
```
### 用数组字面量构造数组 {#creating-an-array-with-an-array-literals}
我们可以使用*数组字面量*来进行数组构造,这是一种用一个或者多个数值构造数组的简单方法。数组字面量是一系列由逗号分割并由方括号包含的数值:
`[value 1, value 2, value 3]`
下面这个例子创建了一个叫做 `shoppingList` 并且存储 `String` 的数组:
```swift
var shoppingList: [String] = ["Eggs", "Milk"]
// shoppingList 已经被构造并且拥有两个初始项。
```
`shoppingList` 变量被声明为“字符串值类型的数组“,记作 `[String]`。 因为这个数组被规定只有 `String` 一种数据结构,所以只有 `String` 类型可以在其中被存取。 在这里,`shoppingList` 数组由两个 `String` 值(`"Eggs"``"Milk"`)构造,并且由数组字面量定义。
> 注意
>
> `shoppingList` 数组被声明为变量(`var` 关键字创建)而不是常量(`let` 创建)是因为以后可能会有更多的数据项被插入其中。
在这个例子中,字面量仅仅包含两个 `String` 值。匹配了该数组的变量声明(只能包含 `String` 的数组),所以这个字面量的分配过程可以作为用两个初始项来构造 `shoppingList` 的一种方式。
由于 Swift 的类型推断机制,当我们用字面量构造只拥有相同类型值数组的时候,我们不必把数组的类型定义清楚。`shoppingList` 的构造也可以这样写:
```swift
var shoppingList = ["Eggs", "Milk"]
```
因为所有数组字面量中的值都是相同的类型Swift 可以推断出 `[String]``shoppingList` 中变量的正确类型。
### 访问和修改数组 {#accessing-and-modifying-an-array}
我们可以通过数组的方法和属性来访问和修改数组,或者使用下标语法。
可以使用数组的只读属性 `count` 来获取数组中的数据项数量:
```swift
print("The shopping list contains \(shoppingList.count) items.")
// 输出“The shopping list contains 2 items.”这个数组有2个项
```
使用布尔属性 `isEmpty` 作为一个缩写形式去检查 `count` 属性是否为 `0`
```swift
if shoppingList.isEmpty {
print("The shopping list is empty.")
} else {
print("The shopping list is not empty.")
}
// 打印“The shopping list is not empty.”shoppinglist 不是空的)
```
也可以使用 `append(_:)` 方法在数组后面添加新的数据项:
```swift
shoppingList.append("Flour")
// shoppingList 现在有3个数据项有人在摊煎饼
```
除此之外,使用加法赋值运算符(`+=`)也可以直接在数组后面添加一个或多个拥有相同类型的数据项:
```swift
shoppingList += ["Baking Powder"]
// shoppingList 现在有四项了
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
// shoppingList 现在有七项了
```
可以直接使用下标语法来获取数组中的数据项,把我们需要的数据项的索引值放在直接放在数组名称的方括号中:
```swift
var firstItem = shoppingList[0]
// 第一项是“Eggs”
```
> 注意
>
> 第一项在数组中的索引值是 `0` 而不是 `1`。 Swift 中的数组索引总是从零开始。
我们也可以用下标来改变某个已有索引值对应的数据值:
```swift
shoppingList[0] = "Six eggs"
// 其中的第一项现在是“Six eggs”而不是“Eggs”
```
还可以利用下标来一次改变一系列数据值,即使新数据和原有数据的数量是不一样的。下面的例子把 `"Chocolate Spread"``"Cheese"``"Butter"` 替换为 `"Bananas"``"Apples"`
```swift
shoppingList[4...6] = ["Bananas", "Apples"]
// shoppingList 现在有6项
```
> 注意
>
> 不可以用下标访问的形式去在数组尾部添加新项。
调用数组的 `insert(_:at:)` 方法来在某个具体索引值之前添加数据项:
```swift
shoppingList.insert("Maple Syrup", at: 0)
// shoppingList 现在有7项
// 现在是这个列表中的第一项是“Maple Syrup”
```
这次 `insert(_:at:)` 方法调用把值为 `"Maple Syrup"` 的新数据项插入列表的最开始位置,并且使用 `0` 作为索引值。
类似的我们可以使用 `remove(at:)` 方法来移除数组中的某一项。这个方法把数组在特定索引值中存储的数据项移除并且返回这个被移除的数据项(我们不需要的时候就可以无视它):
```swift
let mapleSyrup = shoppingList.remove(at: 0)
// 索引值为0的数据项被移除
// shoppingList 现在只有6项而且不包括 Maple Syrup
// mapleSyrup 常量的值等于被移除数据项“Maple Syrup”的值
```
> 注意
>
> 如果我们试着对索引越界的数据进行检索或者设置新值的操作,会引发一个运行期错误。我们可以使用索引值和数组的 `count` 属性进行比较来在使用某个索引之前先检验是否有效。除了当 `count` 等于 0 时(说明这是个空数组),最大索引值一直是 `count - 1`,因为数组都是零起索引。
数据项被移除后数组中的空出项会被自动填补,所以现在索引值为 `0` 的数据项的值再次等于 `"Six eggs"`
```swift
firstItem = shoppingList[0]
// firstItem 现在等于“Six eggs”
```
如果我们只想把数组中的最后一项移除,可以使用 `removeLast()` 方法而不是 `remove(at:)` 方法来避免我们需要获取数组的 `count` 属性。就像后者一样,前者也会返回被移除的数据项:
```swift
let apples = shoppingList.removeLast()
// 数组的最后一项被移除了
// shoppingList 现在只有5项不包括 Apples
// apples 常量的值现在等于“Apples”字符串
```
### 数组的遍历 {#iterating-over-an-array}
我们可以使用 `for-in` 循环来遍历所有数组中的数据项:
```swift
for item in shoppingList {
print(item)
}
// Six eggs
// Milk
// Flour
// Baking Powder
// Bananas
```
如果我们同时需要每个数据项的值和索引值,可以使用 `enumerated()` 方法来进行数组遍历。`enumerated()` 返回一个由每一个数据项索引值和数据值组成的元组。我们可以把这个元组分解成临时常量或者变量来进行遍历:
```swift
for (index, value) in shoppingList.enumerated() {
print("Item \(String(index + 1)): \(value)")
}
// Item 1: Six eggs
// Item 2: Milk
// Item 3: Flour
// Item 4: Baking Powder
// Item 5: Bananas
```
更多关于 `for-in` 循环的介绍请参见[for 循环](05_Control_Flow.html#for_loops)。
## 集合Sets {#sets}
*集合Set*用来存储相同类型并且没有确定顺序的值。当集合元素顺序不重要时或者希望确保每个元素只出现一次时可以使用集合而不是数组。
> 注意
> Swift 的 `Set` 类型被桥接到 `Foundation` 中的 `NSSet` 类。
>
> 关于使用 `Foundation` 和 `Cocoa` 中 `Set` 的知识,参见 [*Using Swift with Cocoa and Obejective-C(Swift 4.1)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。
### 集合类型的哈希值 {#hash-values-for-set-types}
一个类型为了存储在集合中,该类型必须是*可哈希化*的——也就是说,该类型必须提供一个方法来计算它的*哈希值*。一个哈希值是 `Int` 类型的,相等的对象哈希值必须相同,比如 `a==b`,因此必须 `a.hashValue == b.hashValue`
Swift 的所有基本类型(比如 `String``Int``Double``Bool`)默认都是可哈希化的,可以作为集合的值的类型或者字典的键的类型。没有关联值的枚举成员值(在[枚举](./08_Enumerations.md)有讲述)默认也是可哈希化的。
> 注意
>
> 你可以使用你自定义的类型作为集合的值的类型或者是字典的键的类型,但你需要使你的自定义类型遵循 Swift 标准库中的 `Hashable` 协议。遵循 `Hashable` 协议的类型需要提供一个类型为 `Int` 的可读属性 `hashValue`。由类型的 `hashValue` 属性返回的值不需要在同一程序的不同执行周期或者不同程序之间保持相同。
>
> 因为 `Hashable` 协议遵循 `Equatable` 协议,所以遵循该协议的类型也必须提供一个“是否相等”运算符(`==`)的实现。这个 `Equatable` 协议要求任何遵循 `==` 实现的实例间都是一种相等的关系。也就是说,对于 `a,b,c` 三个值来说,`==` 的实现必须满足下面三种情况:
> * `a == a`(自反性)
> * `a == b` 意味着 `b == a`(对称性)
> * `a == b && b == c` 意味着 `a == c`(传递性)
关于遵循协议的更多信息,请看[协议](./22_Protocols.md)。
### 集合类型语法 {#set-type-syntax}
Swift 中的 `Set` 类型被写为 `Set<Element>`,这里的 `Element` 表示 `Set` 中允许存储的类型,和数组不同的是,集合没有等价的简化形式。
### 创建和构造一个空的集合 {#creating-and-initalizing-an-empty-set}
你可以通过构造器语法创建一个特定类型的空集合:
```swift
var letters = Set<Character>()
print("letters is of type Set<Character> with \(letters.count) items.")
// 打印“letters is of type Set<Character> with 0 items.”
```
> 注意
>
> 通过构造器,这里的 `letters` 变量的类型被推断为 `Set<Character>`。
此外,如果上下文提供了类型信息,比如作为函数的参数或者已知类型的变量或常量,我们可以通过一个空的数组字面量创建一个空的 `Set`
```swift
letters.insert("a")
// letters 现在含有1个 Character 类型的值
letters = []
// letters 现在是一个空的 Set但是它依然是 Set<Character> 类型
```
### 用数组字面量创建集合 {#creating-a-set-with-an-array-literal}
你可以使用数组字面量来构造集合,并且可以使用简化形式写一个或者多个值作为集合元素。
下面的例子创建一个称之为 `favoriteGenres` 的集合来存储 `String` 类型的值:
```swift
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// favoriteGenres 被构造成含有三个初始值的集合
```
这个 `favoriteGenres` 变量被声明为“一个 `String` 值的集合”,写为 `Set<String>`。由于这个特定的集合含有指定 `String` 类型的值,所以它只允许存储 `String` 类型值。这里的 `favoriteGenres` 变量有三个 `String` 类型的初始值(`"Rock"``"Classical"``"Hip hop"`),并以数组字面量的方式出现。
> 注意
>
> `favoriteGenres` 被声明为一个变量(拥有 `var` 标示符)而不是一个常量(拥有 `let` 标示符),因为它里面的元素将会在下面的例子中被增加或者移除。
一个 `Set` 类型不能从数组字面量中被单独推断出来,因此 `Set` 类型必须显式声明。然而,由于 Swift 的类型推断功能,如果你想使用一个数组字面量构造一个 `Set` 并且该数组字面量中的所有元素类型相同,那么你无须写出 `Set` 的具体类型。`favoriteGenres` 的构造形式可以采用简化的方式代替:
```swift
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
```
由于数组字面量中的所有元素类型相同Swift 可以推断出 `Set<String>` 作为 `favoriteGenres` 变量的正确类型。
### 访问和修改一个集合 {#accesing-and-modifying-a-set}
你可以通过 `Set` 的属性和方法来访问和修改一个 `Set`
为了找出一个 `Set` 中元素的数量,可以使用其只读属性 `count`
```swift
print("I have \(favoriteGenres.count) favorite music genres.")
// 打印“I have 3 favorite music genres.”
```
使用布尔属性 `isEmpty` 作为一个缩写形式去检查 `count` 属性是否为 `0`
```swift
if favoriteGenres.isEmpty {
print("As far as music goes, I'm not picky.")
} else {
print("I have particular music preferences.")
}
// 打印“I have particular music preferences.”
```
你可以通过调用 `Set``insert(_:)` 方法来添加一个新元素:
```swift
favoriteGenres.insert("Jazz")
// favoriteGenres 现在包含4个元素
```
你可以通过调用 `Set``remove(_:)` 方法去删除一个元素,如果该值是该 `Set` 的一个元素则删除该元素并且返回被删除的元素值,否则如果该 `Set` 不包含该值,则返回 `nil`。另外,`Set` 中的所有元素可以通过它的 `removeAll()` 方法删除。
```swift
if let removedGenre = favoriteGenres.remove("Rock") {
print("\(removedGenre)? I'm over it.")
} else {
print("I never much cared for that.")
}
// 打印“Rock? I'm over it.”
```
使用 `contains(_:)` 方法去检查 `Set` 中是否包含一个特定的值:
```swift
if favoriteGenres.contains("Funk") {
print("I get up on the good foot.")
} else {
print("It's too funky in here.")
}
// 打印“It's too funky in here.”
```
### 遍历一个集合 {#iterating-over-a-set}
你可以在一个 `for-in` 循环中遍历一个 `Set` 中的所有值。
```swift
for genre in favoriteGenres {
print("\(genre)")
}
// Classical
// Jazz
// Hip hop
```
更多关于 `for-in` 循环的信息,参见[For 循环](./05_Control_Flow.md#for_loops)。
Swift 的 `Set` 类型没有确定的顺序,为了按照特定顺序来遍历一个 `Set` 中的值可以使用 `sorted()` 方法,它将返回一个有序数组,这个数组的元素排列顺序由操作符'<'对元素进行比较的结果来确定。
```swift
for genre in favoriteGenres.sorted() {
print("\(genre)")
}
// Classical
// Hip hop
// Jazz
```
## 集合操作 {#performing-set-operations}
你可以高效地完成 `Set` 的一些基本操作,比如把两个集合组合到一起,判断两个集合共有元素,或者判断两个集合是否全包含,部分包含或者不相交。
### 基本集合操作 {#fundamental-set-operations}
下面的插图描述了两个集合 `a``b`,以及通过阴影部分的区域显示集合各种操作的结果。
![](https://docs.swift.org/swift-book/_images/setVennDiagram_2x.png)
* 使用 `intersection(_:)` 方法根据两个集合中都包含的值创建的一个新的集合。
* 使用 `symmetricDifference(_:)` 方法根据在一个集合中但不在两个集合中的值创建一个新的集合。
* 使用 `union(_:)` 方法根据两个集合的值创建一个新的集合。
* 使用 `subtracting(_:)` 方法根据不在该集合中的值创建一个新的集合。
```swift
let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersection(evenDigits).sorted()
// []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9]
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]
```
### 集合成员关系和相等 {#set-membership-and-equality}
下面的插图描述了三个集合 `a``b``c`,以及通过重叠区域表述集合间共享的元素。集合 `a` 是集合 `b` 的父集合,因为 `a` 包含了 `b` 中所有的元素,相反的,集合 `b` 是集合 `a` 的子集合,因为属于 `b` 的元素也被 `a` 包含。集合 `b` 和集合 `c` 彼此不关联,因为它们之间没有共同的元素。
![](https://docs.swift.org/swift-book/_images/setEulerDiagram_2x.png)
* 使用“是否相等”运算符(`==`)来判断两个集合是否包含全部相同的值。
* 使用 `isSubset(of:)` 方法来判断一个集合中的值是否也被包含在另外一个集合中。
* 使用 `isSuperset(of:)` 方法来判断一个集合中包含另一个集合中所有的值。
* 使用 `isStrictSubset(of:)` 或者 `isStrictSuperset(of:)` 方法来判断一个集合是否是另外一个集合的子集合或者父集合并且两个集合并不相等。
* 使用 `isDisjoint(with:)` 方法来判断两个集合是否不含有相同的值(是否没有交集)。
```swift
let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"]
houseAnimals.isSubset(of: farmAnimals)
// true
farmAnimals.isSuperset(of: houseAnimals)
// true
farmAnimals.isDisjoint(with: cityAnimals)
// true
```
## 字典 {#dictionaries}
*字典*是一种存储多个相同类型的值的容器。每个值value都关联唯一的键key键作为字典中的这个值数据的标识符。和数组中的数据项不同字典中的数据项并没有具体顺序。我们在需要通过标识符访问数据的时候使用字典这种方法很大程度上和我们在现实世界中使用字典查字义的方法一样。
> 注意
>
> Swift 的 `Dictionary` 类型被桥接到 `Foundation` 的 `NSDictionary` 类。
>
> 更多关于在 `Foundation` 和 `Cocoa` 中使用 `Dictionary` 类型的信息,参见 [*Using Swift with Cocoa and Obejective-C(Swift 4.1)*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/index.html#//apple_ref/doc/uid/TP40014216) 中[使用 Cocoa 数据类型](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html#//apple_ref/doc/uid/TP40014216-CH6)部分。
### 字典类型简化语法 {#dictionary-type-shorthand-syntax}
Swift 的字典使用 `Dictionary<Key, Value>` 定义,其中 `Key` 是字典中键的数据类型,`Value` 是字典中对应于这些键所存储值的数据类型。
> 注意
>
> 一个字典的 `Key` 类型必须遵循 `Hashable` 协议,就像 `Set` 的值类型。
我们也可以用 `[Key: Value]` 这样简化的形式去创建一个字典类型。虽然这两种形式功能上相同,但是后者是首选,并且这本指导书涉及到字典类型时通篇采用后者。
### 创建一个空字典 {#creating-an-empty-dictionary}
我们可以像数组一样使用构造语法创建一个拥有确定类型的空字典:
```swift
var namesOfIntegers = [Int: String]()
// namesOfIntegers 是一个空的 [Int: String] 字典
```
这个例子创建了一个 `[Int: String]` 类型的空字典来储存整数的英语命名。它的键是 `Int` 型,值是 `String` 型。
如果上下文已经提供了类型信息,我们可以使用空字典字面量来创建一个空字典,记作 `[:]`(中括号中放一个冒号):
```swift
namesOfIntegers[16] = "sixteen"
// namesOfIntegers 现在包含一个键值对
namesOfIntegers = [:]
// namesOfIntegers 又成为了一个 [Int: String] 类型的空字典
```
### 用字典字面量创建字典 {#creating-a-dictionary-with-a-dictionary-literal}
我们可以使用*字典字面量*来构造字典,这和我们刚才介绍过的数组字面量拥有相似语法。字典字面量是一种将一个或多个键值对写作 `Dictionary` 集合的快捷途径。
一个键值对是一个 `key` 和一个 `value` 的结合体。在字典字面量中,每一个键值对的键和值都由冒号分割。这些键值对构成一个列表,其中这些键值对由方括号包含、由逗号分割:
```swift
[key 1: value 1, key 2: value 2, key 3: value 3]
```
下面的例子创建了一个存储国际机场名称的字典。在这个字典中键是三个字母的国际航空运输相关代码,值是机场名称:
```swift
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
```
`airports` 字典被声明为一种 `[String: String]` 类型,这意味着这个字典的键和值都是 `String` 类型。
> 注意
>
> `airports` 字典被声明为变量(用 `var` 关键字)而不是常量(`let` 关键字)因为后来更多的机场信息会被添加到这个示例字典中。
`airports` 字典使用字典字面量初始化,包含两个键值对。第一对的键是 `YYZ`,值是 `Toronto Pearson`。第二对的键是 `DUB`,值是 `Dublin`
这个字典语句包含了两个 `String: String` 类型的键值对。它们对应 `airports` 变量声明的类型(一个只有 `String` 键和 `String` 值的字典)所以这个字典字面量的任务是构造拥有两个初始数据项的 `airport` 字典。
和数组一样,我们在用字典字面量构造字典时,如果它的键和值都有各自一致的类型,那么就不必写出字典的类型。
`airports` 字典也可以用这种简短方式定义:
```swift
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
```
因为这个语句中所有的键和值都各自拥有相同的数据类型Swift 可以推断出 `Dictionary<String, String>``airports` 字典的正确类型。
### 访问和修改字典 {#accessing-and-modifying-a-dictionary}
我们可以通过字典的方法和属性来访问和修改字典,或者通过使用下标语法。
和数组一样,我们可以通过字典的只读属性 `count` 来获取某个字典的数据项数量:
```swift
print("The dictionary of airports contains \(airports.count) items.")
// 打印“The dictionary of airports contains 2 items.”(这个字典有两个数据项)
```
使用布尔属性 `isEmpty` 作为一个缩写形式去检查 `count` 属性是否为 `0`
```swift
if airports.isEmpty {
print("The airports dictionary is empty.")
} else {
print("The airports dictionary is not empty.")
}
// 打印“The airports dictionary is not empty.”
```
我们也可以在字典中使用下标语法来添加新的数据项。可以使用一个恰当类型的键作为下标索引,并且分配恰当类型的新值:
```swift
airports["LHR"] = "London"
// airports 字典现在有三个数据项
```
我们也可以使用下标语法来改变特定键对应的值:
```swift
airports["LHR"] = "London Heathrow"
// “LHR”对应的值被改为“London Heathrow”
```
作为另一种下标方法,字典的 `updateValue(_:forKey:)` 方法可以设置或者更新特定键对应的值。就像上面所示的下标示例,`updateValue(_:forKey:)` 方法在这个键不存在对应值的时候会设置新值或者在存在时更新已存在的值。和上面的下标方法不同的,`updateValue(_:forKey:)` 这个方法返回更新值之前的原值。这样使得我们可以检查更新是否成功。
`updateValue(_:forKey:)` 方法会返回对应值的类型的可选值。举例来说:对于存储 `String` 值的字典,这个函数会返回一个 `String?` 或者“可选 `String`”类型的值。
如果有值存在于更新前,则这个可选值包含了旧值,否则它将会是 `nil`
```swift
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
print("The old value for DUB was \(oldValue).")
}
// 输出“The old value for DUB was Dublin.”
```
我们也可以使用下标语法来在字典中检索特定键对应的值。因为有可能请求的键没有对应的值存在,字典的下标访问会返回对应值的类型的可选值。如果这个字典包含请求键所对应的值,下标会返回一个包含这个存在值的可选值,否则将返回 `nil`
```swift
if let airportName = airports["DUB"] {
print("The name of the airport is \(airportName).")
} else {
print("That airport is not in the airports dictionary.")
}
// 打印“The name of the airport is Dublin Airport.”
```
我们还可以使用下标语法来通过给某个键的对应值赋值为 `nil` 来从字典里移除一个键值对:
```swift
airports["APL"] = "Apple Internation"
// “Apple Internation”不是真的 APL 机场,删除它
airports["APL"] = nil
// APL 现在被移除了
```
此外,`removeValue(forKey:)` 方法也可以用来在字典中移除键值对。这个方法在键值对存在的情况下会移除该键值对并且返回被移除的值或者在没有值的情况下返回 `nil`
```swift
if let removedValue = airports.removeValue(forKey: "DUB") {
print("The removed airport's name is \(removedValue).")
} else {
print("The airports dictionary does not contain a value for DUB.")
}
// 打印“The removed airport's name is Dublin Airport.”
```
### 字典遍历 {#iterating-over-a-dictionary}
我们可以使用 `for-in` 循环来遍历某个字典中的键值对。每一个字典中的数据项都以 `(key, value)` 元组形式返回,并且我们可以使用临时常量或者变量来分解这些元组:
```swift
for (airportCode, airportName) in airports {
print("\(airportCode): \(airportName)")
}
// YYZ: Toronto Pearson
// LHR: London Heathrow
```
更多关于 `for-in` 循环的信息,参见[For 循环](./05_Control_Flow.md#for_loops)。
通过访问 `keys` 或者 `values` 属性,我们也可以遍历字典的键或者值:
```swift
for airportCode in airports.keys {
print("Airport code: \(airportCode)")
}
// Airport code: YYZ
// Airport code: LHR
for airportName in airports.values {
print("Airport name: \(airportName)")
}
// Airport name: Toronto Pearson
// Airport name: London Heathrow
```
如果我们只是需要使用某个字典的键集合或者值集合来作为某个接受 `Array` 实例的 API 的参数,可以直接使用 `keys` 或者 `values` 属性构造一个新数组:
```swift
let airportCodes = [String](airports.keys)
// airportCodes 是 ["YYZ", "LHR"]
let airportNames = [String](airports.values)
// airportNames 是 ["Toronto Pearson", "London Heathrow"]
```
Swift 的字典类型是无序集合类型。为了以特定的顺序遍历字典的键或值,可以对字典的 `keys``values` 属性使用 `sorted()` 方法。

View File

@ -1,630 +0,0 @@
# 词法结构Lexical Structure
Swift 的*“词法结构lexical structure”* 描述了能构成该语言中有效符号token的字符序列。这些合法符号组成了语言中最底层的构建基块并在之后的章节中用于描述语言的其他部分。一个合法符号由一个标识符identifier、关键字keyword、标点符号punctuation、字面量literal或运算符operator组成。
通常情况下,通过考虑输入文本当中可能的最长子串,并且在随后将介绍的语法约束之下,根据随后将介绍的语法约束生成的,根据 Swift 源文件当中的字符来生成相应的“符号”。这种方法称为*“最长匹配longest match”*,或者*“最大适合maximal munch”*。
## 空白与注释 {#whitespace}
空白whitespace有两个用途分隔源文件中的符号以及帮助区分运算符属于前缀还是后缀参见 [运算符](#operators)在其他情况下空白则会被忽略。以下的字符会被当作空白空格U+0020、换行符U+000A、回车符U+000D、水平制表符U+0009、垂直制表符U+000B、换页符U+000C以及空字符U+0000
注释被编译器当作空白处理。单行注释由 `//` 开始直至遇到换行符U+000A或者回车符U+000D。多行注释由 `/*` 开始,以 `*/` 结束。注释允许嵌套,但注释标记必须匹配。
> 空白语法
>
> *空白* → [*空白项*](#whitespace-item) [*空白*](#whitespace)<sub>可选</sub>
>
#### whitespace-item {#whitespace-item}
>
> *空白项* → [*断行符*](#line-break)
>
> *空白项* → [*注释*](#comment)
>
> *空白项* → [*多行注释*](#multiline-comment)
>
> *空白项* → U+0000U+0009U+000BU+000C 或者 U+0020
>
>
#### line-break {#line-break}
>
> *断行符* → U+000A
>
> *断行符* → U+000D
>
> *断行符* → U+000D 接着是 U+000A
>
>
#### comment {#comment}
>
> *注释* → // [*注释内容*](#comment-text) [断行符*](#line-break)
>
>
#### multiline-comment {#multiline-comment}
>
> *多行注释* → `/*` [*多行注释内容*](#multiline-commnet-text) `*/`
>
>
#### comment-text {#comment-text}
>
> *注释内容* → [*注释内容项*](#comment-text-item) [*注释内容*](#comment-text)<sub>可选</sub>
>
>
#### comment-text-item {#comment-text-item}
>
> *注释内容项* → 任何 Unicode 标量值, 除了 U+000A 或者 U+000D
>
>
#### multiline-commnet-text {#multiline-commnet-text}
>
> *多行注释内容* → [*多行注释内容项*](#multiline-comment-text-item) [*多行注释内容*](#multiline-comment-text)<sub>可选</sub>
>
> *多行注释内容项* → [*多行注释*](#multiline-comment).
>
> *多行注释内容项* → [*注释内容项*](#comment-text-item)
>
> *多行注释内容项* → 任何 Unicode 标量值, 除了 `/*` 或者 `*/`
## 标识符 {#identifiers}
*标识符identifier* 可以由以下的字符开始:大写或小写的字母 `A``Z`、下划线(`_`、基本多文种平面Basic Multilingual Plane中非字符数字组合的 Unicode 字符以及基本多文种平面以外的非个人专用区字符。在首字符之后,允许使用数字和组合 Unicode 字符。
使用保留字作为标识符,需要在其前后增加反引号(`` ` ``)。例如,`class` 不是合法的标识符,但可以使用 `` `class` ``。反引号不属于标识符的一部分,`` `x` `` 和 `x` 表示同一标识符。
闭包中如果没有明确指定参数名称,参数将被隐式命名为 `$0`、`$1`、`$2` 等等。这些命名在闭包作用域范围内是合法的标识符。
> 标识符语法
>
> *标识符* → [*头部标识符*](#identifier-head) [*标识符字符组*](#identifier-characters)<sub>可选</sub>
>
> *标识符* → \`[*头部标识符*](#identifier-head) [*标识符字符组*](#identifier-characters)<sub>可选</sub>\`
>
> *标识符* → [*隐式参数名*](#implicit-parameter-name)
>
> *标识符列表* → [*标识符*](#identifier) | [*标识符*](#identifier) **,** [*标识符列表*](#identifier)
>
>
#### identifier-head {#identifier-head}
>
> *头部标识符* → 大写或小写字母 A - Z
>
> *头部标识符* → _
>
> *头部标识符* → U+00A8U+00AAU+00ADU+00AFU+00B2U+00B5或者 U+00B7U+00BA
>
> *头部标识符* → U+00BCU+00BEU+00C0U+00D6U+00D8U+00F6或者 U+00F8U+00FF
>
> *头部标识符* → U+0100U+02FFU+0370U+167FU+1681U+180D或者 U+180FU+1DBF
>
> *头部标识符* → U+1E00U+1FFF
>
> *头部标识符* → U+200BU+200DU+202AU+202EU+203FU+2040U+2054或者 U+2060U+206F
>
> *头部标识符* → U+2070U+20CFU+2100U+218FU+2460U+24FF或者 U+2776U+2793
>
> *头部标识符* → U+2C00U+2DFF 或者 U+2E80U+2FFF
>
> *头部标识符* → U+3004U+3007U+3021U+302FU+3031U+303F或者 U+3040U+D7FF
>
> *头部标识符* → U+F900U+FD3DU+FD40U+FDCFU+FDF0U+FE1F或者 U+FE30U+FE44
>
> *头部标识符* → U+FE47U+FFFD
>
> *头部标识符* → U+10000U+1FFFDU+20000U+2FFFDU+30000U+3FFFD或者 U+40000U+4FFFD
>
> *头部标识符* → U+50000U+5FFFDU+60000U+6FFFDU+70000U+7FFFD或者 U+80000U+8FFFD
>
> *头部标识符* → U+90000U+9FFFDU+A0000U+AFFFDU+B0000U+BFFFD或者 U+C0000U+CFFFD
>
> *头部标识符* → U+D0000U+DFFFD 或者 U+E0000U+EFFFD
>
> *标识符字符* → 数值 0 - 9
>
>
#### identifier-character {#identifier-character}
>
> *标识符字符* → U+0300U+036FU+1DC0U+1DFFU+20D0U+20FF或者 U+FE20U+FE2F
>
> *标识符字符* → [*头部标识符*](#identifier-head)
>
>
#### identifier-characters {#identifier-characters}
>
> *标识符字符组* → [*标识符字符*](#identifier-character) [*标识符字符组*](#identifier-characters)<sub>可选</sub>
>
>
#### implicit-parameter-name {#implicit-parameter-name}
>
> *隐式参数名* → **$** [*十进制数字列表*](#decimal-digit)
## 关键字和标点符号 {#keywords-and-punctuation}
下面这些被保留的关键字不允许用作标识符,除非使用反引号转义,具体描述请参考 [标识符](#identifiers)。除了 `inout``var` 以及 `let` 之外的关键字可以用作某个函数声明或者函数调用当中的外部参数名,无需添加反引号转义。当一个成员与一个关键字具有相同的名称时,不需要使用反引号来转义对该成员的引用,除非在引用该成员和使用该关键字之间存在歧义 - 例如,`self``Type``Protocol` 在显式的成员表达式中具有特殊的含义,因此它们必须在该上下文中使用反引号进行转义。
* 用在声明中的关键字: `associatedtype``class``deinit``enum``extension``fileprivate ``func``import``init``inout``internal``let``open``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``Any``catch``false``is``nil``rethrows``super``self``Self``throw``throws``true` 以及 `try `
* 用在模式中的关键字:`_`
* 以井字号(`#`)开头的关键字:`#available``#colorLiteral``#column``#else``#elseif``#endif``#error``#file``#fileLiteral``#function``#if``#imageLiteral ``#line``#selector``#sourceLocation`以及 `#warning`
* 特定上下文中被保留的关键字: `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`。这些关键字在特定上下文之外可以被用做标识符。
以下符号被当作保留符号,不能用于自定义运算符: `(``)``{``}``[``]``.``,``:``;``=``@``#``&`(作为前缀运算符)、`->`、`` ` ``、`?`、`!`(作为后缀运算符)。
## 字面量 {#literal}
*字面量literal* 用来表示源码中某种特定类型的值,比如一个数字或字符串。
下面是字面量的一些示例:
```swift
42 // 整数字面量
3.14159 // 浮点数字面量
"Hello, world!" // 字符串字面量
true // 布尔值字面量
```
字面量本身并不包含类型信息。事实上,一个字面量会被解析为拥有无限的精度,然后 Swift 的类型推导会尝试去推导出这个字面量的类型。比如,在 `let x: Int8 = 42` 这个声明中Swift 使用了显式类型标注(`: Int8`)来推导出 `42` 这个整数字面量的类型是 `Int8`。如果没有可用的类型信息, Swift 则会从标准库中定义的字面量类型中推导出一个默认的类型。整数字面量的默认类型是 `Int`,浮点数字面量的默认类型是 `Double`,字符串字面量的默认类型是 `String`,布尔值字面量的默认类型是 `Bool`。比如,在 `let str = "Hello, world"` 这个声明中,字符串 `"Hello, world"` 的默认推导类型就是 `String`。
当为一个字面量值指定了类型标注的时候,这个标注的类型必须能通过这个字面量值实例化。也就是说,这个类型必须符合这些 Swift 标准库协议中的一个:整数字面量的 `IntegerLiteralConvertible` 协议、浮点数字面量的 `FloatingPointLiteralConvertible` 协议、字符串字面量的 `StringLiteralConvertible` 协议以及布尔值字面量的 `BooleanLiteralConvertible` 协议。比如,`Int8` 符合 `IntegerLiteralConvertible` 协议,因此它能在 `let x: Int8 = 42` 这个声明中作为整数字面量 `42` 的类型标注。
> 字面量语法
>
> *字面量* → [*数值字面量*](#integer-literal) | [*字符串字面量*](#string-literal) | [*布尔值字面量*](#integer-literal) | [*nil 字面量*](#integer-literal)
>
> *数值字面量* → **-**<sub>可选</sub> [*整数字面量*](#integer-literal) | **-**<sub>可选</sub> [*浮点数字面量*](#floating-point-literal)
>
> *布尔值字面量* → **true** | **false**
>
> *nil 字面量* → **nil**
### 整数字面量{#integer-literal}
*整数字面量Integer Literals* 表示未指定精度整数的值。整数字面量默认用十进制表示,可以加前缀来指定其他的进制。二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x`。
十进制字面量包含数字 `0` 至 `9`。二进制字面量只包含 `0` 或 `1`,八进制字面量包含数字 `0` 至 `7`,十六进制字面量包含数字 `0` 至 `9` 以及字母 `A` 至 `F`(大小写均可)。
负整数的字面量在整数字面量前加负号 `-`,比如 `-42`。
整型字面面可以使用下划线(`_`)来增加数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,这同样也会被系统所忽略,并不会影响字面量的值。
除非特别指定,整数字面量的默认推导类型为 Swift 标准库类型中的 `Int`。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 [整数](../chapter2/01_The_Basics.md#integers)。
> 整数字面量语法
>
>
#### integer-literal {#integer-literal}
>
> *整数字面量* → [*二进制字面量*](#binary-literal)
>
> *整数字面量* → [*八进制字面量*](#octal-literal)
>
> *整数字面量* → [*十进制字面量*](#decimal-literal)
>
> *整数字面量* → [*十六进制字面量*](#hexadecimal-literal)
>
>
#### binary-literal {#binary-literal}
>
> *二进制字面量* → **0b** [*二进制数字*](#binary-digit) [*二进制字面量字符组*](#binary-literal-characters)<sub>可选</sub>
>
>
#### binary-digit {#binary-digit}
>
> *二进制数字* → 数值 0 到 1
>
> *二进制字面量字符* → [*二进制数字*](#binary-digit) | _
>
>
#### binary-literal-characters {#binary-literal-characters}
>
> *二进制字面量字符组* → [*二进制字面量字符*](#binary-literal-character) [*二进制字面量字符组*](#binary-literal-characters)<sub>可选</sub>
>
>
#### octal-literal {#octal-literal}
>
> *八进制字面量* → **0o** [*八进字数字*](#octal-digit) [*八进制字符组*](#octal-literal-characters)<sub>可选</sub>
>
>
#### octal-digit {#octal-digit}
>
> *八进字数字* → 数值 0 到 7
>
> *八进制字符* → [*八进字数字*](#octal-digit) | _
>
>
#### octal-literal-characters {#octal-literal-characters}
>
> *八进制字符组* → [*八进制字符*](#octal-literal-character) [*八进制字符组*](#octal-literal-characters)<sub>可选</sub>
>
>
#### decimal-literal {#decimal-literal}
>
> *十进制字面量* → [*十进制数字*](#decimal-digit) [*十进制字符组*](#decimal-literal-characters)<sub>可选</sub>
>
>
#### decimal-digit {#decimal-digit}
>
> *十进制数字* → 数值 0 到 9
>
>
#### decimal-literal-characters {#decimal-literal-characters}
>
> *十进制数字组* → [*十进制数字*](#decimal-digit) [*十进制数字组*](#decimal-literal-characters)<sub>可选</sub>
>
> *十进制字符* → [*十进制数字*](#decimal-digit) | _
>
> *十进制字符组* → [*十进制字符*](#decimal-literal-characters) [*十进制字符组*](#decimal-literal-characters)<sub>可选</sub>
>
>
#### hexadecimal-literal {#hexadecimal-literal}
>
> *十六进制字面量* → **0x** [*十六进制数字*](#hexadecimal-digit) [*十六进制字面量字符组*](#hexadecimal-literal-characters)<sub>可选</sub>
>
>
#### hexadecimal-digit {#hexadecimal-digit}
>
> *十六进制数字* → 数值 0 到 9, 字母 a 到 f, 或 A 到 F
>
> *十六进制字符* → [*十六进制数字*](#hexadecimal-digit) | _
>
>
#### hexadecimal-literal-characters {#hexadecimal-literal-characters}
>
> *十六进制字面量字符组* → [*十六进制字符*](#hexadecimal-literal-characters) [*十六进制字面量字符组*](#hexadecimal-literal-characters)<sub>可选</sub>
### 浮点数字面量{#floating-point-literal}
*浮点数字面量Floating-point literals* 表示未指定精度浮点数的值。
浮点数字面量默认用十进制表示(无前缀),也可以用十六进制表示(加前缀 `0x`)。
十进制浮点数字面量由十进制数字串后跟小数部分或指数部分(或两者皆有)组成。十进制小数部分由小数点(`.`)后跟十进制数字串组成。指数部分由大写或小写字母 `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 x 2²也就是 `60`;同样,`0xFp-2` 表示 15 x 2¯²也就是 `3.75`。
负数的浮点数字面量由负号(`-`)和浮点数字面量组成,例如 `-42.5`。
浮点数字面量允许使用下划线(`_`)来增强数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。
除非特别指定,浮点数字面量的默认推导类型为 Swift 标准库类型中的 `Double`,表示 64 位浮点数。Swift 标准库也定义了 `Float` 类型,表示 32 位浮点数。
> 浮点数字面量语法
>
>
#### floating-point-literal {#floating-point-literal}
>
> *浮点数字面量* → [*十进制字面量*](#decimal-literal) [*十进制分数*](#decimal-fraction)<sub>可选</sub> [*十进制指数*](#decimal-exponent)<sub>可选</sub>
>
> *浮点数字面量* → [*十六进制字面量*](#hexadecimal-literal) [*十六进制分数*](#hexadecimal-fraction)<sub>可选</sub> [*十六进制指数*](#hexadecimal-exponent)
>
>
#### decimal-fraction {#decimal-fraction}
>
> *十进制分数* → **.** [*十进制字面量*](#decimal-literal)
>
>
#### decimal-exponent {#decimal-exponent}
>
> *十进制指数* → [*十进制指数 e*](#floating-point-e) [*正负号*](#sign)<sub>可选</sub> [*十进制字面量*](#decimal-literal)
>
>
#### hexadecimal-fraction {#hexadecimal-fraction}
>
> *十六进制分数* → **.** [*十六进制数字*](#hexadecimal-digit) [*十六进制字面量字符组*](#hexadecimal-literal-characters)<sub>可选</sub>
>
>
#### hexadecimal-exponent {#hexadecimal-exponent}
>
> *十六进制指数* → [*十六进制指数 p*](#floating-point-p) [*正负号*](#sign)<sub>可选</sub> [*十进制字面量*](#decimal-literal)
>
>
#### floating-point-e {#floating-point-e}
>
> *十进制指数 e* → **e** | **E**
>
>
#### floating-point-p {#floating-point-p}
>
> *十六进制指数 p* → **p** | **P**
>
>
#### sign {#sign}
>
> *正负号* → **+** | **-**
### 字符串字面量 {#string-literal}
字符串字面量是被引号包括的一串字符组成。 单行字符串字面量被包在双引号中的一串字符组成,形式如下:
> "`字符`"
字符串字面量中不能包含未转义的双引号(`"`)、未转义的反斜线(`\`)、回车符、换行符。
多行字符串字面量被包在三个双引号中的一串字符组成,形式如下:
> """
> `字符`
> """
与单行字符串字面量不同的是,多行字符串字面量可以包含不转义的双引号("),回车以及换行。它不能包含三个未转义的连续双引号。
""" 之后的回车或者换行开始多行字符串字面量,不是字符串的一部分。 """ 之前回车或者换行结束字面量,也不是字符串的一部分。要让多行字符串字面量的开始或结束带有换行,就在第一行或者最后一行写一个空行。
多行字符串字面量可以使用任何空格或制表符组合进行缩进;这些缩进不会包含在字符串中。 """ 的结束符号决定了缩进:字面量中的任何一个非空行必须起始于多行字符串字面量结束符号的前面;空格和制表符不会被转换。你可以包在缩进后含额外的空格和制表符;这些空格和制表符会在字符串中出现。
多行字符串字面量中的一行结束使用规范化的换行符号。尽管你的源代码混用了回车和换行符,字符串中所有的行结束都必须一样.
在多行字符串字面量里, 在行末用反斜线(`\`)可以省略字符串行间中断。 反斜线之间的空白和行间中断也可以省略。 你可以在你的代码里用这种语法硬包裹多行字符串字面量,不需要改变产生的字符串的值。
可以在字符串字面量中使用的转义特殊符号如下:
* 空字符 `\0`
* 反斜线 `\\`
* 水平制表符 `\t`
* 换行符 `\n`
* 回车符 `\r`
* 双引号 `\"`
* 单引号 `\'`
* Unicode 标量 `\u{`n`}`n 为一到八位的十六进制数字
字符串字面量允许在反斜杠(`\`)后的括号 `()` 中插入表达式的值。插入表达式可以包含字符串字面量,但不能包含未转义的反斜线(`\`)、回车符以及换行符。
例如,以下所有字符串字面量的值都是相同的:
```swift
"1 2 3"
"1 2 \("3")"
"1 2 \(3)"
"1 2 \(1 + 2)"
let x = 3; "1 2 \(x)"
```
可以使用一对或多对扩展分隔符(#)包裹字符串进行分隔,被分隔的字符串的形式如下所示:
> \#"`characters`"#
>
> \#"""
>
> `characters`
>
> """#
特殊字符在被分隔符分隔的结果字符串中会展示为普通字符,而不是特殊字符。你可以使用扩展分隔符来创建一些具有特殊效果的字符串。例如,生成字符串插值,启动或终止转义序列(字符串)。
以下所示,由字符串字面量和扩展分隔符所创建的字符串是等价的:
```swift
let string = #"\(x) \ " \u{2603}"#
let escaped = "\\(x) \\ \" \\u{2603}"
print(string)
// Prints "\(x) \ " \u{2603}"
print(string == escaped)
// Prints "true"
```
如果在一个字符串中使用多对扩展分隔符,请不要在分隔符之间使用空格。
```swift
print(###"Line 1\###nLine 2"###) // OK
print(# # #"Line 1\# # #nLine 2"# # #) // Error
```
使用扩展分隔符创建的多行字符串字面量与普通多行字符串字面量具有相同的缩进要求。
字符串字面量的默认推导类型为 `String`。更多有关 `String` 类型的信息请参考 [字符串和字符](../chapter2/03_Strings_and_Characters.md) 以及 [*字符串结构参考*](https://developer.apple.com/documentation/swift/string)。
`` 操作符连接的字符型字面量是在编译时进行连接的。比如下面的 `textA``textB` 是完全一样的,`textA` 没有任何运行时的连接操作。
```swift
let textA = "Hello " + "world"
let textB = "Hello world"
```
> 字符串字面量语法
>
> *字符串字面量* → [*静态字符串字面量*](#static-string-literal) | [*插值字符串字面量*](#interpolated-string-literal)
>
> *字符串开分隔定界符* → [*字符串扩展分隔符*](#extended-string-literal-delimiter) **"**
>
> *字符串闭分隔定界符* → **"** [*字符串扩展分隔符*](#extended-string-literal-delimiter)<sub>可选</sub>
>
>
#### static-string-literal {#static-string-literal}
>
> *静态字符串字面量* → [*字符串开分隔定界符*](#extended-string-literal-delimiter) [*引用文本*](#quoted-text)<sub>可选</sub> [*字符串闭分隔定界符*](#extended-string-literal-delimiter)
>
> *静态字符串字面量* → [*多行字符串开分隔定界符*](#extended-string-literal-delimiter) [*多行引用文本*](#multiline-quoted-text)<sub>可选</sub> [*多行字符串闭分隔定界符*](#extended-string-literal-delimiter)
>
> *多行字符串开分隔定界符* → [*字符串扩展分隔符*](#extended-string-literal-delimiter) **"""**
>
> *多行字符串闭分隔定界符* → **"""** [*字符串扩展分隔符*](#extended-string-literal-delimiter)
>
>
#### extended-string-literal-delimiter {#extended-string-literal-delimiter}
>
> *字符串扩展分隔符* → **#** [*字符串扩展分隔符*](#extended-string-literal-delimiter)<sub>可选</sub>
>
>
#### quoted-text {#quoted-text}
>
> *引用文本* → [*引用文本项*](#quoted-text-item) [*引用文本*](#quoted-text)<sub>可选</sub>
>
>
#### quoted-text-item {#quoted-text-item}
>
> *引用文本项* → [*转义字符*](#escaped-character)
>
> *引用文本项* → 除了 **"**、**\\**、U+000A、U+000D 以外的所有 Unicode 字符
>
>
#### multiline-quoted-text {#multiline-quoted-text}
>
> *多行引用文本* → [*多行引用文本项*](#multiline-quoted-text-item) [*多行引用文本*](#multiline-quoted-text)<sub>可选</sub>
>
>
#### multiline-quoted-text-item {#multiline-quoted-text-item}
>
> *多行引用文本项* [*转义字符*](#escaped-character)<sub>可选</sub>
>
>
#### multiline-quoted-text {#multiline-quoted-text}
>
> *多行引用文本* → 除了 **\** 以外的任何Unicode标量值
>
> *多行引用文本* → [*转义换行*](#escaped-newline)
>
>
#### interpolated-string-literal {#interpolated-string-literal}
>
> *插值字符串字面量* → [*字符串开分隔定界符*](#extended-string-literal-delimiter) [*插值文本*](#interpolated-text)<sub>可选</sub> [*字符串闭分隔定界符*](#extended-string-literal-delimiter)
>
> *插值字符串字面量* → [*多行字符串开分隔定界符*](#extended-string-literal-delimiter) [*插值文本*](#interpolated-text)<sub>可选</sub> [*多行字符串闭分隔定界符*](#extended-string-literal-delimiter)
>
>
#### interpolated-text {#interpolated-text}
>
> *插值文本* → [*插值文本项*](#interpolated-text-item) [*插值文本*](#interpolated-text)<sub>可选</sub>
>
>
#### interpolated-text-item {#interpolated-text-item}
>
> *插值文本项* → **\\****(**[*表达式*](./04_Expressions.md)**)** | [*引用文本项*](#quoted-text-item)
>
> *多行插值文本* → [*多行插值文本项*](#multiline-quoted-text-item) [*多行插值文本*](#multiline-quoted-text)<sub>可选</sub>
>
> *多行插值文本项* → **\\(** [表达式](./04_Expressions.md) **)** | [多行引用文本项](#multiline-quoted-text-item)
>
>
#### escape-sequence {#escape-sequence}
>
> *转义序列* → **\\** [字符串扩展分隔符](#extended-string-literal-delimiter)
>
>
#### escaped-character {#escaped-character}
>
> *转义字符* → [*转义序列*](#escape-sequence) **0** | [*转义序列*](#escape-sequence) **\\** | [*转义序列*](#escape-sequence) **t** | [*转义序列*](#escape-sequence) **n** | [*转义序列*](#escape-sequence) **r** | [*转义序列*](#escape-sequence) **\"** | [*转义序列*](#escape-sequence) **'**
>
> *转义字符* → [*转义序列*](#escape-sequence) **u {** [*unicode 标量数字*](#unicode-scalar-digits) **}**
>
>
#### unicode-scalar-digits {#unicode-scalar-digits}
>
> *unicode 标量数字* → 一到八位的十六进制数字
>
>
#### escaped-newline {#escaped-newline}
>
> *转义换行符* → [*转义序列*](#escape-sequence) [*空白*](#whitespace)<sub>可选</sub> [*断行符*](#line-break)
## 运算符 {#operator}
Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基础运算符](../chapter2/02_Basic_Operators.md) 和 [高级运算符](../chapter2/26_Advanced_Operators.md) 中进行了阐述。这一小节将描述哪些字符能用于自定义运算符。
自定义运算符可以由以下其中之一的 ASCII 字符 `/``=``-``+``!``*``%``<``>``&``|``^``?` 以及 `~`,或者后面语法中规定的任一个 Unicode 字符(其中包含了*数学运算符*、*零散符号Miscellaneous Symbols* 以及印刷符号Dingbats之类的 Unicode 块)开始。在第一个字符之后,允许使用组合型 Unicode 字符。
您也可以以点号(`.`)开头来定义自定义运算符。这些运算符可以包含额外的点,例如 `.+.`。如果某个运算符不是以点号开头的,那么它就无法再包含另外的点号了。例如,`+.+` 就会被看作为一个 `+` 运算符后面跟着一个 `.+` 运算符。
虽然您可以用问号 `?` 来自定义运算符,但是这个运算符不能只包含单独的一个问号。此外,虽然运算符可以包含一个惊叹号 `!`,但是前缀运算符不能够以问号或者惊叹号开头。
> 注意
>
> 以下这些标记 `=`、`->`、`//`、`/*`、`*/`、`.`、`<`(前缀运算符)、`&`、`?`、`?`(中缀运算符)、`>`(后缀运算符)、`!` 、`?` 是被系统保留的。这些符号不能被重载,也不能用于自定义运算符。
运算符两侧的空白被用来区分该运算符是否为前缀运算符、后缀运算符或二元运算符。规则总结如下:
* 如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:`a+++b``a +++ b` 当中的 `+++` 运算符会被看作二元运算符。
* 如果运算符只有左侧空白,将被看作一元前缀运算符。例如 `a +++b` 中的 `+++` 运算符会被看做是一元前缀运算符。
* 如果运算符只有右侧空白,将被看作一元后缀运算符。例如 `a+++ b` 中的 `+++` 运算符会被看作是一元后缀运算符。
* 如果运算符左侧没有空白并紧跟 `.`,将被看作一元后缀运算符。例如 `a+++.b` 中的 `+++` 运算符会被视为一元后缀运算符(即上式被视为 `a+++ .b` 而不是 `a +++ .b`)。
鉴于这些规则,运算符前的字符 `(``[``{`,运算符后的字符 `)``]``}`,以及字符 `,``;``:` 都被视为空白。
以上规则需注意一点,如果预定义运算符 `!``?` 左侧没有空白,则不管右侧是否有空白都将被看作后缀运算符。如果将 `?` 用作可选链式调用运算符,左侧必须无空白。如果用于条件运算符 `? :`,必须两侧都有空白。
在某些特定的设计中 ,以 `<``>` 开头的运算符会被分离成两个或多个符号,剩余部分可能会以同样的方式被再次分离。因此,在 `Dictionary<String, Array<Int>>` 中没有必要添加空白来消除闭合字符 `>` 的歧义。在这个例子中, 闭合字符 `>` 不会被视为单独的符号,因而不会被错误解析为 `>>` 运算符。
要学习如何自定义运算符,请参考 [自定义运算符](../chapter2/26_Advanced_Operators.md#custom_operators) 和 [运算符声明](./06_Declarations.md#operator_declaration)。要学习如何重载运算符,请参考 [运算符函数](../chapter2/26_Advanced_Operators.md#operator_functions)。
> 运算符语法
>
> *运算符* → [*头部运算符*](#operator-head) [*运算符字符组*](#operator-characters)<sub>可选</sub>
>
> *运算符* → [*头部点运算符*](#dot-operator-head) [*点运算符字符组*](#dot-operator-characters)
>
>
#### operator-head {#operator-head}
>
> *头部运算符* → **/** | **=** | **-** | **+** | **!** | __*__ | **%** | **<** | **>** | **&** | **|** | **^** | **~** | **?**
>
> *头部运算符* → U+00A1U+00A7
>
> *头部运算符* → U+00A9 或 U+00AB
>
> *头部运算符* → U+00AC 或 U+00AE
>
> *头部运算符* → U+00B0U+00B1U+00B6U+00BBU+00BFU+00D7或 U+00F7
>
> *头部运算符* → U+2016U+2017 或 U+2020U+2027
>
> *头部运算符* → U+2030U+203E
>
> *头部运算符* → U+2041U+2053
>
> *头部运算符* → U+2055U+205E
>
> *头部运算符* → U+2190U+23FF
>
> *头部运算符* → U+2500U+2775
>
> *头部运算符* → U+2794U+2BFF
>
> *头部运算符* → U+2E00U+2E7F
>
> *头部运算符* → U+3001U+3003
>
> *头部运算符* → U+3008U+3030
>
>
#### operator-character {#operator-character}
>
> *运算符字符* → [*头部运算符*](#operator-head)
>
> *运算符字符* → U+0300U+036F
>
> *运算符字符* → U+1DC0U+1DFF
>
> *运算符字符* → U+20D0U+20FF
>
> *运算符字符* → U+FE00U+FE0F
>
> *运算符字符* → U+FE20U+FE2F
>
> *运算符字符* → U+E0100U+E01EF
>
>
#### operator-characters {#operator-characters}
>
> *运算符字符组* → [*运算符字符*](#operator-character) [*运算符字符组*](#operator-characters)<sub>可选</sub>
>
>
#### dot-operator-head {#dot-operator-head}
>
> *头部点运算符* → **..**
>
>
#### dot-operator-character {#dot-operator-character}
>
> *点运算符字符* → **.** | [*运算符字符*](#operator-character)
>
>
#### dot-operator-characters {#dot-operator-characters}
>
> *点运算符字符组* → [*点运算符字符*](#dot-operator-character) [*点运算符字符组*](#dot-operator-characters)<sub>可选</sub>
>
> *二元运算符* → [*运算符*](#operator)
>
> *前缀运算符* → [*运算符*](#operator)
>
> *后缀运算符* → [*运算符*](#operator)

View File

@ -1,399 +0,0 @@
# 特性Attributes
在 Swift 中有两种特性,分别用于修饰声明和类型。特性提供了有关声明和类型的更多信息。例如,使用 `discardableResult` 特性声明的函数,表明该函数虽然有返回值,但如果没有使用该返回值,编译器不会产生警告。
您可以通过以下方式指定一个特性,通过符号 `@` 后跟特性的名称和特性接收的任何参数:
@`特性名`
@`特性名`(`特性参数`)
有些声明特性通过接收参数来指定特性的更多信息以及它是如何修饰某个特定的声明的。这些_特性的参数_写在圆括号内它们的格式由它们所属的特性来定义。
## 声明特性 {#declaration-attributes}
声明特性只能应用于声明。
### `available` {#available}
`available` 特性用于声明时,表示该声明的生命周期是相对于特定的平台和操作系统版本。
`available` 特性经常与参数列表一同出现,该参数列表至少有两个特性参数,参数之间由逗号分隔。这些参数由以下这些平台名字中的一个起头:
- `iOS`
- `iOSApplicationExtension`
- `macOS`
- `macOSApplicationExtension`
- `watchOS`
- `watchOSApplicationExtension`
- `tvOS`
- `tvOSApplicationExtension`
- `swift`
当然,你也可以用一个星号(`*`)来表示上面提到的所有平台。指定 Swift 版本的 `available` 特性参数,不能使用星号表示。
其余的参数,可以按照任何顺序出现,并且可以添加关于声明生命周期的附加信息,包括重要事件。
- `unavailable` 参数表示该声明在指定的平台上是无效的。当指定 Swift 版本可用性时不可使用该参数。
- `introduced` 参数表示指定平台从哪一版本开始引入该声明。格式如下:
`introduced`: `版本号`
*版本号*由一至三个正整数构成,由句点分隔的。
- `deprecated` 参数表示指定平台从哪一版本开始弃用该声明。格式如下:
`deprecated`: `版本号`
可选的*版本号*由一个或多个正整数构成,由句点分隔的。省略版本号表示该声明目前已弃用,当弃用出现时没有给出任何有关信息。如果你省略了版本号,冒号(`:`)也可省略。
- `obsoleted` 参数表示指定平台或语言从哪一版本开始废弃该声明。当一个声明被废弃后,它就从平台或语言中移除,不能再被使用。格式如下:
`obsoleted`: `版本号`
*版本号*由一至三个正整数构成,由句点分隔的。
- `message` 参数用来提供文本信息。当使用被弃用或者被废弃的声明时,编译器会抛出警告或错误信息。格式如下:
`message`: `信息内容`
_信息内容_由一个字符串构成。
- `renamed` 参数用来提供文本信息,用以表示被重命名的声明的新名字。当使用声明的旧名字时,编译器会报错提示新名字。格式如下:
`renamed`: `新名字`
_新名字_由一个字符串构成。
你可以将 `renamed` 参数和 `unavailable` 参数用于 `available` 特性,来表示声明在不同平台和 Swift 版本上的可用性。如下所示,表示声明的名字在一个框架或者库的不同发布版本间发生了变化。以此组合表示该声明被重命名的编译错误。
```swift
// 首发版本
protocol MyProtocol {
// 这里是协议定义
}
```
```swift
// 后续版本重命名了 MyProtocol
protocol MyRenamedProtocol {
// 这里是协议定义
}
@available(*, unavailable, renamed:"MyRenamedProtocol")
typealias MyProtocol = MyRenamedProtocol
```
你可以在某个声明上使用多个 `available` 特性,以指定该声明在不同平台和 Swift 版本上的可用性。编译器只有在与 `available` 特性中指定的平台或语言版本匹配时,才会使用 `available` 特性。
如果 `available` 特性除了平台名称参数外,只指定了一个 `introduced` 参数,那么可以使用以下简写语法代替:
@available(`平台名称` `版本号`, *)
@available(swift `版本号`)
`available` 特性的简写语法可以简明地表达出声明在多个平台上的可用性。尽管这两种形式在功能上是相同的,但请尽可能地使用简写语法形式。
```swift
@available(iOS 10.0, macOS 10.12, *)
class MyClass {
// 这里是类定义
}
```
`available` 特性需要同时指定 Swift 版本和平台可用性,需要使用单独的 `available` 特性来声明。
```swift
@available(swift 3.0.2)
@available(macOS 10.12, *)
struct MyStruct {
// 这里是结构体定义
}
```
### `discardableResult` {#discardableresult}
该特性用于的函数或方法声明,以抑制编译器中函数或方法的返回值被调而没有使用其结果的警告。
### `dynamicCallable` {#dynamiccallable}
该特性用于类、结构体、枚举或协议,以将该类型的实例视为可调用的函数。该类型必须实现 `dynamicallyCall(withArguments:)``dynamicallyCall(withKeywordArguments:)` 方法之一,或两者同时实现。
你可以调用 `dynamicCallable` 特性的实例,就像是调用一个任意数量参数的函数。
```swift
@dynamicCallable
struct TelephoneExchange {
func dynamicallyCall(withArguments phoneNumber: [Int]) {
if phoneNumber == [4, 1, 1] {
print("Get Swift help on forums.swift.org")
} else {
print("Unrecognized number")
}
}
}
let dial = TelephoneExchange()
// 使用动态方法调用
dial(4, 1, 1)
// 打印“Get Swift help on forums.swift.org”
dial(8, 6, 7, 5, 3, 0, 9)
// 打印“Unrecognized number”
// 直接调用底层方法
dial.dynamicallyCall(withArguments: [4, 1, 1])
```
`dynamicallyCall(withArguments:)` 方法的声明必须至少有一个参数遵循 [`ExpressibleByArrayLiteral`](https://developer.apple.com/documentation/swift/expressiblebyarrayliteral) 协议,如 `[Int]`,而返回值类型可以是任意类型。
```swift
@dynamicCallable
struct Repeater {
func dynamicallyCall(withKeywordArguments pairs: KeyValuePairs<String, Int>) -> String {
>
return pairs
.map { label, count in
repeatElement(label, count: count).joined(separator: " ")
}
.joined(separator: "\n")
}
}
let repeatLabels = Repeater()
print(repeatLabels(a: 1, b: 2, c: 3, b: 2, a: 1))
// a
// b b
// c c c
// b b
// a
```
`dynamicallyCall(withKeywordArguments:)` 方法声明必须至少有一个参数遵循 [`ExpressibleByDictionaryLiteral`](https://developer.apple.com/documentation/swift/expressiblebydictionaryliteral) 协议,返回值可以任意类型。参数的 [`Key`](https://developer.apple.com/documentation/swift/expressiblebydictionaryliteral/2294108-key) 必须遵循 [`ExpressibleByStringLiteral`](https://developer.apple.com/documentation/swift/expressiblebystringliteral) 协议。上述的示例使用 [`KeyValuePairs`](https://developer.apple.com/documentation/swift/keyvaluepairs) 作为参数类型,以便调用者可以传入重复的参数标签,`a``b` 在调用 `repeat`中多次使用。
如果你同时实现两种 `dynamicallyCall` 方法,则当在方法调用中包含关键字参数时,会调用 `dynamicallyCall(withKeywordArguments:)` 方法,否则调用 `dynamicallyCall(withArguments:)` 方法。
你只能调用参数和返回值与 `dynamicallyCall` 方法实现匹配的动态调用实例。在下面示例的调用无法编译,因为其 `dynamicallyCall(withArguments:)` 实现不接受 `KeyValuePairs<String, String>` 参数。
```swift
repeatLabels(a: "four") // Error
```
### `dynamicMemberLookup` {#dynamicmemberlookup}
该特性用于类、结构体、枚举或协议,让其能在运行时查找成员。该类型必须实现 `subscript(dynamicMemberLookup:)` 下标。
在显式成员表达式中,如果没有成名指定成员,则该表达式被理解为对该类型的 `subscript(dynamicMemberLookup:)` 下标的调用,传递包含成员名称字符串的参数。下标的参数只需遵循 `ExpressibleByStringLiteral` 协议,返回值类型可以为任意类型。在大多数情况下,下标的参数是一个 `String` 值。例如:
```swift
@dynamicMemberLookup
struct DynamicStruct {
let dictionary = ["someDynamicMember": 325,
"someOtherMember": 787]
subscript(dynamicMember member: String) -> Int {
>
return dictionary[member] ?? 1054
}
}
let s = DynamicStruct()
// 使用动态成员查找
let dynamic = s.someDynamicMember
print(dynamic)
// 打印“325”
// 直接调用底层下标
let equivalent = s[dynamicMember: "someDynamicMember"]
print(dynamic == equivalent)
// 打印“true”
```
### `GKInspectable` {#gkinspectable}
应用此属性,暴露一个自定义 GameplayKit 组件属性给 SpriteKit 编辑器 UI。
### `inlinable` {#inlinable}
该特性用于函数、方法、计算属性、下标、便利构造器或析构器的声明,以将该声明的实现公开为模块公开接口的一部分。编译器允许在调用处把 `inlinable` 标记的符号替换为符号实现的副本。
内联代码可以与任意模块中 `public` 访问级别的符号进行交互,同时可以与在相同模块中标记 `usableFromInline` 特性的 `internal` 访问级别的符号进行交互。内联代码不能与 `private``fileprivate` 级别的符号进行交互。
该特性不能用于嵌套在函数内的声明,也不能用于 `fileprivate``private` 访问级别的声明。在内联函数定义的函数和闭包是隐式非内联的,即使他们不能标记该特性。
### `nonobjc` {#nonobjc}
该特性用于方法、属性、下标、或构造器的声明,这些声明本可以在 Objective-C 代码中使用,而使用 `nonobjc` 特性则告诉编译器这个声明不能在 Objective-C 代码中使用。
该特性用在扩展中,与在没有明确标记为 `objc` 特性的扩展中给每个成员添加该特性具有相同效果。
可以使用 `nonobjc` 特性解决标有 `objc` 的类中桥接方法的循环问题,该特性还允许对标有 `objc` 的类中的构造器和方法进行重载。
标有 `nonobjc` 特性的方法不能重写标有 `objc` 特性的方法。然而,标有 `objc` 特性的方法可以重写标有 `nonobjc` 特性的方法。同样,标有 `nonobjc` 特性的方法不能满足标有 `@objc` 特性的协议中的方法要求。
### `NSApplicationMain` {#nsapplicationmain}
在类上使用该特性表示该类是应用程序委托类,使用该特性与调用 `NSApplicationMain(_:_:)` 函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
如果你不想使用这个特性,可以提供一个 `main.swift` 文件,并在代码**顶层**调用 `NSApplicationMain(_:_:)` 函数,如下所示:
```swift
import AppKit
NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
```
### `NSCopying` {#nscopying}
该特性用于修饰一个类的存储型变量属性。该特性将使属性的设值方法使用传入值的副本进行赋值,这个值由传入值的 `copyWithZone(_:)` 方法返回。该属性的类型必需符合 `NSCopying` 协议。
`NSCopying` 特性的行为与 Objective-C 中的 `copy` 特性相似。
### `NSManaged` {#nsmanaged}
该特性用于修饰 `NSManagedObject` 子类中的实例方法或存储型变量属性,表明它们的实现由 `Core Data` 在运行时基于相关实体描述动态提供。对于标记了 `NSManaged` 特性的属性,`Core Data` 也会在运行时为其提供存储。应用这个特性也意味着 `objc` 特性。
### `objc` {#objc}
该特性用于修饰任何可以在 Objective-C 中表示的声明。比如,非嵌套类、协议、非泛型枚举(仅限原始值为整型的枚举)、类和协议中的属性和方法(包括存取方法)、构造器、析构器以及下标运算符。`objc` 特性告诉编译器这个声明可以在 Objective-C 代码中使用。
该特性用在扩展中,与在没有明确标记为 `nonobjc` 特性的扩展中给每个成员添加该特性具有相同效果。
编译器隐式地将 `objc` 特性添加到 Objective-C 中定义的任何类的子类。但是,子类不能是泛型的,并且不能继承于任何泛型类。你可以将 `objc` 特性显式添加到满足这些条件的子类,来指定其 Objective-C 名称,如下所述。添加了 `objc` 的协议不能继承于没有此特性的协议。
在以下情况中隐式添加了 `objc` 特性。
- 父类有 `objc` 特性,且重写为子类的声明。
- 遵循带有 `objc` 特性协议的声明。
- 带有 `IBAction``IBOutlet``IBDesignable``IBInspectable``NSManaged``GKInspectable` 特性的声明。
如果你将 `objc` 特性应用于枚举,每一个枚举用例都会以枚举名称和用例名称组合的方式暴露在 Objective-C 代码中。例如,在 `Planet` 枚举中有一个名为 `Venus` 的用例,该用例暴露在 Objective-C 代码中时叫做 `PlanetVenus`
`objc` 特性有一个可选的参数,由标识符构成。当你想把 `objc` 所修饰的实体以一个不同的名字暴露给 Objective-C 时,你就可以使用这个特性参数。你可以使用这个参数来命名类、枚举类型、枚举用例、协议、方法、存取方法以及构造器。如果你要指定类、协议或枚举在 Objective-C 下的名称,在名称上包含三个字母的前缀,如 [Objective-C 编程](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011210) 中的 [约定](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Conventions/Conventions.html#//apple_ref/doc/uid/TP40011210-CH10-SW1)。下面的例子把 `ExampleClass` 中的 `enabled` 属性的取值方法暴露给 Objective-C名字是 `isEnabled`,而不是它原来的属性名。
```swift
class ExampleClass: NSObject {
@objc var enabled: Bool {
@objc(isEnabled) get {
// 返回适当的值
}
}
}
```
### `objcMembers` {#objcmembers}
该特性用于类声明,以将 `objc` 特性应用于该类、扩展、子类以及子类的扩展的所有 Objective-C 兼容成员。
大多数代码应该使用 `objc` 特性,以暴露所需的声明。如果需要暴露多个声明,可以将其分组到添加 `objc` 特性的扩展中。`objcMembers` 特性为大量使用 Objective-C 运行时的内省工具的库提供了便利。添加不必要的 `objc` 特性会增加二进制体积并影响性能。
### `requires_stored_property_inits` {#requires-stored-property-inits}
该特性用于类声明,以要求类中所有存储属性提供默认值作为其定义的一部分。对于从中继承的任何类都推断出 `NSManagedObject` 特性。
### `testable` {#testable}
在导入允许测试的编译模块时,该特性用于修饰 `import` 声明,这样就能访问被导入模块中的任何标有 `internal` 访问级别修饰符的实体,犹如它们被标记了 `public` 访问级别修饰符。测试也可以访问使用 `internal` 或者 `public` 访问级别修饰符标记的类和类成员,就像它们是 `open` 访问修饰符声明的。
### `UIApplicationMain` {#uiapplicationmain}
在类上使用该特性表示该类是应用程序委托类,使用该特性与调用 `UIApplicationMain` 函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
如果你不想使用这个特性,可以提供一个 `main.swift` 文件,并在代码顶层调用 `UIApplicationMain(_:_:_:_:)` 函数。比如,如果你的应用程序使用一个继承于 UIApplication 的自定义子类作为主要类,你可以调用 `UIApplicationMain(_:_:_:_:)` 函数而不是使用该特性。
### `usableFromInline` {#usablefrominline}
该特性用于函数、方法、计算属性、下标、构造器或析构器的声明,以在同一模块中允许该符号用于内联代码的声明。声明必须具有 `internal` 访问级别修饰符。
`public` 访问修饰符相同的是,该特性将声明公开为模块公共接口的一部分。区别于 `public`,编译器不允许在模块外部的代码通过名称引用 `usableFromInline` 标记的声明,即使导出了声明符号也是无法引用。但是,模块外的代码仍然可以通过运行时交换声明符号。
标记为 `inlinable` 特性的声明,在内联代码中可以隐式使用。虽然 `inlinable``usableFromInline` 可以用于 `internal` 声明,但这两者不能同时使用。
### `warn_unqualified_access` {#warn-unqualified-access}
该特性应用于顶级函数、实例方法、类方法或静态方法,以在没有前置限定符(例如模块名称、类型名称、实例变量或常量)的情况下使用该函数或方法时触发警告。使用该特性可以帮助减少在同一作用于访问同名函数之间的歧义。
例如Swift 标准库包含 [`min(_:_:)`](https://developer.apple.com/documentation/swift/1538339-min/) 顶级函数和用于序列比较元素的 [`min()`](https://developer.apple.com/documentation/swift/sequence/1641174-min) 方法。序列方法声明使用了 `warn_unqualified_access`,以减少在 `Sequence` 扩展中使用它们的歧义。
### Interface Builder 使用的声明特性 {#declaration-attributes-used-by-interface-builder}
`Interface Builder` 特性是 `Interface Builder` 用来与 Xcode 同步的声明特性。`Swift` 提供了以下的 `Interface Builder` 特性:`IBAction``IBOutlet``IBDesignable`,以及 `IBInspectable` 。这些特性与 Objective-C 中对应的特性在概念上是相同的。
`IBOutlet``IBInspectable` 用于修饰一个类的属性声明,`IBAction` 特性用于修饰一个类的方法声明,`IBDesignable` 用于修饰类的声明。
应用 `IBAction``IBOutlet``IBDesignable` 或者 `IBInspectable` 特性都意味着同时应用 `objc` 特性。
## 类型特性 {#type-attributes}
类型特性只能用于修饰类型。
### `autoclosure` {#autoclosure}
这个特性通过把表达式自动封装成无参数的闭包来延迟表达式的计算。它可以修饰类型为返回表达式结果类型的无参数函数类型的函数参数。关于如何使用 autoclosure 特性的例子,请参阅 [自动闭包](../chapter2/07_Closures.md#autoclosures) 和 [函数类型](./03_Types.md#function_type)。
### `convention` {#convention}
该特性用于修饰函数类型,它指出了函数调用的约定。
convention 特性总是与下面的参数之一一起出现。
- `swift` 参数用于表示一个 Swift 函数引用。这是 Swift 中函数值的标准调用约定。
- `block` 参数用于表示一个 Objective-C 兼容的块引用。函数值会作为一个块对象的引用,块是一种 `id` 兼容的 Objective-C 对象,其中嵌入了调用函数。调用函数使用 C 的调用约定。
- `c` 参数用于表示一个 C 函数引用。函数值没有上下文,不具备捕获功能,同样使用 C 的调用约定。
使用 C 函数调用约定的函数也可用作使用 Objective-C 块调用约定的函数,同时使用 Objective-C 块调用约定的函数也可用作使用 Swift 函数调用约定的函数。然而,只有非泛型的全局函数、局部函数以及未捕获任何局部变量的闭包,才可以被用作使用 C 函数调用约定的函数。
### `escaping` {#escaping}
在函数或者方法声明上使用该特性,它表示参数将不会被存储以供延迟执行,这将确保参数不会超出函数调用的生命周期。在使用 `escaping` 声明特性的函数类型中访问属性和方法时不需要显式地使用 `self.`。关于如何使用 `escaping` 特性的例子,请参阅 [逃逸闭包](../chapter2/07_Closures.md#escaping_closures)。
## Switch Case 特性 {#switch-case-attributes}
你只能在 switch cases 中使用 switch case 特性。
### `unknown` {#unknown}
次特性用于 switch case表示在编译时该地方不会匹配枚举的任何情况。有关如何使用 `unknown` 特性的示例,可参阅 [对未来枚举的 `case` 进行 `switch`](./05_Statements.md#future-case)。
> 特性语法
>
>
>
#### attribute {#attribute}
>
> *特性*→ [特性名](#attribute_name) [特性参数子句](#atribute_argument_clause)<sub>可选</sub>
>
>
#### attribute_name {#attribute-name}
>
> *特性名* → [标识符](./02_Lexical_Structure.md#identifier)
>
>
#### atribute_argument_clause {#atribute-argument-clause}
>
> *特性参数子句* → **(** [均衡令牌列表](#balanced_tokens)<sub>可选</sub> **)**
>
>
#### attributes {#attributes}
>
> *特性列表* → [特性](#attribute) [特性列表](#attributes)<sub>可选</sub>
>
>
>
#### balanced_tokens {#balanced-tokens}
>
> *均衡令牌列表* → [均衡令牌](#balanced_token) [均衡令牌列表](#balanced_tokens)<sub>可选</sub>
>
>
#### balanced_token {#balanced-token}
>
> *均衡令牌* → **(** [均衡令牌列表](#balanced_tokens)<sub>可选</sub> **)**
>
> *均衡令牌* → **\[** [均衡令牌列表](#balanced_tokens)<sub>可选</sub> **\]**
>
> *均衡令牌* → **{** [均衡令牌列表](#balanced_tokens)<sub>可选</sub> **}**
>
> *均衡令牌* → 任意标识符,关键字,字面量或运算符
>
> *均衡令牌* → 任意标点除了 **(****)****[****]****{**,或 **}**
>

View File

@ -1,6 +1,6 @@
# 文档翻译 & 校对工作记录
Swift 官方文档中文翻译工作由[numbbbbb](https://github.com/numbbbbb)发起并主导,该工作已经得到了苹果官方的认可。下面是各个版本官方文档翻译和校对工作的主要贡献者,排名不分先后。
Swift 官方文档中文翻译由 [numbbbbb](https://github.com/numbbbbb) 发起并主导,本项目已经得到了苹果官方的 [认可](https://swift.org/documentation/)Translations 部分)。下面是各个版本官方文档翻译和校对工作的主要贡献者,排名不分先后。
## Swift 5.x 主要贡献者
@ -12,11 +12,18 @@ Swift 官方文档中文翻译工作由[numbbbbb](https://github.com/numbbbbb)
- [DarrenChen123](https://github.com/DarrenChen123)
- [dzyding](https://github.com/dzyding)
- [Hale](https://github.com/wuqiuhao)
- [Joeytat](https://github.com/joeytat)
- [jojotov](https://github.com/jojotov)
- [Licardo](https://github.com/L1cardo)
- [Khala-wan](https://github.com/Khala-wan)
- [Nemocdz](https://github.com/Nemocdz)
- [numbbbbb](https://github.com/numbbbbb)
- [pmst](https://github.com/colourful987)
- [Phenmod](https://github.com/Phenmod)
- [RickeyBoy](https://github.com/RickeyBoy)
- [SunsetWan](https://github.com/SunsetWan)
- [WAMaker](https://github.com/WAMaker)
- [YiYiZheng](https://github.com/YiYiZheng)
- [Yousanflics](https://github.com/Yousanflics)
## Swift 4.x 主要贡献者
@ -143,4 +150,3 @@ Swift 官方文档中文翻译工作由[numbbbbb](https://github.com/numbbbbb)
- [zqp](https://github.com/zqp)
- [成都老码团队翻译组-Arya](http://weibo.com/littlekok/)
- [成都老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)

View File

@ -11,7 +11,7 @@
> 小明说哇靠学姐你还会妖法......
Swift 语言从 Xcode 6 beta 5版本起加入了对权限控制Access Control的支持。其实权限控制和小明的物品一样你可以设定水壶是只有自己能用还是只有宿舍里的人能用还是全校都可以用。
Swift 语言从 Xcode 6 beta 5 版本起加入了对权限控制Access Control的支持。其实权限控制和小明的物品一样你可以设定水壶是只有自己能用还是只有宿舍里的人能用还是全校都可以用。
从此以后,你可以好像神盾局局长一样,完全掌控自己的代码块的”保密级别“,哪些是只能在本文件引用,哪些能用在整个项目里,你还可以发挥大爱精神,把它开源成只要导入你的框架,大家都可以使用的 API。