Compare commits
230 Commits
gitbook
...
Ch-Enumera
| Author | SHA1 | Date | |
|---|---|---|---|
| ddaa180905 | |||
| aba2a5fb55 | |||
| 096f2acfc4 | |||
| 8b99054c0c | |||
| d9d2952e85 | |||
| 9b302e76cb | |||
| 9d8588726a | |||
| 59e95dc3a5 | |||
| bb4d0c7fcd | |||
| e0c37b748c | |||
| bf18871349 | |||
| 13c47f495d | |||
| 6b38f6d7a7 | |||
| 262a06affd | |||
| 558814b61d | |||
| 2408ee2066 | |||
| 7725dfb9ad | |||
| 2e82e1621a | |||
| f2999daf52 | |||
| 83500eba22 | |||
| be7dca4e18 | |||
| 99453d1d4e | |||
| 72cb5a61b8 | |||
| c9d8b0be6d | |||
| 9abdbd4e4a | |||
| a6656c4221 | |||
| b73ff1765c | |||
| 528f2109de | |||
| bf8ac090d8 | |||
| e7d525781a | |||
| fee0c4a388 | |||
| da42305134 | |||
| 9a84b62fab | |||
| 9683a0b82f | |||
| a00bd5b888 | |||
| 2fd7ccd829 | |||
| f5c85a2eed | |||
| 65f605fc00 | |||
| 6f2488e19d | |||
| 7f94b446ef | |||
| 50c356dade | |||
| aa77e5cdd6 | |||
| 31a51ccace | |||
| 4fff00bd5c | |||
| e18e9c422a | |||
| ff8a9ca1a4 | |||
| 51b864f711 | |||
| 67df77108c | |||
| f7c9433bff | |||
| 05e7dafe9e | |||
| 764753697c | |||
| 2aef26f66a | |||
| dcc5837a53 | |||
| d21f043b64 | |||
| 52c81ff7b1 | |||
| 2c5d4a51ff | |||
| f47766a015 | |||
| 08e7cf1e63 | |||
| 3d38eaa8a0 | |||
| e91cdd5d29 | |||
| ad085a8563 | |||
| 39bf300bd4 | |||
| 77b1824d6b | |||
| 93c7fada67 | |||
| 72829a29b9 | |||
| 9a2bc92a6b | |||
| f333332ee6 | |||
| 5bf3e735a5 | |||
| f82f40fd47 | |||
| b894831e80 | |||
| 4ea6ea7d7a | |||
| cc9f46c3dc | |||
| c945edb4e5 | |||
| 7d2b949236 | |||
| f416e311eb | |||
| e59b995eda | |||
| 89aa08ab57 | |||
| 670cef47ed | |||
| 55001fd340 | |||
| d54d3a6c78 | |||
| ec456eac27 | |||
| d4f1442dc9 | |||
| f6da2e83a6 | |||
| c4f7fac162 | |||
| 10ff6be465 | |||
| 028268e7a6 | |||
| b31fa0ef60 | |||
| caa9beebee | |||
| 1489d076e0 | |||
| 9a48057873 | |||
| 92c89aa4da | |||
| 7220830354 | |||
| cb11ad439e | |||
| 1f2ffa1668 | |||
| 453266a7c1 | |||
| 8e7dbad8a7 | |||
| e8ae235524 | |||
| e5e81b81d1 | |||
| f7b9eb5e1e | |||
| 663a21536b | |||
| 7a115a996a | |||
| 9988f57550 | |||
| 32fa806e77 | |||
| 273a7bdd3b | |||
| 3063f18a78 | |||
| 1f0844bb36 | |||
| 8d0a87909e | |||
| dfd3383bd0 | |||
| 7656143f00 | |||
| eeda1ed0ed | |||
| 3434b008eb | |||
| 1704931ac4 | |||
| b0a9b63e1f | |||
| 2b715ddb06 | |||
| 05be6d4ba7 | |||
| 631a2cbe92 | |||
| 5d3d15b836 | |||
| 11aa5ab0d6 | |||
| 028a6aa795 | |||
| 7ba5a56301 | |||
| 4dcdd13e74 | |||
| 8f53cc7d82 | |||
| 48373d7791 | |||
| 603a40adb0 | |||
| 06e3e12621 | |||
| 5392b093d2 | |||
| 8b743fafe5 | |||
| 30db13bc1e | |||
| d34aee47cd | |||
| 64f80c9cba | |||
| db48920fa3 | |||
| 9284579c0b | |||
| 20f0cb7f81 | |||
| ca027e9132 | |||
| 6d6118e7a3 | |||
| d1b4d709b6 | |||
| 0e784e110d | |||
| 7bd73172ed | |||
| f3a4630915 | |||
| 02e0b33fd6 | |||
| 38ba1198b7 | |||
| eedcd69d28 | |||
| b7a8566a0f | |||
| ebda86cd05 | |||
| 546db6bc47 | |||
| 3d64a8e5e6 | |||
| b95202eb6d | |||
| ed7eb9e045 | |||
| 15ee26ca6d | |||
| dca10b1aa0 | |||
| 2d10a8d6ec | |||
| 45c537a42f | |||
| a6d6e468f6 | |||
| 72708963ab | |||
| 22265fa31d | |||
| 771c5b3da0 | |||
| 0369884f87 | |||
| 75e0837fb0 | |||
| 0afcd2ebf1 | |||
| d07fdbfb98 | |||
| 21e80f09b5 | |||
| 2b140081de | |||
| 60cc810915 | |||
| 68e51d7f57 | |||
| 4381756f0f | |||
| 96f3c84b8a | |||
| cc029194be | |||
| ae117bdfbc | |||
| 5fc4252bfc | |||
| 9ec61ba488 | |||
| fc89e3ca5d | |||
| 9a629a57e3 | |||
| 787687cb1e | |||
| b699654ab8 | |||
| d902be8367 | |||
| d90c50ec74 | |||
| 4894951c3b | |||
| b5defb4c0e | |||
| b1887d01ac | |||
| 5f0da139f0 | |||
| 53eaaeb19c | |||
| c9c9bb980e | |||
| 51ceab425c | |||
| 16cae8adf6 | |||
| bab43e7db6 | |||
| 4cace74528 | |||
| b807a6edb9 | |||
| 1a6d56d228 | |||
| 004fc0906d | |||
| 850cf97f83 | |||
| d6862fa4dc | |||
| d3efb989d3 | |||
| 0f2ab87dca | |||
| 9a6f016f45 | |||
| 2164db3b8b | |||
| fb2dfa6764 | |||
| d8068f4325 | |||
| 4e4e291956 | |||
| 2f430495d4 | |||
| 7b123505db | |||
| e7def896cb | |||
| 5fbfc38fd7 | |||
| b26f8aacff | |||
| b76f8cf111 | |||
| ca87420665 | |||
| 93c390dd5a | |||
| 7020682acd | |||
| aa91ad4692 | |||
| 367cece0f5 | |||
| 3f0cc6c047 | |||
| 65dc6b133f | |||
| b58d744852 | |||
| e9aaa026c6 | |||
| 1167a215c9 | |||
| f5c9d9c5e5 | |||
| 71f0757ba0 | |||
| afc8cddb0e | |||
| a3a7770b9a | |||
| 1ffeeeedaf | |||
| b991a4eb75 | |||
| e3c9bd7992 | |||
| 425d534e7f | |||
| e0af10bc8e | |||
| 3210e30972 | |||
| e82cea3866 | |||
| 497d68be37 | |||
| cb2ebd282b | |||
| 9fd540b8d7 | |||
| 86690d4472 | |||
| de559ce2d8 |
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal 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']
|
||||
66
README.md
66
README.md
@ -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.2,2020-02-15
|
||||
- 更新到 Swift 5.1,2019-11-11
|
||||
- 更新到 Swift 5.0,2019-04-05
|
||||
- 更新到 Swift 4.2,2019-01-29
|
||||
- 更新到 Swift 4.1,2018-04-12,感谢 [@Mylittleswift](https://github.com/Mylittleswift)
|
||||
- 更新到 Swift 3.0,2016-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 | 运行时 |
|
||||
|
||||
# 贡献者
|
||||
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
# Summary
|
||||
|
||||
* [Introduction](README.md)
|
||||
|
||||
@ -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())
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
BIN
document/TheSwiftProgrammingLanguageSwift51.epub
Normal file
BIN
document/TheSwiftProgrammingLanguageSwift51.epub
Normal file
Binary file not shown.
BIN
document/TheSwiftProgrammingLanguageSwift52.epub
Normal file
BIN
document/TheSwiftProgrammingLanguageSwift52.epub
Normal file
Binary file not shown.
BIN
document/TheSwiftProgrammingLanguageSwift53.epub
Normal file
BIN
document/TheSwiftProgrammingLanguageSwift53.epub
Normal file
Binary file not shown.
@ -1 +0,0 @@
|
||||
google-site-verification: googleb0a4f5a22e9cb82f.html
|
||||
13
index.html
13
index.html
@ -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>
|
||||
|
||||
4
source/.gitignore
vendored
4
source/.gitignore
vendored
@ -1,4 +0,0 @@
|
||||
_book
|
||||
*.pdf
|
||||
*.epub
|
||||
*.mobi
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
Swift 是一种非常好的编写软件的方式,无论是手机,台式机,服务器,还是其他运行代码的设备。它是一种安全,快速和互动的编程语言,将现代编程语言的精华和苹果工程师文化的智慧,以及来自开源社区的多样化贡献结合了起来。编译器对性能进行了优化,编程语言对开发进行了优化,两者互不干扰,鱼与熊掌兼得。
|
||||
|
||||
Swift 对于初学者来说也很友好。它是第一个既满足工业标准又像脚本语言一样充满表现力和趣味的系统编程语言。它支持代码预览(playgrounds),这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。
|
||||
Swift 对于初学者来说也很友好。它是一门满足工业标准的编程语言,但又有着脚本语言般的表达力和可玩性。它支持代码预览(playgrounds),这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行 Swift 代码并实时查看结果。
|
||||
|
||||
Swift 通过采用现代编程模式来避免大量常见编程错误:
|
||||
|
||||
13
source/01_welcome_to_swift/02_version_compatibility.md
Executable file
13
source/01_welcome_to_swift/02_version_compatibility.md
Executable 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。
|
||||
@ -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,15 +797,16 @@ 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 {
|
||||
for lhsItem in lhs {
|
||||
for rhsItem in rhs {
|
||||
if lhsItem == rhsItem {
|
||||
return true
|
||||
}
|
||||
where T.Element: Equatable, T.Element == U.Element
|
||||
{
|
||||
for lhsItem in lhs {
|
||||
for rhsItem in rhs {
|
||||
if lhsItem == rhsItem {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
anyCommonElements([1, 2, 3], [3])
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,479 +1,479 @@
|
||||
# 基本运算符
|
||||
|
||||
*运算符*是检查、改变、合并值的特殊符号或短语。例如,加号(`+`)将两个数相加(如 `let i = 1 + 2`)。更复杂的运算例子包括逻辑与运算符 `&&`(如 `if enteredDoorCode && passedRetinaScan`)。
|
||||
|
||||
Swift 支持大部分标准 C 语言的运算符,且为了减少常见编码错误做了部分改进。如:赋值符(`=`)不再有返回值,这样就消除了手误将判等运算符(`==`)写成赋值符导致代码错误的缺陷。算术运算符(`+`,`-`,`*`,`/`,`%` 等)的结果会被检测并禁止值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见[溢出运算符](./26_Advanced_Operators.md#overflow_operators)。
|
||||
|
||||
Swift 还提供了 C 语言没有的区间运算符,例如 `a..<b` 或 `a...b`,这方便我们表达一个区间内的数值。
|
||||
|
||||
本章节只描述了 Swift 中的基本运算符,[高级运算符](./26_Advanced_Operators.md)这章会包含 Swift 中的高级运算符,及如何自定义运算符,及如何进行自定义类型的运算符重载。
|
||||
|
||||
## 术语 {#terminology}
|
||||
|
||||
运算符分为一元、二元和三元运算符:
|
||||
|
||||
- *一元*运算符对单一操作对象操作(如 `-a`)。一元运算符分前置运算符和后置运算符,*前置运算符*需紧跟在操作对象之前(如 `!b`),*后置运算符*需紧跟在操作对象之后(如 `c!`)。
|
||||
- *二元*运算符操作两个操作对象(如 `2 + 3`),是*中置*的,因为它们出现在两个操作对象之间。
|
||||
- *三元*运算符操作三个操作对象,和 C 语言一样,Swift 只有一个三元运算符,就是三目运算符(`a ? b : c`)。
|
||||
|
||||
受运算符影响的值叫*操作数*,在表达式 `1 + 2` 中,加号 `+` 是二元运算符,它的两个操作数是值 `1` 和 `2`。
|
||||
|
||||
## 赋值运算符 {#assignment-operator}
|
||||
|
||||
*赋值运算符*(`a = b`),表示用 `b` 的值来初始化或更新 `a` 的值:
|
||||
|
||||
```swift
|
||||
let b = 10
|
||||
var a = 5
|
||||
a = b
|
||||
// a 现在等于 10
|
||||
```
|
||||
|
||||
如果赋值的右边是一个多元组,它的元素可以马上被分解成多个常量或变量:
|
||||
|
||||
```swift
|
||||
let (x, y) = (1, 2)
|
||||
// 现在 x 等于 1,y 等于 2
|
||||
```
|
||||
|
||||
与 C 语言和 Objective-C 不同,Swift 的赋值操作并不返回任何值。所以下面语句是无效的:
|
||||
|
||||
```swift
|
||||
if x = y {
|
||||
// 此句错误,因为 x = y 并不返回任何值
|
||||
}
|
||||
```
|
||||
|
||||
通过将 `if x = y` 标记为无效语句,Swift 能帮你避免把 (`==`)错写成(`=`)这类错误的出现。
|
||||
|
||||
## 算术运算符 {#arithmetic-operators}
|
||||
|
||||
Swift 中所有数值类型都支持了基本的四则*算术运算符*:
|
||||
|
||||
- 加法(`+`)
|
||||
- 减法(`-`)
|
||||
- 乘法(`*`)
|
||||
- 除法(`/`)
|
||||
|
||||
```swift
|
||||
1 + 2 // 等于 3
|
||||
5 - 3 // 等于 2
|
||||
2 * 3 // 等于 6
|
||||
10.0 / 2.5 // 等于 4.0
|
||||
```
|
||||
|
||||
与 C 语言和 Objective-C 不同的是,Swift 默认情况下不允许在数值运算中出现溢出情况。但是你可以使用 Swift 的溢出运算符来实现溢出运算(如 `a &+ b`)。详情参见[溢出运算符](./26_Advanced_Operators.md#overflow_operators)。
|
||||
|
||||
加法运算符也可用于 `String` 的拼接:
|
||||
|
||||
```swift
|
||||
"hello, " + "world" // 等于 "hello, world"
|
||||
```
|
||||
|
||||
### 求余运算符 {#remainder-operator}
|
||||
|
||||
*求余运算符*(`a % b`)是计算 `b` 的多少倍刚刚好可以容入 `a`,返回多出来的那部分(余数)。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 求余运算符(`%`)在其他语言也叫*取模运算符*。但是严格说来,我们看该运算符对负数的操作结果,「求余」比「取模」更合适些。
|
||||
|
||||
我们来谈谈取余是怎么回事,计算 `9 % 4`,你先计算出 `4` 的多少倍会刚好可以容入 `9` 中:
|
||||
|
||||

|
||||
|
||||
你可以在 `9` 中放入两个 `4`,那余数是 1(用橙色标出)。
|
||||
|
||||
在 Swift 中可以表达为:
|
||||
|
||||
```swift
|
||||
9 % 4 // 等于 1
|
||||
```
|
||||
|
||||
为了得到 `a % b` 的结果,`%` 计算了以下等式,并输出 `余数`作为结果:
|
||||
|
||||
a = (b × 倍数) + 余数
|
||||
|
||||
当 `倍数`取最大值的时候,就会刚好可以容入 `a` 中。
|
||||
|
||||
把 `9` 和 `4` 代入等式中,我们得 `1`:
|
||||
|
||||
9 = (4 × 2) + 1
|
||||
|
||||
同样的方法,我们来计算 `-9 % 4`:
|
||||
|
||||
```swift
|
||||
-9 % 4 // 等于 -1
|
||||
```
|
||||
|
||||
把 `-9` 和 `4` 代入等式,`-2` 是取到的最大整数:
|
||||
|
||||
-9 = (4 × -2) + -1
|
||||
|
||||
余数是 `-1`。
|
||||
|
||||
在对负数 `b` 求余时,`b` 的符号会被忽略。这意味着 `a % b` 和 `a % -b` 的结果是相同的。
|
||||
|
||||
### 一元负号运算符 {#unary-minus-operator}
|
||||
|
||||
数值的正负号可以使用前缀 `-`(即*一元负号符*)来切换:
|
||||
|
||||
```swift
|
||||
let three = 3
|
||||
let minusThree = -three // minusThree 等于 -3
|
||||
let plusThree = -minusThree // plusThree 等于 3, 或 "负负3"
|
||||
```
|
||||
|
||||
一元负号符(`-`)写在操作数之前,中间没有空格。
|
||||
|
||||
### 一元正号运算符 {#unary-plus-operator}
|
||||
|
||||
*一元正号符*(`+`)不做任何改变地返回操作数的值:
|
||||
|
||||
```swift
|
||||
let minusSix = -6
|
||||
let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6
|
||||
```
|
||||
|
||||
虽然一元正号符什么都不会改变,但当你在使用一元负号来表达负数时,你可以使用一元正号来表达正数,如此你的代码会具有对称美。
|
||||
|
||||
## 组合赋值运算符 {#compound-assignment-operators}
|
||||
|
||||
如同 C 语言,Swift 也提供把其他运算符和赋值运算(`=`)组合的*组合赋值运算符*,组合加运算(`+=`)是其中一个例子:
|
||||
|
||||
```swift
|
||||
var a = 1
|
||||
a += 2
|
||||
// a 现在是 3
|
||||
```
|
||||
|
||||
表达式 `a += 2` 是 `a = a + 2` 的简写,一个组合加运算就是把加法运算和赋值运算组合成进一个运算符里,同时完成两个运算任务。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 复合赋值运算没有返回值,`let b = a += 2` 这类代码是错误。这不同于上面提到的自增和自减运算符。
|
||||
|
||||
更多 Swift 标准库运算符的信息,请看[运算符声明](https://developer.apple.com/documentation/swift/operator_declarations)。
|
||||
|
||||
## 比较运算符(Comparison Operators) {#comparison-operators}
|
||||
|
||||
所有标准 C 语言中的*比较运算符*都可以在 Swift 中使用:
|
||||
|
||||
- 等于(`a == b`)
|
||||
- 不等于(`a != b`)
|
||||
- 大于(`a > b`)
|
||||
- 小于(`a < b`)
|
||||
- 大于等于(`a >= b`)
|
||||
- 小于等于(`a <= b`)
|
||||
|
||||
> 注意
|
||||
>
|
||||
> Swift 也提供恒等(`===`)和不恒等(`!==`)这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在[类与结构](./09_Classes_and_Structures.md)章节的 **Identity Operators** 部分。
|
||||
|
||||
每个比较运算都返回了一个标识表达式是否成立的布尔值:
|
||||
|
||||
```swift
|
||||
1 == 1 // true, 因为 1 等于 1
|
||||
2 != 1 // true, 因为 2 不等于 1
|
||||
2 > 1 // true, 因为 2 大于 1
|
||||
1 < 2 // true, 因为 1 小于2
|
||||
1 >= 1 // true, 因为 1 大于等于 1
|
||||
2 <= 1 // false, 因为 2 并不小于等于 1
|
||||
```
|
||||
|
||||
比较运算多用于条件语句,如 `if` 条件:
|
||||
|
||||
```swift
|
||||
let name = "world"
|
||||
if name == "world" {
|
||||
print("hello, world")
|
||||
} else {
|
||||
print("I'm sorry \(name), but I don't recognize you")
|
||||
}
|
||||
// 输出“hello, world", 因为 `name` 就是等于 "world”
|
||||
```
|
||||
|
||||
关于 `if` 语句,请看[控制流](./05_Control_Flow.md)。
|
||||
|
||||
如果两个元组的元素相同,且长度相同的话,元组就可以被比较。比较元组大小会按照从左到右、逐值比较的方式,直到发现有两个值不等时停止。如果所有的值都相等,那么这一对元组我们就称它们是相等的。例如:
|
||||
|
||||
```swift
|
||||
(1, "zebra") < (2, "apple") // true,因为 1 小于 2
|
||||
(3, "apple") < (3, "bird") // true,因为 3 等于 3,但是 apple 小于 bird
|
||||
(4, "dog") == (4, "dog") // true,因为 4 等于 4,dog 等于 dog
|
||||
```
|
||||
|
||||
在上面的例子中,你可以看到,在第一行中从左到右的比较行为。因为 `1` 小于 `2`,所以 `(1, "zebra")` 小于 `(2, "apple")`,不管元组剩下的值如何。所以 `"zebra"` 大于 `"apple"` 对结果没有任何影响,因为元组的比较结果已经被第一个元素决定了。不过,当元组的第一个元素相同时候,第二个元素将会用作比较-第二行和第三行代码就发生了这样的比较。
|
||||
|
||||
当元组中的元素都可以被比较时,你也可以使用这些运算符来比较它们的大小。例如,像下面展示的代码,你可以比较两个类型为 `(String, Int)` 的元组,因为 `Int` 和 `String` 类型的值可以比较。相反,`Bool` 不能被比较,也意味着存有布尔类型的元组不能被比较。
|
||||
|
||||
```swift
|
||||
("blue", -1) < ("purple", 1) // 正常,比较的结果为 true
|
||||
("blue", false) < ("purple", true) // 错误,因为 < 不能比较布尔类型
|
||||
```
|
||||
|
||||
> 注意
|
||||
>
|
||||
> Swift 标准库只能比较七个以内元素的元组比较函数。如果你的元组元素超过七个时,你需要自己实现比较运算符。
|
||||
|
||||
## 三元运算符(Ternary Conditional Operator) {#ternary-conditional-operator}
|
||||
|
||||
*三元运算符*的特殊在于它是有三个操作数的运算符,它的形式是 `问题 ? 答案 1 : 答案 2`。它简洁地表达根据 `问题`成立与否作出二选一的操作。如果 `问题` 成立,返回 `答案 1` 的结果;反之返回 `答案 2` 的结果。
|
||||
|
||||
三元运算符是以下代码的缩写形式:
|
||||
|
||||
```swift
|
||||
if question {
|
||||
answer1
|
||||
} else {
|
||||
answer2
|
||||
}
|
||||
```
|
||||
|
||||
这里有个计算表格行高的例子。如果有表头,那行高应比内容高度要高出 50 点;如果没有表头,只需高出 20 点:
|
||||
|
||||
```swift
|
||||
let contentHeight = 40
|
||||
let hasHeader = true
|
||||
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
|
||||
// rowHeight 现在是 90
|
||||
```
|
||||
|
||||
上面的写法比下面的代码更简洁:
|
||||
|
||||
```swift
|
||||
let contentHeight = 40
|
||||
let hasHeader = true
|
||||
var rowHeight = contentHeight
|
||||
if hasHeader {
|
||||
rowHeight = rowHeight + 50
|
||||
} else {
|
||||
rowHeight = rowHeight + 20
|
||||
}
|
||||
// rowHeight 现在是 90
|
||||
```
|
||||
|
||||
第一段代码例子使用了三元运算,所以一行代码就能让我们得到正确答案。这比第二段代码简洁得多,无需将 `rowHeight` 定义成变量,因为它的值无需在 `if` 语句中改变。
|
||||
|
||||
三元运算为二选一场景提供了一个非常便捷的表达形式。不过需要注意的是,滥用三元运算符会降低代码可读性。所以我们应避免在一个复合语句中使用多个三元运算符。
|
||||
|
||||
## 空合运算符(Nil Coalescing Operator) {#nil-coalescing-operator}
|
||||
|
||||
*空合运算符*(`a ?? b`)将对可选类型 `a` 进行空判断,如果 `a` 包含一个值就进行解包,否则就返回一个默认值 `b`。表达式 `a` 必须是 Optional 类型。默认值 `b` 的类型必须要和 `a` 存储值的类型保持一致。
|
||||
|
||||
空合运算符是对以下代码的简短表达方法:
|
||||
|
||||
```swift
|
||||
a != nil ? a! : b
|
||||
```
|
||||
|
||||
上述代码使用了三元运算符。当可选类型 `a` 的值不为空时,进行强制解封(`a!`),访问 `a` 中的值;反之返回默认值 `b`。无疑空合运算符(`??`)提供了一种更为优雅的方式去封装条件判断和解封两种行为,显得简洁以及更具可读性。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 如果 `a` 为非空值(`non-nil`),那么值 `b` 将不会被计算。这也就是所谓的*短路求值*。
|
||||
|
||||
下文例子采用空合运算符,实现了在默认颜色名和可选自定义颜色名之间抉择:
|
||||
|
||||
```swift
|
||||
let defaultColorName = "red"
|
||||
var userDefinedColorName: String? //默认值为 nil
|
||||
|
||||
var colorNameToUse = userDefinedColorName ?? defaultColorName
|
||||
// userDefinedColorName 的值为空,所以 colorNameToUse 的值为 "red"
|
||||
```
|
||||
|
||||
`userDefinedColorName` 变量被定义为一个可选的 `String` 类型,默认值为 `nil`。由于 `userDefinedColorName` 是一个可选类型,我们可以使用空合运算符去判断其值。在上一个例子中,通过空合运算符为一个名为 `colorNameToUse` 的变量赋予一个字符串类型初始值。
|
||||
由于 `userDefinedColorName` 值为空,因此表达式 `userDefinedColorName ?? defaultColorName` 返回 `defaultColorName` 的值,即 `red`。
|
||||
|
||||
如果你分配一个非空值(`non-nil`)给 `userDefinedColorName`,再次执行空合运算,运算结果为封包在 `userDefaultColorName` 中的值,而非默认值。
|
||||
|
||||
```swift
|
||||
userDefinedColorName = "green"
|
||||
colorNameToUse = userDefinedColorName ?? defaultColorName
|
||||
// userDefinedColorName 非空,因此 colorNameToUse 的值为 "green"
|
||||
```
|
||||
|
||||
## 区间运算符(Range Operators) {#range-operators}
|
||||
|
||||
Swift 提供了几种方便表达一个区间的值的*区间运算符*。
|
||||
|
||||
### 闭区间运算符 {#closed-range-operator}
|
||||
|
||||
*闭区间运算符*(`a...b`)定义一个包含从 `a` 到 `b`(包括 `a` 和 `b`)的所有值的区间。`a` 的值不能超过 `b`。
|
||||
|
||||
闭区间运算符在迭代一个区间的所有值时是非常有用的,如在 `for-in` 循环中:
|
||||
|
||||
```swift
|
||||
for index in 1...5 {
|
||||
print("\(index) * 5 = \(index * 5)")
|
||||
}
|
||||
// 1 * 5 = 5
|
||||
// 2 * 5 = 10
|
||||
// 3 * 5 = 15
|
||||
// 4 * 5 = 20
|
||||
// 5 * 5 = 25
|
||||
```
|
||||
|
||||
关于 `for-in` 循环,请看[控制流](./05_Control_Flow.md)。
|
||||
|
||||
### 半开区间运算符 {#half-open-range-operator}
|
||||
|
||||
*半开区间运算符*(`a..<b`)定义一个从 `a` 到 `b` 但不包括 `b` 的区间。
|
||||
之所以称为*半开区间*,是因为该区间包含第一个值而不包括最后的值。
|
||||
|
||||
半开区间的实用性在于当你使用一个从 0 开始的列表(如数组)时,非常方便地从0数到列表的长度。
|
||||
|
||||
```swift
|
||||
let names = ["Anna", "Alex", "Brian", "Jack"]
|
||||
let count = names.count
|
||||
for i in 0..<count {
|
||||
print("第 \(i + 1) 个人叫 \(names[i])")
|
||||
}
|
||||
// 第 1 个人叫 Anna
|
||||
// 第 2 个人叫 Alex
|
||||
// 第 3 个人叫 Brian
|
||||
// 第 4 个人叫 Jack
|
||||
```
|
||||
|
||||
数组有 4 个元素,但 `0..<count` 只数到3(最后一个元素的下标),因为它是半开区间。关于数组,请查阅[数组](./04_Collection_Types.md#arrays)。
|
||||
|
||||
### 单侧区间 {#one-sided-ranges}
|
||||
|
||||
闭区间操作符有另一个表达形式,可以表达往一侧无限延伸的区间 —— 例如,一个包含了数组从索引 2 到结尾的所有值的区间。在这些情况下,你可以省略掉区间操作符一侧的值。这种区间叫做单侧区间,因为操作符只有一侧有值。例如:
|
||||
|
||||
```swift
|
||||
for name in names[2...] {
|
||||
print(name)
|
||||
}
|
||||
// Brian
|
||||
// Jack
|
||||
|
||||
for name in names[...2] {
|
||||
print(name)
|
||||
}
|
||||
// Anna
|
||||
// Alex
|
||||
// Brian
|
||||
```
|
||||
|
||||
半开区间操作符也有单侧表达形式,附带上它的最终值。就像你使用区间去包含一个值,最终值并不会落在区间内。例如:
|
||||
|
||||
```swift
|
||||
for name in names[..<2] {
|
||||
print(name)
|
||||
}
|
||||
// Anna
|
||||
// Alex
|
||||
```
|
||||
|
||||
单侧区间不止可以在下标里使用,也可以在别的情境下使用。你不能遍历省略了初始值的单侧区间,因为遍历的开端并不明显。你可以遍历一个省略最终值的单侧区间;然而,由于这种区间无限延伸的特性,请保证你在循环里有一个结束循环的分支。你也可以查看一个单侧区间是否包含某个特定的值,就像下面展示的那样。
|
||||
|
||||
```swift
|
||||
let range = ...5
|
||||
range.contains(7) // false
|
||||
range.contains(4) // true
|
||||
range.contains(-1) // true
|
||||
```
|
||||
|
||||
## 逻辑运算符(Logical Operators) {#logical-operators}
|
||||
|
||||
*逻辑运算符*的操作对象是逻辑布尔值。Swift 支持基于 C 语言的三个标准逻辑运算。
|
||||
|
||||
- 逻辑非(`!a`)
|
||||
- 逻辑与(`a && b`)
|
||||
- 逻辑或(`a || b`)
|
||||
|
||||
### 逻辑非运算符
|
||||
|
||||
*逻辑非运算符*(`!a`)对一个布尔值取反,使得 `true` 变 `false`,`false` 变 `true`。
|
||||
|
||||
它是一个前置运算符,需紧跟在操作数之前,且不加空格。读作 `非 a` ,例子如下:
|
||||
|
||||
```swift
|
||||
let allowedEntry = false
|
||||
if !allowedEntry {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出“ACCESS DENIED”
|
||||
```
|
||||
|
||||
`if !allowedEntry` 语句可以读作「如果非 allowedEntry」,接下一行代码只有在「非 allowedEntry」为 `true`,即 `allowEntry` 为 `false` 时被执行。
|
||||
|
||||
在示例代码中,小心地选择布尔常量或变量有助于代码的可读性,并且避免使用双重逻辑非运算,或混乱的逻辑语句。
|
||||
|
||||
### 逻辑与运算符 #{logical_and_operator}
|
||||
|
||||
*逻辑与运算符*(`a && b`)表达了只有 `a` 和 `b` 的值都为 `true` 时,整个表达式的值才会是 `true`。
|
||||
|
||||
只要任意一个值为 `false`,整个表达式的值就为 `false`。事实上,如果第一个值为 `false`,那么是不去计算第二个值的,因为它已经不可能影响整个表达式的结果了。这被称做*短路计算(short-circuit evaluation)*。
|
||||
|
||||
以下例子,只有两个 `Bool` 值都为 `true` 的时候才允许进入 if:
|
||||
|
||||
```swift
|
||||
let enteredDoorCode = true
|
||||
let passedRetinaScan = false
|
||||
if enteredDoorCode && passedRetinaScan {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出“ACCESS DENIED”
|
||||
```
|
||||
|
||||
### 逻辑或运算符 #{logical_or_operator}
|
||||
|
||||
逻辑或运算符(`a || b`)是一个由两个连续的 `|` 组成的中置运算符。它表示了两个逻辑表达式的其中一个为 `true`,整个表达式就为 `true`。
|
||||
|
||||
同逻辑与运算符类似,逻辑或也是「短路计算」的,当左端的表达式为 `true` 时,将不计算右边的表达式了,因为它不可能改变整个表达式的值了。
|
||||
|
||||
以下示例代码中,第一个布尔值(`hasDoorKey`)为 `false`,但第二个值(`knowsOverridePassword`)为 `true`,所以整个表达是 `true`,于是允许进入:
|
||||
|
||||
```swift
|
||||
let hasDoorKey = false
|
||||
let knowsOverridePassword = true
|
||||
if hasDoorKey || knowsOverridePassword {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出“Welcome!”
|
||||
```
|
||||
|
||||
### 逻辑运算符组合计算 {#combining-logical-operators}
|
||||
|
||||
我们可以组合多个逻辑运算符来表达一个复合逻辑:
|
||||
|
||||
```swift
|
||||
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出“Welcome!”
|
||||
```
|
||||
|
||||
这个例子使用了含多个 `&&` 和 `||` 的复合逻辑。但无论怎样,`&&` 和 `||` 始终只能操作两个值。所以这实际是三个简单逻辑连续操作的结果。我们来解读一下:
|
||||
|
||||
如果我们输入了正确的密码并通过了视网膜扫描,或者我们有一把有效的钥匙,又或者我们知道紧急情况下重置的密码,我们就能把门打开进入。
|
||||
|
||||
前两种情况,我们都不满足,所以前两个简单逻辑的结果是 `false`,但是我们是知道紧急情况下重置的密码的,所以整个复杂表达式的值还是 `true`。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> Swift 逻辑操作符 `&&` 和 `||` 是左结合的,这意味着拥有多元逻辑操作符的复合表达式优先计算最左边的子表达式。
|
||||
|
||||
### 使用括号来明确优先级 {#explicit-parentheses}
|
||||
|
||||
为了一个复杂表达式更容易读懂,在合适的地方使用括号来明确优先级是很有效的,虽然它并非必要的。在上个关于门的权限的例子中,我们给第一个部分加个括号,使它看起来逻辑更明确:
|
||||
|
||||
```swift
|
||||
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出“Welcome!”
|
||||
```
|
||||
|
||||
这括号使得前两个值被看成整个逻辑表达中独立的一个部分。虽然有括号和没括号的输出结果是一样的,但对于读代码的人来说有括号的代码更清晰。可读性比简洁性更重要,请在可以让你代码变清晰的地方加个括号吧!
|
||||
# 基本运算符
|
||||
|
||||
*运算符*是检查、改变、合并值的特殊符号或短语。例如,加号(`+`)将两个数相加(如 `let i = 1 + 2`)。更复杂的运算例子包括逻辑与运算符 `&&`(如 `if enteredDoorCode && passedRetinaScan`)。
|
||||
|
||||
Swift 所支持运算符你可能在别的语言比如 C 语言里已经认识了,同时为了减少常见编码错误对它们做了部分改进。如:赋值符(`=`)不再有返回值,这样就消除了手误将判等运算符(`==`)写成赋值符导致代码错误的缺陷。算术运算符(`+`,`-`,`*`,`/`,`%` 等)的结果会被检测并禁止值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出。详情参见 [溢出运算符](./27_Advanced_Operators.md#overflow-operators)。
|
||||
|
||||
Swift 还提供了 C 语言没有的区间运算符,例如 `a..<b` 或 `a...b`,这方便我们表达一个区间内的数值。
|
||||
|
||||
本章节只描述了 Swift 中的基本运算符,[高级运算符](./27_Advanced_Operators.md) 这章会包含 Swift 中的高级运算符,及如何自定义运算符,及如何进行自定义类型的运算符重载。
|
||||
|
||||
## 术语 {#terminology}
|
||||
|
||||
运算符分为一元、二元和三元运算符:
|
||||
|
||||
- *一元*运算符对单一操作对象操作(如 `-a`)。一元运算符分前置运算符和后置运算符,*前置运算符*需紧跟在操作对象之前(如 `!b`),*后置运算符*需紧跟在操作对象之后(如 `c!`)。
|
||||
- *二元*运算符操作两个操作对象(如 `2 + 3`),是*中置*的,因为它们出现在两个操作对象之间。
|
||||
- *三元*运算符操作三个操作对象,和 C 语言一样,Swift 只有一个三元运算符,就是三目运算符(`a ? b : c`)。
|
||||
|
||||
受运算符影响的值叫*操作数*,在表达式 `1 + 2` 中,加号 `+` 是二元运算符,它的两个操作数是值 `1` 和 `2`。
|
||||
|
||||
## 赋值运算符 {#assignment-operator}
|
||||
|
||||
*赋值运算符*(`a = b`),表示用 `b` 的值来初始化或更新 `a` 的值:
|
||||
|
||||
```swift
|
||||
let b = 10
|
||||
var a = 5
|
||||
a = b
|
||||
// a 现在等于 10
|
||||
```
|
||||
|
||||
如果赋值的右边是一个多元组,它的元素可以马上被分解成多个常量或变量:
|
||||
|
||||
```swift
|
||||
let (x, y) = (1, 2)
|
||||
// 现在 x 等于 1,y 等于 2
|
||||
```
|
||||
|
||||
与 C 语言和 Objective-C 不同,Swift 的赋值操作并不返回任何值。所以下面语句是无效的:
|
||||
|
||||
```swift
|
||||
if x = y {
|
||||
// 此句错误,因为 x = y 并不返回任何值
|
||||
}
|
||||
```
|
||||
|
||||
通过将 `if x = y` 标记为无效语句,Swift 能帮你避免把 (`==`)错写成(`=`)这类错误的出现。
|
||||
|
||||
## 算术运算符 {#arithmetic-operators}
|
||||
|
||||
Swift 中所有数值类型都支持了基本的四则*算术运算符*:
|
||||
|
||||
- 加法(`+`)
|
||||
- 减法(`-`)
|
||||
- 乘法(`*`)
|
||||
- 除法(`/`)
|
||||
|
||||
```swift
|
||||
1 + 2 // 等于 3
|
||||
5 - 3 // 等于 2
|
||||
2 * 3 // 等于 6
|
||||
10.0 / 2.5 // 等于 4.0
|
||||
```
|
||||
|
||||
与 C 语言和 Objective-C 不同的是,Swift 默认情况下不允许在数值运算中出现溢出情况。但是你可以使用 Swift 的溢出运算符来实现溢出运算(如 `a &+ b`)。详情参见 [溢出运算符](./27_Advanced_Operators.md#overflow-operators)。
|
||||
|
||||
加法运算符也可用于 `String` 的拼接:
|
||||
|
||||
```swift
|
||||
"hello, " + "world" // 等于 "hello, world"
|
||||
```
|
||||
|
||||
### 求余运算符 {#remainder-operator}
|
||||
|
||||
*求余运算符*(`a % b`)是计算 `b` 的多少倍刚刚好可以容入 `a`,返回多出来的那部分(余数)。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 求余运算符(`%`)在其他语言也叫*取模运算符*。但是严格说来,我们看该运算符对负数的操作结果,「求余」比「取模」更合适些。
|
||||
|
||||
我们来谈谈取余是怎么回事,计算 `9 % 4`,你先计算出 `4` 的多少倍会刚好可以容入 `9` 中:
|
||||
|
||||

|
||||
|
||||
你可以在 `9` 中放入两个 `4`,那余数是 1(用橙色标出)。
|
||||
|
||||
在 Swift 中可以表达为:
|
||||
|
||||
```swift
|
||||
9 % 4 // 等于 1
|
||||
```
|
||||
|
||||
为了得到 `a % b` 的结果,`%` 计算了以下等式,并输出 `余数`作为结果:
|
||||
|
||||
a = (b × 倍数) + 余数
|
||||
|
||||
当 `倍数`取最大值的时候,就会刚好可以容入 `a` 中。
|
||||
|
||||
把 `9` 和 `4` 代入等式中,我们得 `1`:
|
||||
|
||||
9 = (4 × 2) + 1
|
||||
|
||||
同样的方法,我们来计算 `-9 % 4`:
|
||||
|
||||
```swift
|
||||
-9 % 4 // 等于 -1
|
||||
```
|
||||
|
||||
把 `-9` 和 `4` 代入等式,`-2` 是取到的最大整数:
|
||||
|
||||
-9 = (4 × -2) + -1
|
||||
|
||||
余数是 `-1`。
|
||||
|
||||
在对负数 `b` 求余时,`b` 的符号会被忽略。这意味着 `a % b` 和 `a % -b` 的结果是相同的。
|
||||
|
||||
### 一元负号运算符 {#unary-minus-operator}
|
||||
|
||||
数值的正负号可以使用前缀 `-`(即*一元负号符*)来切换:
|
||||
|
||||
```swift
|
||||
let three = 3
|
||||
let minusThree = -three // minusThree 等于 -3
|
||||
let plusThree = -minusThree // plusThree 等于 3, 或 "负负3"
|
||||
```
|
||||
|
||||
一元负号符(`-`)写在操作数之前,中间没有空格。
|
||||
|
||||
### 一元正号运算符 {#unary-plus-operator}
|
||||
|
||||
*一元正号符*(`+`)不做任何改变地返回操作数的值:
|
||||
|
||||
```swift
|
||||
let minusSix = -6
|
||||
let alsoMinusSix = +minusSix // alsoMinusSix 等于 -6
|
||||
```
|
||||
|
||||
虽然一元正号符什么都不会改变,但当你在使用一元负号来表达负数时,你可以使用一元正号来表达正数,如此你的代码会具有对称美。
|
||||
|
||||
## 组合赋值运算符 {#compound-assignment-operators}
|
||||
|
||||
如同 C 语言,Swift 也提供把其他运算符和赋值运算(`=`)组合的*组合赋值运算符*,组合加运算(`+=`)是其中一个例子:
|
||||
|
||||
```swift
|
||||
var a = 1
|
||||
a += 2
|
||||
// a 现在是 3
|
||||
```
|
||||
|
||||
表达式 `a += 2` 是 `a = a + 2` 的简写,一个组合加运算就是把加法运算和赋值运算组合成进一个运算符里,同时完成两个运算任务。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 复合赋值运算没有返回值,`let b = a += 2` 这类代码是错误。这不同于上面提到的自增和自减运算符。
|
||||
|
||||
更多 Swift 标准库运算符的信息,请看 [运算符声明](https://developer.apple.com/documentation/swift/operator_declarations)。
|
||||
|
||||
## 比较运算符(Comparison Operators) {#comparison-operators}
|
||||
|
||||
Swift 支持以下的比较运算符:
|
||||
|
||||
- 等于(`a == b`)
|
||||
- 不等于(`a != b`)
|
||||
- 大于(`a > b`)
|
||||
- 小于(`a < b`)
|
||||
- 大于等于(`a >= b`)
|
||||
- 小于等于(`a <= b`)
|
||||
|
||||
> 注意
|
||||
>
|
||||
> Swift 也提供恒等(`===`)和不恒等(`!==`)这两个比较符来判断两个对象是否引用同一个对象实例。更多细节在 [类与结构](./09_Structures_And_Classes.md) 章节的 **Identity Operators** 部分。
|
||||
|
||||
每个比较运算都返回了一个标识表达式是否成立的布尔值:
|
||||
|
||||
```swift
|
||||
1 == 1 // true, 因为 1 等于 1
|
||||
2 != 1 // true, 因为 2 不等于 1
|
||||
2 > 1 // true, 因为 2 大于 1
|
||||
1 < 2 // true, 因为 1 小于2
|
||||
1 >= 1 // true, 因为 1 大于等于 1
|
||||
2 <= 1 // false, 因为 2 并不小于等于 1
|
||||
```
|
||||
|
||||
比较运算多用于条件语句,如 `if` 条件:
|
||||
|
||||
```swift
|
||||
let name = "world"
|
||||
if name == "world" {
|
||||
print("hello, world")
|
||||
} else {
|
||||
print("I'm sorry \(name), but I don't recognize you")
|
||||
}
|
||||
// 输出“hello, world", 因为 `name` 就是等于 "world”
|
||||
```
|
||||
|
||||
关于 `if` 语句,请看 [控制流](./05_Control_Flow.md)。
|
||||
|
||||
如果两个元组的元素相同,且长度相同的话,元组就可以被比较。比较元组大小会按照从左到右、逐值比较的方式,直到发现有两个值不等时停止。如果所有的值都相等,那么这一对元组我们就称它们是相等的。例如:
|
||||
|
||||
```swift
|
||||
(1, "zebra") < (2, "apple") // true,因为 1 小于 2
|
||||
(3, "apple") < (3, "bird") // true,因为 3 等于 3,但是 apple 小于 bird
|
||||
(4, "dog") == (4, "dog") // true,因为 4 等于 4,dog 等于 dog
|
||||
```
|
||||
|
||||
在上面的例子中,你可以看到,在第一行中从左到右的比较行为。因为 `1` 小于 `2`,所以 `(1, "zebra")` 小于 `(2, "apple")`,不管元组剩下的值如何。所以 `"zebra"` 大于 `"apple"` 对结果没有任何影响,因为元组的比较结果已经被第一个元素决定了。不过,当元组的第一个元素相同时候,第二个元素将会用作比较-第二行和第三行代码就发生了这样的比较。
|
||||
|
||||
当元组中的元素都可以被比较时,你也可以使用这些运算符来比较它们的大小。例如,像下面展示的代码,你可以比较两个类型为 `(String, Int)` 的元组,因为 `Int` 和 `String` 类型的值可以比较。相反,`Bool` 不能被比较,也意味着存有布尔类型的元组不能被比较。
|
||||
|
||||
```swift
|
||||
("blue", -1) < ("purple", 1) // 正常,比较的结果为 true
|
||||
("blue", false) < ("purple", true) // 错误,因为 < 不能比较布尔类型
|
||||
```
|
||||
|
||||
> 注意
|
||||
>
|
||||
> Swift 标准库只能比较七个以内元素的元组比较函数。如果你的元组元素超过七个时,你需要自己实现比较运算符。
|
||||
|
||||
## 三元运算符(Ternary Conditional Operator) {#ternary-conditional-operator}
|
||||
|
||||
*三元运算符*的特殊在于它是有三个操作数的运算符,它的形式是 `问题 ? 答案 1 : 答案 2`。它简洁地表达根据 `问题`成立与否作出二选一的操作。如果 `问题` 成立,返回 `答案 1` 的结果;反之返回 `答案 2` 的结果。
|
||||
|
||||
三元运算符是以下代码的缩写形式:
|
||||
|
||||
```swift
|
||||
if question {
|
||||
answer1
|
||||
} else {
|
||||
answer2
|
||||
}
|
||||
```
|
||||
|
||||
这里有个计算表格行高的例子。如果有表头,那行高应比内容高度要高出 50 点;如果没有表头,只需高出 20 点:
|
||||
|
||||
```swift
|
||||
let contentHeight = 40
|
||||
let hasHeader = true
|
||||
let rowHeight = contentHeight + (hasHeader ? 50 : 20)
|
||||
// rowHeight 现在是 90
|
||||
```
|
||||
|
||||
上面的写法比下面的代码更简洁:
|
||||
|
||||
```swift
|
||||
let contentHeight = 40
|
||||
let hasHeader = true
|
||||
var rowHeight = contentHeight
|
||||
if hasHeader {
|
||||
rowHeight = rowHeight + 50
|
||||
} else {
|
||||
rowHeight = rowHeight + 20
|
||||
}
|
||||
// rowHeight 现在是 90
|
||||
```
|
||||
|
||||
第一段代码例子使用了三元运算,所以一行代码就能让我们得到正确答案。这比第二段代码简洁得多,无需将 `rowHeight` 定义成变量,因为它的值无需在 `if` 语句中改变。
|
||||
|
||||
三元运算为二选一场景提供了一个非常便捷的表达形式。不过需要注意的是,滥用三元运算符会降低代码可读性。所以我们应避免在一个复合语句中使用多个三元运算符。
|
||||
|
||||
## 空合运算符(Nil Coalescing Operator) {#nil-coalescing-operator}
|
||||
|
||||
*空合运算符*(`a ?? b`)将对可选类型 `a` 进行空判断,如果 `a` 包含一个值就进行解包,否则就返回一个默认值 `b`。表达式 `a` 必须是 Optional 类型。默认值 `b` 的类型必须要和 `a` 存储值的类型保持一致。
|
||||
|
||||
空合运算符是对以下代码的简短表达方法:
|
||||
|
||||
```swift
|
||||
a != nil ? a! : b
|
||||
```
|
||||
|
||||
上述代码使用了三元运算符。当可选类型 `a` 的值不为空时,进行强制解包(`a!`),访问 `a` 中的值;反之返回默认值 `b`。无疑空合运算符(`??`)提供了一种更为优雅的方式去封装条件判断和解包两种行为,显得简洁以及更具可读性。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 如果 `a` 为非空值(`non-nil`),那么值 `b` 将不会被计算。这也就是所谓的*短路求值*。
|
||||
|
||||
下文例子采用空合运算符,实现了在默认颜色名和可选自定义颜色名之间抉择:
|
||||
|
||||
```swift
|
||||
let defaultColorName = "red"
|
||||
var userDefinedColorName: String? //默认值为 nil
|
||||
|
||||
var colorNameToUse = userDefinedColorName ?? defaultColorName
|
||||
// userDefinedColorName 的值为空,所以 colorNameToUse 的值为 "red"
|
||||
```
|
||||
|
||||
`userDefinedColorName` 变量被定义为一个可选的 `String` 类型,默认值为 `nil`。由于 `userDefinedColorName` 是一个可选类型,我们可以使用空合运算符去判断其值。在上一个例子中,通过空合运算符为一个名为 `colorNameToUse` 的变量赋予一个字符串类型初始值。
|
||||
由于 `userDefinedColorName` 值为空,因此表达式 `userDefinedColorName ?? defaultColorName` 返回 `defaultColorName` 的值,即 `red`。
|
||||
|
||||
如果你分配一个非空值(`non-nil`)给 `userDefinedColorName`,再次执行空合运算,运算结果为封包在 `userDefinedColorName` 中的值,而非默认值。
|
||||
|
||||
```swift
|
||||
userDefinedColorName = "green"
|
||||
colorNameToUse = userDefinedColorName ?? defaultColorName
|
||||
// userDefinedColorName 非空,因此 colorNameToUse 的值为 "green"
|
||||
```
|
||||
|
||||
## 区间运算符(Range Operators) {#range-operators}
|
||||
|
||||
Swift 提供了几种方便表达一个区间的值的*区间运算符*。
|
||||
|
||||
### 闭区间运算符 {#closed-range-operator}
|
||||
|
||||
*闭区间运算符*(`a...b`)定义一个包含从 `a` 到 `b`(包括 `a` 和 `b`)的所有值的区间。`a` 的值不能超过 `b`。
|
||||
|
||||
闭区间运算符在迭代一个区间的所有值时是非常有用的,如在 `for-in` 循环中:
|
||||
|
||||
```swift
|
||||
for index in 1...5 {
|
||||
print("\(index) * 5 = \(index * 5)")
|
||||
}
|
||||
// 1 * 5 = 5
|
||||
// 2 * 5 = 10
|
||||
// 3 * 5 = 15
|
||||
// 4 * 5 = 20
|
||||
// 5 * 5 = 25
|
||||
```
|
||||
|
||||
关于 `for-in` 循环,请看 [控制流](./05_Control_Flow.md)。
|
||||
|
||||
### 半开区间运算符 {#half-open-range-operator}
|
||||
|
||||
*半开区间运算符*(`a..<b`)定义一个从 `a` 到 `b` 但不包括 `b` 的区间。
|
||||
之所以称为*半开区间*,是因为该区间包含第一个值而不包括最后的值。
|
||||
|
||||
半开区间的实用性在于当你使用一个从 0 开始的列表(如数组)时,非常方便地从0数到列表的长度。
|
||||
|
||||
```swift
|
||||
let names = ["Anna", "Alex", "Brian", "Jack"]
|
||||
let count = names.count
|
||||
for i in 0..<count {
|
||||
print("第 \(i + 1) 个人叫 \(names[i])")
|
||||
}
|
||||
// 第 1 个人叫 Anna
|
||||
// 第 2 个人叫 Alex
|
||||
// 第 3 个人叫 Brian
|
||||
// 第 4 个人叫 Jack
|
||||
```
|
||||
|
||||
数组有 4 个元素,但 `0..<count` 只数到3(最后一个元素的下标),因为它是半开区间。关于数组,请查阅 [数组](./04_Collection_Types.md#arrays)。
|
||||
|
||||
### 单侧区间 {#one-sided-ranges}
|
||||
|
||||
闭区间操作符有另一个表达形式,可以表达往一侧无限延伸的区间 —— 例如,一个包含了数组从索引 2 到结尾的所有值的区间。在这些情况下,你可以省略掉区间操作符一侧的值。这种区间叫做单侧区间,因为操作符只有一侧有值。例如:
|
||||
|
||||
```swift
|
||||
for name in names[2...] {
|
||||
print(name)
|
||||
}
|
||||
// Brian
|
||||
// Jack
|
||||
|
||||
for name in names[...2] {
|
||||
print(name)
|
||||
}
|
||||
// Anna
|
||||
// Alex
|
||||
// Brian
|
||||
```
|
||||
|
||||
半开区间操作符也有单侧表达形式,附带上它的最终值。就像你使用区间去包含一个值,最终值并不会落在区间内。例如:
|
||||
|
||||
```swift
|
||||
for name in names[..<2] {
|
||||
print(name)
|
||||
}
|
||||
// Anna
|
||||
// Alex
|
||||
```
|
||||
|
||||
单侧区间不止可以在下标里使用,也可以在别的情境下使用。你不能遍历省略了初始值的单侧区间,因为遍历的开端并不明显。你可以遍历一个省略最终值的单侧区间;然而,由于这种区间无限延伸的特性,请保证你在循环里有一个结束循环的分支。你也可以查看一个单侧区间是否包含某个特定的值,就像下面展示的那样。
|
||||
|
||||
```swift
|
||||
let range = ...5
|
||||
range.contains(7) // false
|
||||
range.contains(4) // true
|
||||
range.contains(-1) // true
|
||||
```
|
||||
|
||||
## 逻辑运算符(Logical Operators) {#logical-operators}
|
||||
|
||||
*逻辑运算符*的操作对象是逻辑布尔值。Swift 支持基于 C 语言的三个标准逻辑运算。
|
||||
|
||||
- 逻辑非(`!a`)
|
||||
- 逻辑与(`a && b`)
|
||||
- 逻辑或(`a || b`)
|
||||
|
||||
### 逻辑非运算符
|
||||
|
||||
*逻辑非运算符*(`!a`)对一个布尔值取反,使得 `true` 变 `false`,`false` 变 `true`。
|
||||
|
||||
它是一个前置运算符,需紧跟在操作数之前,且不加空格。读作 `非 a` ,例子如下:
|
||||
|
||||
```swift
|
||||
let allowedEntry = false
|
||||
if !allowedEntry {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出“ACCESS DENIED”
|
||||
```
|
||||
|
||||
`if !allowedEntry` 语句可以读作「如果非 allowedEntry」,接下一行代码只有在「非 allowedEntry」为 `true`,即 `allowEntry` 为 `false` 时被执行。
|
||||
|
||||
在示例代码中,小心地选择布尔常量或变量有助于代码的可读性,并且避免使用双重逻辑非运算,或混乱的逻辑语句。
|
||||
|
||||
### 逻辑与运算符 {#logical-and-operator}
|
||||
|
||||
*逻辑与运算符*(`a && b`)表达了只有 `a` 和 `b` 的值都为 `true` 时,整个表达式的值才会是 `true`。
|
||||
|
||||
只要任意一个值为 `false`,整个表达式的值就为 `false`。事实上,如果第一个值为 `false`,那么是不去计算第二个值的,因为它已经不可能影响整个表达式的结果了。这被称做*短路计算(short-circuit evaluation)*。
|
||||
|
||||
以下例子,只有两个 `Bool` 值都为 `true` 的时候才允许进入 if:
|
||||
|
||||
```swift
|
||||
let enteredDoorCode = true
|
||||
let passedRetinaScan = false
|
||||
if enteredDoorCode && passedRetinaScan {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出“ACCESS DENIED”
|
||||
```
|
||||
|
||||
### 逻辑或运算符 {#logical-or-operator}
|
||||
|
||||
逻辑或运算符(`a || b`)是一个由两个连续的 `|` 组成的中置运算符。它表示了两个逻辑表达式的其中一个为 `true`,整个表达式就为 `true`。
|
||||
|
||||
同逻辑与运算符类似,逻辑或也是「短路计算」的,当左端的表达式为 `true` 时,将不计算右边的表达式了,因为它不可能改变整个表达式的值了。
|
||||
|
||||
以下示例代码中,第一个布尔值(`hasDoorKey`)为 `false`,但第二个值(`knowsOverridePassword`)为 `true`,所以整个表达是 `true`,于是允许进入:
|
||||
|
||||
```swift
|
||||
let hasDoorKey = false
|
||||
let knowsOverridePassword = true
|
||||
if hasDoorKey || knowsOverridePassword {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出“Welcome!”
|
||||
```
|
||||
|
||||
### 逻辑运算符组合计算 {#combining-logical-operators}
|
||||
|
||||
我们可以组合多个逻辑运算符来表达一个复合逻辑:
|
||||
|
||||
```swift
|
||||
if enteredDoorCode && passedRetinaScan || hasDoorKey || knowsOverridePassword {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出“Welcome!”
|
||||
```
|
||||
|
||||
这个例子使用了含多个 `&&` 和 `||` 的复合逻辑。但无论怎样,`&&` 和 `||` 始终只能操作两个值。所以这实际是三个简单逻辑连续操作的结果。我们来解读一下:
|
||||
|
||||
如果我们输入了正确的密码并通过了视网膜扫描,或者我们有一把有效的钥匙,又或者我们知道紧急情况下重置的密码,我们就能把门打开进入。
|
||||
|
||||
前两种情况,我们都不满足,所以前两个简单逻辑的结果是 `false`,但是我们是知道紧急情况下重置的密码的,所以整个复杂表达式的值还是 `true`。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> Swift 逻辑操作符 `&&` 和 `||` 是左结合的,这意味着拥有多元逻辑操作符的复合表达式优先计算最左边的子表达式。
|
||||
|
||||
### 使用括号来明确优先级 {#explicit-parentheses}
|
||||
|
||||
为了一个复杂表达式更容易读懂,在合适的地方使用括号来明确优先级是很有效的,虽然它并非必要的。在上个关于门的权限的例子中,我们给第一个部分加个括号,使它看起来逻辑更明确:
|
||||
|
||||
```swift
|
||||
if (enteredDoorCode && passedRetinaScan) || hasDoorKey || knowsOverridePassword {
|
||||
print("Welcome!")
|
||||
} else {
|
||||
print("ACCESS DENIED")
|
||||
}
|
||||
// 输出“Welcome!”
|
||||
```
|
||||
|
||||
这括号使得前两个值被看成整个逻辑表达中独立的一个部分。虽然有括号和没括号的输出结果是一样的,但对于读代码的人来说有括号的代码更清晰。可读性比简洁性更重要,请在可以让你代码变清晰的地方加个括号吧!
|
||||
@ -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,7 +444,7 @@ 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!"
|
||||
@ -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` 创建的,拥有一片自己的内存空间。下面的图展示了他们之间的关系:
|
||||
|
||||

|
||||
|
||||
> 注意
|
||||
>
|
||||
> `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 兼容的方式访问字符串的值:
|
||||
|
||||
644
source/02_language_guide/04_Collection_Types.md
Executable file
644
source/02_language_guide/04_Collection_Types.md
Executable file
@ -0,0 +1,644 @@
|
||||
# 集合类型
|
||||
|
||||
Swift 语言提供数组(Array)、集合(Set)和字典(Dictionary)三种基本的*集合类型*用来存储集合数据。数组是有序数据的集。集合是无序无重复数据的集。字典是无序的键值对的集。
|
||||
|
||||

|
||||
|
||||
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`,以及通过阴影部分的区域显示集合各种操作的结果。
|
||||
|
||||

|
||||
|
||||
* 使用 `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` 是*不相交*的,因为它们之间没有共同的元素。
|
||||
|
||||

|
||||
|
||||
* 使用“是否相等”运算符(`==`)来判断两个集合包含的值是否全部相同。
|
||||
* 使用 `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()` 方法。
|
||||
@ -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(平台名称 版本号, ..., *) {
|
||||
@ -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) 一节。
|
||||
|
||||
你只能传递变量给输入输出参数。你不能传入常量或者字面量,因为这些量是不能被修改的。当传入的参数作为输入输出参数时,需要在参数名前加 `&` 符,表示这个值可以被函数修改。
|
||||
|
||||
@ -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"]
|
||||
@ -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`:
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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` 修饰符。
|
||||
>
|
||||
> 局部范围的常量和变量从不延迟计算。
|
||||
|
||||
@ -1,255 +1,255 @@
|
||||
# 方法
|
||||
|
||||
*方法*是与某些特定类型相关联的函数。类、结构体、枚举都可以定义实例方法;实例方法为给定类型的实例封装了具体的任务与功能。类、结构体、枚举也可以定义类型方法;类型方法与类型本身相关联。类型方法与 Objective-C 中的类方法(class methods)相似。
|
||||
|
||||
结构体和枚举能够定义方法是 Swift 与 C/Objective-C 的主要区别之一。在 Objective-C 中,类是唯一能定义方法的类型。但在 Swift 中,你不仅能选择是否要定义一个类/结构体/枚举,还能灵活地在你创建的类型(类/结构体/枚举)上定义方法。
|
||||
|
||||
## 实例方法(Instance Methods) {#instance-methods}
|
||||
|
||||
*实例方法*是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见[函数](./06_Functions.md)。
|
||||
|
||||
实例方法要写在它所属的类型的前后大括号之间。实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的某个特定实例调用。实例方法不能脱离于现存的实例而被调用。
|
||||
|
||||
下面的例子,定义一个很简单的 `Counter` 类,`Counter` 能被用来对一个动作发生的次数进行计数:
|
||||
|
||||
```swift
|
||||
class Counter {
|
||||
var count = 0
|
||||
func increment() {
|
||||
count += 1
|
||||
}
|
||||
func increment(by amount: Int) {
|
||||
count += amount
|
||||
}
|
||||
func reset() {
|
||||
count = 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Counter` 类定义了三个实例方法:
|
||||
- `increment` 让计数器按一递增;
|
||||
- `increment(by: Int)` 让计数器按一个指定的整数值递增;
|
||||
- `reset` 将计数器重置为0。
|
||||
|
||||
`Counter` 这个类还声明了一个可变属性 `count`,用它来保持对当前计数器值的追踪。
|
||||
|
||||
和调用属性一样,用点语法(dot syntax)调用实例方法:
|
||||
|
||||
```swift
|
||||
let counter = Counter()
|
||||
// 初始计数值是0
|
||||
counter.increment()
|
||||
// 计数值现在是1
|
||||
counter.increment(by: 5)
|
||||
// 计数值现在是6
|
||||
counter.reset()
|
||||
// 计数值现在是0
|
||||
```
|
||||
|
||||
函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见[指定外部参数名](./06_Functions.md#specifying_external_parameter_names)。方法参数也一样,因为方法就是函数,只是这个函数与某个类型相关联了。
|
||||
|
||||
### self 属性 {#the-self-property}
|
||||
|
||||
类型的每一个实例都有一个隐含属性叫做 `self`,`self` 完全等同于该实例本身。你可以在一个实例的实例方法中使用这个隐含的 `self` 属性来引用当前实例。
|
||||
|
||||
上面例子中的 `increment` 方法还可以这样写:
|
||||
|
||||
```swift
|
||||
func increment() {
|
||||
self.count += 1
|
||||
}
|
||||
```
|
||||
|
||||
实际上,你不必在你的代码里面经常写 `self`。不论何时,只要在一个方法中使用一个已知的属性或者方法名称,如果你没有明确地写 `self`,Swift 假定你是指当前实例的属性或者方法。这种假定在上面的 `Counter` 中已经示范了:`Counter` 中的三个实例方法中都使用的是 `count`(而不是 `self.count`)。
|
||||
|
||||
使用这条规则的主要场景是实例方法的某个参数名称与实例的某个属性名称相同的时候。在这种情况下,参数名称享有优先权,并且在引用属性时必须使用一种更严格的方式。这时你可以使用 `self` 属性来区分参数名称和属性名称。
|
||||
|
||||
下面的例子中,`self` 消除方法参数 `x` 和实例属性 `x` 之间的歧义:
|
||||
|
||||
```swift
|
||||
struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
func isToTheRightOf(x: Double) -> Bool {
|
||||
return self.x > x
|
||||
}
|
||||
}
|
||||
let somePoint = Point(x: 4.0, y: 5.0)
|
||||
if somePoint.isToTheRightOf(x: 1.0) {
|
||||
print("This point is to the right of the line where x == 1.0")
|
||||
}
|
||||
// 打印“This point is to the right of the line where x == 1.0”
|
||||
```
|
||||
|
||||
如果不使用 `self` 前缀,Swift会认为 `x` 的两个用法都引用了名为 `x` 的方法参数。
|
||||
|
||||
### 在实例方法中修改值类型 {#modifying-value-types-from-within-instance-methods}
|
||||
|
||||
结构体和枚举是*值类型*。默认情况下,值类型的属性不能在它的实例方法中被修改。
|
||||
|
||||
但是,如果你确实需要在某个特定的方法中修改结构体或者枚举的属性,你可以为这个方法选择 `可变(mutating)`行为,然后就可以从其方法内部改变它的属性;并且这个方法做的任何改变都会在方法执行结束时写回到原始结构中。方法还可以给它隐含的 `self` 属性赋予一个全新的实例,这个新实例在方法结束时会替换现存实例。
|
||||
|
||||
要使用 `可变`方法,将关键字 `mutating` 放到方法的 `func` 关键字之前就可以了:
|
||||
|
||||
```swift
|
||||
struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
|
||||
x += deltaX
|
||||
y += deltaY
|
||||
}
|
||||
}
|
||||
var somePoint = Point(x: 1.0, y: 1.0)
|
||||
somePoint.moveBy(x: 2.0, y: 3.0)
|
||||
print("The point is now at (\(somePoint.x), \(somePoint.y))")
|
||||
// 打印“The point is now at (3.0, 4.0)”
|
||||
```
|
||||
|
||||
上面的 `Point` 结构体定义了一个可变方法 `moveBy(x:y :)` 来移动 `Point` 实例到给定的位置。该方法被调用时修改了这个点,而不是返回一个新的点。方法定义时加上了 `mutating` 关键字,从而允许修改属性。
|
||||
|
||||
注意,不能在结构体类型的常量(a constant of structure type)上调用可变方法,因为其属性不能被改变,即使属性是变量属性,详情参见[常量结构体的存储属性](./10_Properties.md#stored_properties_of_constant_structure_instances):
|
||||
|
||||
```swift
|
||||
let fixedPoint = Point(x: 3.0, y: 3.0)
|
||||
fixedPoint.moveBy(x: 2.0, y: 3.0)
|
||||
// 这里将会报告一个错误
|
||||
```
|
||||
|
||||
### 在可变方法中给 self 赋值 {#assigning-to-self-within-a-mutating-method}
|
||||
|
||||
可变方法能够赋给隐含属性 `self` 一个全新的实例。上面 `Point` 的例子可以用下面的方式改写:
|
||||
|
||||
```swift
|
||||
struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
|
||||
self = Point(x: x + deltaX, y: y + deltaY)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
新版的可变方法 `moveBy(x:y:)` 创建了一个新的结构体实例,它的 x 和 y 的值都被设定为目标值。调用这个版本的方法和调用上个版本的最终结果是一样的。
|
||||
|
||||
枚举的可变方法可以把 `self` 设置为同一枚举类型中不同的成员:
|
||||
|
||||
```swift
|
||||
enum TriStateSwitch {
|
||||
case off, low, high
|
||||
mutating func next() {
|
||||
switch self {
|
||||
case .off:
|
||||
self = .low
|
||||
case .low:
|
||||
self = .high
|
||||
case .high:
|
||||
self = .off
|
||||
}
|
||||
}
|
||||
}
|
||||
var ovenLight = TriStateSwitch.low
|
||||
ovenLight.next()
|
||||
// ovenLight 现在等于 .high
|
||||
ovenLight.next()
|
||||
// ovenLight 现在等于 .off
|
||||
```
|
||||
|
||||
上面的例子中定义了一个三态切换的枚举。每次调用 `next()` 方法时,开关在不同的电源状态(`off`, `low`, `high`)之间循环切换。
|
||||
|
||||
## 类型方法 {#type-methods}
|
||||
|
||||
实例方法是被某个类型的实例调用的方法。你也可以定义在类型本身上调用的方法,这种方法就叫做*类型方法*。在方法的 `func` 关键字之前加上关键字 `static`,来指定类型方法。类还可以用关键字 `class` 来允许子类重写父类的方法实现。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 在 Objective-C 中,你只能为 Objective-C 的类类型(classes)定义类型方法(type-level methods)。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法。每一个类型方法都被它所支持的类型显式包含。
|
||||
|
||||
类型方法和实例方法一样用点语法调用。但是,你是在类型上调用这个方法,而不是在实例上调用。下面是如何在 `SomeClass` 类上调用类型方法的例子:
|
||||
|
||||
```swift
|
||||
class SomeClass {
|
||||
class func someTypeMethod() {
|
||||
// 在这里实现类型方法
|
||||
}
|
||||
}
|
||||
SomeClass.someTypeMethod()
|
||||
```
|
||||
|
||||
在类型方法的方法体(body)中,`self` 属性指向这个类型本身,而不是类型的某个实例。这意味着你可以用 `self` 来消除类型属性和类型方法参数之间的歧义(类似于我们在前面处理实例属性和实例方法参数时做的那样)。
|
||||
|
||||
一般来说,在类型方法的方法体中,任何未限定的方法和属性名称,可以被本类中其他的类型方法和类型属性引用。一个类型方法可以直接通过类型方法的名称调用本类中的其它类型方法,而无需在方法名称前面加上类型名称。类似地,在结构体和枚举中,也能够直接通过类型属性的名称访问本类中的类型属性,而不需要前面加上类型名称。
|
||||
|
||||
下面的例子定义了一个名为 `LevelTracker` 结构体。它监测玩家的游戏发展情况(游戏的不同层次或阶段)。这是一个单人游戏,但也可以存储多个玩家在同一设备上的游戏信息。
|
||||
|
||||
游戏初始时,所有的游戏等级(除了等级 1)都被锁定。每次有玩家完成一个等级,这个等级就对这个设备上的所有玩家解锁。`LevelTracker` 结构体用类型属性和方法监测游戏的哪个等级已经被解锁。它还监测每个玩家的当前等级。
|
||||
|
||||
```swift
|
||||
struct LevelTracker {
|
||||
static var highestUnlockedLevel = 1
|
||||
var currentLevel = 1
|
||||
|
||||
static func unlock(_ level: Int) {
|
||||
if level > highestUnlockedLevel { highestUnlockedLevel = level }
|
||||
}
|
||||
|
||||
static func isUnlocked(_ level: Int) -> Bool {
|
||||
return level <= highestUnlockedLevel
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
mutating func advance(to level: Int) -> Bool {
|
||||
if LevelTracker.isUnlocked(level) {
|
||||
currentLevel = level
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`LevelTracker` 监测玩家已解锁的最高等级。这个值被存储在类型属性 `highestUnlockedLevel` 中。
|
||||
|
||||
`LevelTracker` 还定义了两个类型方法与 `highestUnlockedLevel` 配合工作。第一个类型方法是 `unlock(_:)`,一旦新等级被解锁,它会更新 `highestUnlockedLevel` 的值。第二个类型方法是 `isUnlocked(_:)`,如果某个给定的等级已经被解锁,它将返回 `true`。(注意,尽管我们没有使用类似 `LevelTracker.highestUnlockedLevel` 的写法,这个类型方法还是能够访问类型属性 `highestUnlockedLevel`)
|
||||
|
||||
除了类型属性和类型方法,`LevelTracker` 还监测每个玩家的进度。它用实例属性 `currentLevel` 来监测每个玩家当前的等级。
|
||||
|
||||
为了便于管理 `currentLevel` 属性,`LevelTracker` 定义了实例方法 `advance(to:)`。这个方法会在更新 `currentLevel` 之前检查所请求的新等级是否已经解锁。`advance(to:)` 方法返回布尔值以指示是否能够设置 `currentLevel`。因为允许在调用 `advance(to:)` 时候忽略返回值,不会产生编译警告,所以函数被标注为 `@discardableResult` 属性,更多关于属性信息,请参考[特性](../chapter3/07_Attributes.html)章节。
|
||||
|
||||
下面,`Player` 类使用 `LevelTracker` 来监测和更新每个玩家的发展进度:
|
||||
|
||||
```swift
|
||||
class Player {
|
||||
var tracker = LevelTracker()
|
||||
let playerName: String
|
||||
func complete(level: Int) {
|
||||
LevelTracker.unlock(level + 1)
|
||||
tracker.advance(to: level + 1)
|
||||
}
|
||||
init(name: String) {
|
||||
playerName = name
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Player` 类创建一个新的 `LevelTracker` 实例来监测这个用户的进度。它提供了 `complete(level:)` 方法,一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了 `advance(to:)` 返回的布尔值,因为之前调用 `LevelTracker.unlock(_:)` 时就知道了这个等级已经被解锁了)。
|
||||
|
||||
你还可以为一个新的玩家创建一个 `Player` 的实例,然后看这个玩家完成等级一时发生了什么:
|
||||
|
||||
```swift
|
||||
var player = Player(name: "Argyrios")
|
||||
player.complete(level: 1)
|
||||
print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
|
||||
// 打印“highest unlocked level is now 2”
|
||||
```
|
||||
|
||||
如果你创建了第二个玩家,并尝试让他开始一个没有被任何玩家解锁的等级,那么试图设置玩家当前等级将会失败:
|
||||
|
||||
```swift
|
||||
player = Player(name: "Beto")
|
||||
if player.tracker.advance(to: 6) {
|
||||
print("player is now on level 6")
|
||||
} else {
|
||||
print("level 6 has not yet been unlocked")
|
||||
}
|
||||
// 打印“level 6 has not yet been unlocked”
|
||||
```
|
||||
# 方法
|
||||
|
||||
*方法*是与某些特定类型相关联的函数。类、结构体、枚举都可以定义实例方法;实例方法为给定类型的实例封装了具体的任务与功能。类、结构体、枚举也可以定义类型方法;类型方法与类型本身相关联。类型方法与 Objective-C 中的类方法(class methods)相似。
|
||||
|
||||
结构体和枚举能够定义方法是 Swift 与 C/Objective-C 的主要区别之一。在 Objective-C 中,类是唯一能定义方法的类型。但在 Swift 中,你不仅能选择是否要定义一个类/结构体/枚举,还能灵活地在你创建的类型(类/结构体/枚举)上定义方法。
|
||||
|
||||
## 实例方法(Instance Methods) {#instance-methods}
|
||||
|
||||
*实例方法*是属于某个特定类、结构体或者枚举类型实例的方法。实例方法提供访问和修改实例属性的方法或提供与实例目的相关的功能,并以此来支撑实例的功能。实例方法的语法与函数完全一致,详情参见 [函数](./06_Functions.md)。
|
||||
|
||||
实例方法要写在它所属的类型的前后大括号之间。实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的某个特定实例调用。实例方法不能脱离于现存的实例而被调用。
|
||||
|
||||
下面的例子,定义一个很简单的 `Counter` 类,`Counter` 能被用来对一个动作发生的次数进行计数:
|
||||
|
||||
```swift
|
||||
class Counter {
|
||||
var count = 0
|
||||
func increment() {
|
||||
count += 1
|
||||
}
|
||||
func increment(by amount: Int) {
|
||||
count += amount
|
||||
}
|
||||
func reset() {
|
||||
count = 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Counter` 类定义了三个实例方法:
|
||||
- `increment` 让计数器按一递增;
|
||||
- `increment(by: Int)` 让计数器按一个指定的整数值递增;
|
||||
- `reset` 将计数器重置为0。
|
||||
|
||||
`Counter` 这个类还声明了一个可变属性 `count`,用它来保持对当前计数器值的追踪。
|
||||
|
||||
和调用属性一样,用点语法(dot syntax)调用实例方法:
|
||||
|
||||
```swift
|
||||
let counter = Counter()
|
||||
// 初始计数值是0
|
||||
counter.increment()
|
||||
// 计数值现在是1
|
||||
counter.increment(by: 5)
|
||||
// 计数值现在是6
|
||||
counter.reset()
|
||||
// 计数值现在是0
|
||||
```
|
||||
|
||||
函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),详情参见 [指定外部参数名](./06_Functions.md#specifying-external-parameter-names)。方法参数也一样,因为方法就是函数,只是这个函数与某个类型相关联了。
|
||||
|
||||
### self 属性 {#the-self-property}
|
||||
|
||||
类型的每一个实例都有一个隐含属性叫做 `self`,`self` 完全等同于该实例本身。你可以在一个实例的实例方法中使用这个隐含的 `self` 属性来引用当前实例。
|
||||
|
||||
上面例子中的 `increment` 方法还可以这样写:
|
||||
|
||||
```swift
|
||||
func increment() {
|
||||
self.count += 1
|
||||
}
|
||||
```
|
||||
|
||||
实际上,你不必在你的代码里面经常写 `self`。不论何时,只要在一个方法中使用一个已知的属性或者方法名称,如果你没有明确地写 `self`,Swift 假定你是指当前实例的属性或者方法。这种假定在上面的 `Counter` 中已经示范了:`Counter` 中的三个实例方法中都使用的是 `count`(而不是 `self.count`)。
|
||||
|
||||
使用这条规则的主要场景是实例方法的某个参数名称与实例的某个属性名称相同的时候。在这种情况下,参数名称享有优先权,并且在引用属性时必须使用一种更严格的方式。这时你可以使用 `self` 属性来区分参数名称和属性名称。
|
||||
|
||||
下面的例子中,`self` 消除方法参数 `x` 和实例属性 `x` 之间的歧义:
|
||||
|
||||
```swift
|
||||
struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
func isToTheRightOf(x: Double) -> Bool {
|
||||
return self.x > x
|
||||
}
|
||||
}
|
||||
let somePoint = Point(x: 4.0, y: 5.0)
|
||||
if somePoint.isToTheRightOf(x: 1.0) {
|
||||
print("This point is to the right of the line where x == 1.0")
|
||||
}
|
||||
// 打印“This point is to the right of the line where x == 1.0”
|
||||
```
|
||||
|
||||
如果不使用 `self` 前缀,Swift会认为 `x` 的两个用法都引用了名为 `x` 的方法参数。
|
||||
|
||||
### 在实例方法中修改值类型 {#modifying-value-types-from-within-instance-methods}
|
||||
|
||||
结构体和枚举是*值类型*。默认情况下,值类型的属性不能在它的实例方法中被修改。
|
||||
|
||||
但是,如果你确实需要在某个特定的方法中修改结构体或者枚举的属性,你可以为这个方法选择 `可变(mutating)`行为,然后就可以从其方法内部改变它的属性;并且这个方法做的任何改变都会在方法执行结束时写回到原始结构中。方法还可以给它隐含的 `self` 属性赋予一个全新的实例,这个新实例在方法结束时会替换现存实例。
|
||||
|
||||
要使用 `可变`方法,将关键字 `mutating` 放到方法的 `func` 关键字之前就可以了:
|
||||
|
||||
```swift
|
||||
struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
|
||||
x += deltaX
|
||||
y += deltaY
|
||||
}
|
||||
}
|
||||
var somePoint = Point(x: 1.0, y: 1.0)
|
||||
somePoint.moveBy(x: 2.0, y: 3.0)
|
||||
print("The point is now at (\(somePoint.x), \(somePoint.y))")
|
||||
// 打印“The point is now at (3.0, 4.0)”
|
||||
```
|
||||
|
||||
上面的 `Point` 结构体定义了一个可变方法 `moveBy(x:y :)` 来移动 `Point` 实例到给定的位置。该方法被调用时修改了这个点,而不是返回一个新的点。方法定义时加上了 `mutating` 关键字,从而允许修改属性。
|
||||
|
||||
注意,不能在结构体类型的常量(a constant of structure type)上调用可变方法,因为其属性不能被改变,即使属性是变量属性,详情参见 [常量结构体的存储属性](./10_Properties.md#stored-properties-of-constant-structure-instances):
|
||||
|
||||
```swift
|
||||
let fixedPoint = Point(x: 3.0, y: 3.0)
|
||||
fixedPoint.moveBy(x: 2.0, y: 3.0)
|
||||
// 这里将会报告一个错误
|
||||
```
|
||||
|
||||
### 在可变方法中给 self 赋值 {#assigning-to-self-within-a-mutating-method}
|
||||
|
||||
可变方法能够赋给隐含属性 `self` 一个全新的实例。上面 `Point` 的例子可以用下面的方式改写:
|
||||
|
||||
```swift
|
||||
struct Point {
|
||||
var x = 0.0, y = 0.0
|
||||
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
|
||||
self = Point(x: x + deltaX, y: y + deltaY)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
新版的可变方法 `moveBy(x:y:)` 创建了一个新的结构体实例,它的 x 和 y 的值都被设定为目标值。调用这个版本的方法和调用上个版本的最终结果是一样的。
|
||||
|
||||
枚举的可变方法可以把 `self` 设置为同一枚举类型中不同的成员:
|
||||
|
||||
```swift
|
||||
enum TriStateSwitch {
|
||||
case off, low, high
|
||||
mutating func next() {
|
||||
switch self {
|
||||
case .off:
|
||||
self = .low
|
||||
case .low:
|
||||
self = .high
|
||||
case .high:
|
||||
self = .off
|
||||
}
|
||||
}
|
||||
}
|
||||
var ovenLight = TriStateSwitch.low
|
||||
ovenLight.next()
|
||||
// ovenLight 现在等于 .high
|
||||
ovenLight.next()
|
||||
// ovenLight 现在等于 .off
|
||||
```
|
||||
|
||||
上面的例子中定义了一个三态切换的枚举。每次调用 `next()` 方法时,开关在不同的电源状态(`off`, `low`, `high`)之间循环切换。
|
||||
|
||||
## 类型方法 {#type-methods}
|
||||
|
||||
实例方法是被某个类型的实例调用的方法。你也可以定义在类型本身上调用的方法,这种方法就叫做*类型方法*。在方法的 `func` 关键字之前加上关键字 `static`,来指定类型方法。类还可以用关键字 `class` 来指定,从而允许子类重写父类该方法的实现。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 在 Objective-C 中,你只能为 Objective-C 的类类型(classes)定义类型方法(type-level methods)。在 Swift 中,你可以为所有的类、结构体和枚举定义类型方法。每一个类型方法都被它所支持的类型显式包含。
|
||||
|
||||
类型方法和实例方法一样用点语法调用。但是,你是在类型上调用这个方法,而不是在实例上调用。下面是如何在 `SomeClass` 类上调用类型方法的例子:
|
||||
|
||||
```swift
|
||||
class SomeClass {
|
||||
class func someTypeMethod() {
|
||||
// 在这里实现类型方法
|
||||
}
|
||||
}
|
||||
SomeClass.someTypeMethod()
|
||||
```
|
||||
|
||||
在类型方法的方法体(body)中,`self` 属性指向这个类型本身,而不是类型的某个实例。这意味着你可以用 `self` 来消除类型属性和类型方法参数之间的歧义(类似于我们在前面处理实例属性和实例方法参数时做的那样)。
|
||||
|
||||
一般来说,在类型方法的方法体中,任何未限定的方法和属性名称,可以被本类中其他的类型方法和类型属性引用。一个类型方法可以直接通过类型方法的名称调用本类中的其它类型方法,而无需在方法名称前面加上类型名称。类似地,在结构体和枚举中,也能够直接通过类型属性的名称访问本类中的类型属性,而不需要前面加上类型名称。
|
||||
|
||||
下面的例子定义了一个名为 `LevelTracker` 结构体。它监测玩家的游戏发展情况(游戏的不同层次或阶段)。这是一个单人游戏,但也可以存储多个玩家在同一设备上的游戏信息。
|
||||
|
||||
游戏初始时,所有的游戏等级(除了等级 1)都被锁定。每次有玩家完成一个等级,这个等级就对这个设备上的所有玩家解锁。`LevelTracker` 结构体用类型属性和方法监测游戏的哪个等级已经被解锁。它还监测每个玩家的当前等级。
|
||||
|
||||
```swift
|
||||
struct LevelTracker {
|
||||
static var highestUnlockedLevel = 1
|
||||
var currentLevel = 1
|
||||
|
||||
static func unlock(_ level: Int) {
|
||||
if level > highestUnlockedLevel { highestUnlockedLevel = level }
|
||||
}
|
||||
|
||||
static func isUnlocked(_ level: Int) -> Bool {
|
||||
return level <= highestUnlockedLevel
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
mutating func advance(to level: Int) -> Bool {
|
||||
if LevelTracker.isUnlocked(level) {
|
||||
currentLevel = level
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`LevelTracker` 监测玩家已解锁的最高等级。这个值被存储在类型属性 `highestUnlockedLevel` 中。
|
||||
|
||||
`LevelTracker` 还定义了两个类型方法与 `highestUnlockedLevel` 配合工作。第一个类型方法是 `unlock(_:)`,一旦新等级被解锁,它会更新 `highestUnlockedLevel` 的值。第二个类型方法是 `isUnlocked(_:)`,如果某个给定的等级已经被解锁,它将返回 `true`。(注意,尽管我们没有使用类似 `LevelTracker.highestUnlockedLevel` 的写法,这个类型方法还是能够访问类型属性 `highestUnlockedLevel`)
|
||||
|
||||
除了类型属性和类型方法,`LevelTracker` 还监测每个玩家的进度。它用实例属性 `currentLevel` 来监测每个玩家当前的等级。
|
||||
|
||||
为了便于管理 `currentLevel` 属性,`LevelTracker` 定义了实例方法 `advance(to:)`。这个方法会在更新 `currentLevel` 之前检查所请求的新等级是否已经解锁。`advance(to:)` 方法返回布尔值以指示是否能够设置 `currentLevel`。因为允许在调用 `advance(to:)` 时候忽略返回值,不会产生编译警告,所以函数被标注为 `@discardableResult` 属性,更多关于属性信息,请参考 [特性](../03_language_reference/07_Attributes.md) 章节。
|
||||
|
||||
下面,`Player` 类使用 `LevelTracker` 来监测和更新每个玩家的发展进度:
|
||||
|
||||
```swift
|
||||
class Player {
|
||||
var tracker = LevelTracker()
|
||||
let playerName: String
|
||||
func complete(level: Int) {
|
||||
LevelTracker.unlock(level + 1)
|
||||
tracker.advance(to: level + 1)
|
||||
}
|
||||
init(name: String) {
|
||||
playerName = name
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Player` 类创建一个新的 `LevelTracker` 实例来监测这个用户的进度。它提供了 `complete(level:)` 方法,一旦玩家完成某个指定等级就调用它。这个方法为所有玩家解锁下一等级,并且将当前玩家的进度更新为下一等级。(我们忽略了 `advance(to:)` 返回的布尔值,因为之前调用 `LevelTracker.unlock(_:)` 时就知道了这个等级已经被解锁了)。
|
||||
|
||||
你还可以为一个新的玩家创建一个 `Player` 的实例,然后看这个玩家完成等级一时发生了什么:
|
||||
|
||||
```swift
|
||||
var player = Player(name: "Argyrios")
|
||||
player.complete(level: 1)
|
||||
print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
|
||||
// 打印“highest unlocked level is now 2”
|
||||
```
|
||||
|
||||
如果你创建了第二个玩家,并尝试让他开始一个没有被任何玩家解锁的等级,那么试图设置玩家当前等级将会失败:
|
||||
|
||||
```swift
|
||||
player = Player(name: "Beto")
|
||||
if player.tracker.advance(to: 6) {
|
||||
print("player is now on level 6")
|
||||
} else {
|
||||
print("level 6 has not yet been unlocked")
|
||||
}
|
||||
// 打印“level 6 has not yet been unlocked”
|
||||
```
|
||||
@ -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` 数组按照从左上到右下的阅读顺序将矩阵扁平化存储:
|
||||
|
||||

|
||||
|
||||
@ -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)
|
||||
```
|
||||
|
||||
@ -1,221 +1,221 @@
|
||||
# 继承
|
||||
|
||||
一个类可以*继承*另一个类的方法,属性和其它特性。当一个类继承其它类时,继承类叫*子类*,被继承类叫*超类(或父类)*。在 Swift 中,继承是区分「类」与其它类型的一个基本特征。
|
||||
|
||||
在 Swift 中,类可以调用和访问超类的方法、属性和下标,并且可以重写这些方法,属性和下标来优化或修改它们的行为。Swift 会检查你的重写定义在超类中是否有匹配的定义,以此确保你的重写行为是正确的。
|
||||
|
||||
可以为类中继承来的属性添加属性观察器,这样一来,当属性值改变时,类就会被通知到。可以为任何属性添加属性观察器,无论它原本被定义为存储型属性还是计算型属性。
|
||||
|
||||
## 定义一个基类 {#defining-a-base-class}
|
||||
|
||||
不继承于其它类的类,称之为*基类*。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> Swift 中的类并不是从一个通用的基类继承而来的。如果你不为自己定义的类指定一个超类的话,这个类就会自动成为基类。
|
||||
|
||||
下面的例子定义了一个叫 `Vehicle` 的基类。这个基类声明了一个名为 `currentSpeed`,默认值是 `0.0` 的存储型属性(属性类型推断为 `Double`)。`currentSpeed` 属性的值被一个 `String` 类型的只读计算型属性 `description` 使用,用来创建对于车辆的描述。
|
||||
|
||||
`Vehicle` 基类还定义了一个名为 `makeNoise` 的方法。这个方法实际上不为 `Vehicle` 实例做任何事,但之后将会被 `Vehicle` 的子类定制:
|
||||
|
||||
```swift
|
||||
class Vehicle {
|
||||
var currentSpeed = 0.0
|
||||
var description: String {
|
||||
return "traveling at \(currentSpeed) miles per hour"
|
||||
}
|
||||
func makeNoise() {
|
||||
// 什么也不做——因为车辆不一定会有噪音
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
可以用初始化语法创建一个 `Vehicle` 的新实例,即类名后面跟一个空括号:
|
||||
|
||||
```swift
|
||||
let someVehicle = Vehicle()
|
||||
```
|
||||
|
||||
现在已经创建了一个 `Vehicle` 的新实例,你可以访问它的 `description` 属性来打印车辆的当前速度:
|
||||
|
||||
```swift
|
||||
print("Vehicle: \(someVehicle.description)")
|
||||
// 打印“Vehicle: traveling at 0.0 miles per hour”
|
||||
```
|
||||
|
||||
`Vehicle` 类定义了一个具有通用特性的车辆类,但实际上对于它本身来说没什么用处。为了让它变得更加有用,还需要进一步完善它,从而能够描述一个具体类型的车辆。
|
||||
|
||||
## 子类生成 {#subclassing}
|
||||
|
||||
*子类生成*指的是在一个已有类的基础上创建一个新的类。子类继承超类的特性,并且可以进一步完善。你还可以为子类添加新的特性。
|
||||
|
||||
为了指明某个类的超类,将超类名写在子类名的后面,用冒号分隔:
|
||||
|
||||
```swift
|
||||
class SomeClass: SomeSuperclass {
|
||||
// 这里是子类的定义
|
||||
}
|
||||
```
|
||||
|
||||
下一个例子,定义了一个叫 `Bicycle` 的子类,继承自父类 `Vehicle`:
|
||||
|
||||
```swift
|
||||
class Bicycle: Vehicle {
|
||||
var hasBasket = false
|
||||
}
|
||||
```
|
||||
|
||||
新的 `Bicycle` 类自动继承 `Vehicle` 类的所有特性,比如 `currentSpeed` 和 `description` 属性,还有 `makeNoise()` 方法。
|
||||
|
||||
除了所继承的特性,`Bicycle` 类还定义了一个默认值为 `false` 的存储型属性 `hasBasket`(属性推断为 `Bool`)。
|
||||
|
||||
默认情况下,你创建的所有新的 `Bicycle` 实例不会有一个篮子(即 `hasBasket` 属性默认为 `false`)。创建该实例之后,你可以为 `Bicycle` 实例设置 `hasBasket` 属性为 `ture`:
|
||||
|
||||
```swift
|
||||
let bicycle = Bicycle()
|
||||
bicycle.hasBasket = true
|
||||
```
|
||||
|
||||
你还可以修改 `Bicycle` 实例所继承的 `currentSpeed` 属性,和查询实例所继承的 `description` 属性:
|
||||
|
||||
```swift
|
||||
bicycle.currentSpeed = 15.0
|
||||
print("Bicycle: \(bicycle.description)")
|
||||
// 打印“Bicycle: traveling at 15.0 miles per hour”
|
||||
```
|
||||
|
||||
子类还可以继续被其它类继承,下面的示例为 `Bicycle` 创建了一个名为 `Tandem`(双人自行车)的子类:
|
||||
|
||||
```swift
|
||||
class Tandem: Bicycle {
|
||||
var currentNumberOfPassengers = 0
|
||||
}
|
||||
```
|
||||
|
||||
`Tandem` 从 `Bicycle` 继承了所有的属性与方法,这又使它同时继承了 `Vehicle` 的所有属性与方法。`Tandem` 也增加了一个新的叫做 `currentNumberOfPassengers` 的存储型属性,默认值为 `0`。
|
||||
|
||||
如果你创建了一个 `Tandem` 的实例,你可以使用它所有的新属性和继承的属性,还能查询从 `Vehicle` 继承来的只读属性 `description`:
|
||||
|
||||
```swift
|
||||
let tandem = Tandem()
|
||||
tandem.hasBasket = true
|
||||
tandem.currentNumberOfPassengers = 2
|
||||
tandem.currentSpeed = 22.0
|
||||
print("Tandem: \(tandem.description)")
|
||||
// 打印:“Tandem: traveling at 22.0 miles per hour”
|
||||
```
|
||||
|
||||
## 重写 {#overriding}
|
||||
|
||||
子类可以为继承来的实例方法,类方法,实例属性,类属性,或下标提供自己定制的实现。我们把这种行为叫*重写*。
|
||||
|
||||
如果要重写某个特性,你需要在重写定义的前面加上 `override` 关键字。这么做,就表明了你是想提供一个重写版本,而非错误地提供了一个相同的定义。意外的重写行为可能会导致不可预知的错误,任何缺少 `override` 关键字的重写都会在编译时被认定为错误。
|
||||
|
||||
`override` 关键字会提醒 Swift 编译器去检查该类的超类(或其中一个父类)是否有匹配重写版本的声明。这个检查可以确保你的重写定义是正确的。
|
||||
|
||||
### 访问超类的方法,属性及下标 {#accessing-superclass-methods-properties-and-subscripts}
|
||||
|
||||
当你在子类中重写超类的方法,属性或下标时,有时在你的重写版本中使用已经存在的超类实现会大有裨益。比如,你可以完善已有实现的行为,或在一个继承来的变量中存储一个修改过的值。
|
||||
|
||||
在合适的地方,你可以通过使用 `super` 前缀来访问超类版本的方法,属性或下标:
|
||||
|
||||
* 在方法 `someMethod()` 的重写实现中,可以通过 `super.someMethod()` 来调用超类版本的 `someMethod()` 方法。
|
||||
* 在属性 `someProperty` 的 getter 或 setter 的重写实现中,可以通过 `super.someProperty` 来访问超类版本的 `someProperty` 属性。
|
||||
* 在下标的重写实现中,可以通过 `super[someIndex]` 来访问超类版本中的相同下标。
|
||||
|
||||
### 重写方法 {#overriding-methods}
|
||||
|
||||
在子类中,你可以重写继承来的实例方法或类方法,提供一个定制或替代的方法实现。
|
||||
|
||||
下面的例子定义了 `Vehicle` 的一个新的子类,叫 `Train`,它重写了从 `Vehicle` 类继承来的 `makeNoise()` 方法:
|
||||
|
||||
```swift
|
||||
class Train: Vehicle {
|
||||
override func makeNoise() {
|
||||
print("Choo Choo")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
如果你创建一个 `Train` 的新实例,并调用了它的 `makeNoise()` 方法,你就会发现 `Train` 版本的方法被调用:
|
||||
|
||||
```swift
|
||||
let train = Train()
|
||||
train.makeNoise()
|
||||
// 打印“Choo Choo”
|
||||
```
|
||||
|
||||
### 重写属性 {#overriding-properties}
|
||||
|
||||
你可以重写继承来的实例属性或类型属性,提供自己定制的 getter 和 setter,或添加属性观察器,使重写的属性可以观察到底层的属性值什么时候发生改变。
|
||||
|
||||
#### 重写属性的 Getters 和 Setters {#overriding-property-etters-and-setters}
|
||||
|
||||
你可以提供定制的 getter(或 setter)来重写任何一个继承来的属性,无论这个属性是存储型还是计算型属性。子类并不知道继承来的属性是存储型的还是计算型的,它只知道继承来的属性会有一个名字和类型。你在重写一个属性时,必须将它的名字和类型都写出来。这样才能使编译器去检查你重写的属性是与超类中同名同类型的属性相匹配的。
|
||||
|
||||
你可以将一个继承来的只读属性重写为一个读写属性,只需要在重写版本的属性里提供 getter 和 setter 即可。但是,你不可以将一个继承来的读写属性重写为一个只读属性。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 如果你在重写属性中提供了 setter,那么你也一定要提供 getter。如果你不想在重写版本中的 getter 里修改继承来的属性值,你可以直接通过 `super.someProperty` 来返回继承来的值,其中 `someProperty` 是你要重写的属性的名字。
|
||||
|
||||
以下的例子定义了一个新类,叫 `Car`,它是 `Vehicle` 的子类。这个类引入了一个新的存储型属性叫做 `gear`,默认值为整数 `1`。`Car` 类重写了继承自 `Vehicle` 的 `description` 属性,提供包含当前档位的自定义描述:
|
||||
|
||||
```swift
|
||||
class Car: Vehicle {
|
||||
var gear = 1
|
||||
override var description: String {
|
||||
return super.description + " in gear \(gear)"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
重写的 `description` 属性首先要调用 `super.description` 返回 `Vehicle` 类的 `description` 属性。之后,`Car` 类版本的 `description` 在末尾增加了一些额外的文本来提供关于当前档位的信息。
|
||||
|
||||
如果你创建了 `Car` 的实例并且设置了它的 `gear` 和 `currentSpeed` 属性,你可以看到它的 `description` 返回了 `Car` 中的自定义描述:
|
||||
|
||||
```swift
|
||||
let car = Car()
|
||||
car.currentSpeed = 25.0
|
||||
car.gear = 3
|
||||
print("Car: \(car.description)")
|
||||
// 打印“Car: traveling at 25.0 miles per hour in gear 3”
|
||||
```
|
||||
|
||||
#### 重写属性观察器 {#overriding-property-observers}
|
||||
|
||||
你可以通过重写属性为一个继承来的属性添加属性观察器。这样一来,无论被继承属性原本是如何实现的,当其属性值发生改变时,你就会被通知到。关于属性观察器的更多内容,请看[属性观察器](../chapter2/10_Properties.html#property_observers)。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 你不可以为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。这些属性的值是不可以被设置的,所以,为它们提供 `willSet` 或 `didSet` 实现也是不恰当。
|
||||
此外还要注意,你不可以同时提供重写的 setter 和重写的属性观察器。如果你想观察属性值的变化,并且你已经为那个属性提供了定制的 setter,那么你在 setter 中就可以观察到任何值变化了。
|
||||
|
||||
下面的例子定义了一个新类叫 `AutomaticCar`,它是 `Car` 的子类。`AutomaticCar` 表示自动档汽车,它可以根据当前的速度自动选择合适的档位:
|
||||
|
||||
```swift
|
||||
class AutomaticCar: Car {
|
||||
override var currentSpeed: Double {
|
||||
didSet {
|
||||
gear = Int(currentSpeed / 10.0) + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
当你设置 `AutomaticCar` 的 `currentSpeed` 属性,属性的 `didSet` 观察器就会自动地设置 `gear` 属性,为新的速度选择一个合适的档位。具体来说就是,属性观察器将新的速度值除以 `10`,然后向下取得最接近的整数值,最后加 `1` 来得到档位 `gear` 的值。例如,速度为 `35.0` 时,档位为 `4`:
|
||||
|
||||
```swift
|
||||
let automatic = AutomaticCar()
|
||||
automatic.currentSpeed = 35.0
|
||||
print("AutomaticCar: \(automatic.description)")
|
||||
// 打印“AutomaticCar: traveling at 35.0 miles per hour in gear 4”
|
||||
```
|
||||
|
||||
## 防止重写 {#preventing-overrides}
|
||||
|
||||
你可以通过把方法,属性或下标标记为 *`final`* 来防止它们被重写,只需要在声明关键字前加上 `final` 修饰符即可(例如:`final var`、`final func`、`final class func` 以及 `final subscript`)。
|
||||
|
||||
任何试图对带有 `final` 标记的方法、属性或下标进行重写的代码,都会在编译时会报错。在类扩展中的方法,属性或下标也可以在扩展的定义里标记为 `final`。
|
||||
|
||||
可以通过在关键字 `class` 前添加 `final` 修饰符(`final class`)来将整个类标记为 final 。这样的类是不可被继承的,试图继承这样的类会导致编译报错。
|
||||
# 继承
|
||||
|
||||
一个类可以*继承*另一个类的方法,属性和其它特性。当一个类继承其它类时,继承类叫*子类*,被继承类叫*超类(或父类)*。在 Swift 中,继承是区分「类」与其它类型的一个基本特征。
|
||||
|
||||
在 Swift 中,类可以调用和访问超类的方法、属性和下标,并且可以重写这些方法,属性和下标来优化或修改它们的行为。Swift 会检查你的重写定义在超类中是否有匹配的定义,以此确保你的重写行为是正确的。
|
||||
|
||||
可以为类中继承来的属性添加属性观察器,这样一来,当属性值改变时,类就会被通知到。可以为任何属性添加属性观察器,无论它原本被定义为存储型属性还是计算型属性。
|
||||
|
||||
## 定义一个基类 {#defining-a-base-class}
|
||||
|
||||
不继承于其它类的类,称之为*基类*。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> Swift 中的类并不是从一个通用的基类继承而来的。如果你不为自己定义的类指定一个超类的话,这个类就会自动成为基类。
|
||||
|
||||
下面的例子定义了一个叫 `Vehicle` 的基类。这个基类声明了一个名为 `currentSpeed`,默认值是 `0.0` 的存储型属性(属性类型推断为 `Double`)。`currentSpeed` 属性的值被一个 `String` 类型的只读计算型属性 `description` 使用,用来创建对于车辆的描述。
|
||||
|
||||
`Vehicle` 基类还定义了一个名为 `makeNoise` 的方法。这个方法实际上不为 `Vehicle` 实例做任何事,但之后将会被 `Vehicle` 的子类定制:
|
||||
|
||||
```swift
|
||||
class Vehicle {
|
||||
var currentSpeed = 0.0
|
||||
var description: String {
|
||||
return "traveling at \(currentSpeed) miles per hour"
|
||||
}
|
||||
func makeNoise() {
|
||||
// 什么也不做——因为车辆不一定会有噪音
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
可以用初始化语法创建一个 `Vehicle` 的新实例,即类名后面跟一个空括号:
|
||||
|
||||
```swift
|
||||
let someVehicle = Vehicle()
|
||||
```
|
||||
|
||||
现在已经创建了一个 `Vehicle` 的新实例,你可以访问它的 `description` 属性来打印车辆的当前速度:
|
||||
|
||||
```swift
|
||||
print("Vehicle: \(someVehicle.description)")
|
||||
// 打印“Vehicle: traveling at 0.0 miles per hour”
|
||||
```
|
||||
|
||||
`Vehicle` 类定义了一个具有通用特性的车辆类,但实际上对于它本身来说没什么用处。为了让它变得更加有用,还需要进一步完善它,从而能够描述一个具体类型的车辆。
|
||||
|
||||
## 子类生成 {#subclassing}
|
||||
|
||||
*子类生成*指的是在一个已有类的基础上创建一个新的类。子类继承超类的特性,并且可以进一步完善。你还可以为子类添加新的特性。
|
||||
|
||||
为了指明某个类的超类,将超类名写在子类名的后面,用冒号分隔:
|
||||
|
||||
```swift
|
||||
class SomeClass: SomeSuperclass {
|
||||
// 这里是子类的定义
|
||||
}
|
||||
```
|
||||
|
||||
下一个例子,定义了一个叫 `Bicycle` 的子类,继承自超类 `Vehicle`:
|
||||
|
||||
```swift
|
||||
class Bicycle: Vehicle {
|
||||
var hasBasket = false
|
||||
}
|
||||
```
|
||||
|
||||
新的 `Bicycle` 类自动继承 `Vehicle` 类的所有特性,比如 `currentSpeed` 和 `description` 属性,还有 `makeNoise()` 方法。
|
||||
|
||||
除了所继承的特性,`Bicycle` 类还定义了一个默认值为 `false` 的存储型属性 `hasBasket`(属性推断为 `Bool`)。
|
||||
|
||||
默认情况下,你创建的所有新的 `Bicycle` 实例不会有一个篮子(即 `hasBasket` 属性默认为 `false`)。创建该实例之后,你可以为 `Bicycle` 实例设置 `hasBasket` 属性为 `ture`:
|
||||
|
||||
```swift
|
||||
let bicycle = Bicycle()
|
||||
bicycle.hasBasket = true
|
||||
```
|
||||
|
||||
你还可以修改 `Bicycle` 实例所继承的 `currentSpeed` 属性,和查询实例所继承的 `description` 属性:
|
||||
|
||||
```swift
|
||||
bicycle.currentSpeed = 15.0
|
||||
print("Bicycle: \(bicycle.description)")
|
||||
// 打印“Bicycle: traveling at 15.0 miles per hour”
|
||||
```
|
||||
|
||||
子类还可以继续被其它类继承,下面的示例为 `Bicycle` 创建了一个名为 `Tandem`(双人自行车)的子类:
|
||||
|
||||
```swift
|
||||
class Tandem: Bicycle {
|
||||
var currentNumberOfPassengers = 0
|
||||
}
|
||||
```
|
||||
|
||||
`Tandem` 从 `Bicycle` 继承了所有的属性与方法,这又使它同时继承了 `Vehicle` 的所有属性与方法。`Tandem` 也增加了一个新的叫做 `currentNumberOfPassengers` 的存储型属性,默认值为 `0`。
|
||||
|
||||
如果你创建了一个 `Tandem` 的实例,你可以使用它所有的新属性和继承的属性,还能查询从 `Vehicle` 继承来的只读属性 `description`:
|
||||
|
||||
```swift
|
||||
let tandem = Tandem()
|
||||
tandem.hasBasket = true
|
||||
tandem.currentNumberOfPassengers = 2
|
||||
tandem.currentSpeed = 22.0
|
||||
print("Tandem: \(tandem.description)")
|
||||
// 打印:“Tandem: traveling at 22.0 miles per hour”
|
||||
```
|
||||
|
||||
## 重写 {#overriding}
|
||||
|
||||
子类可以为继承来的实例方法,类方法,实例属性,类属性,或下标提供自己定制的实现。我们把这种行为叫*重写*。
|
||||
|
||||
如果要重写某个特性,你需要在重写定义的前面加上 `override` 关键字。这么做,就表明了你是想提供一个重写版本,而非错误地提供了一个相同的定义。意外的重写行为可能会导致不可预知的错误,任何缺少 `override` 关键字的重写都会在编译时被认定为错误。
|
||||
|
||||
`override` 关键字会提醒 Swift 编译器去检查该类的超类(或其中一个超类)是否有匹配重写版本的声明。这个检查可以确保你的重写定义是正确的。
|
||||
|
||||
### 访问超类的方法,属性及下标 {#accessing-superclass-methods-properties-and-subscripts}
|
||||
|
||||
当你在子类中重写超类的方法,属性或下标时,有时在你的重写版本中使用已经存在的超类实现会大有裨益。比如,你可以完善已有实现的行为,或在一个继承来的变量中存储一个修改过的值。
|
||||
|
||||
在合适的地方,你可以通过使用 `super` 前缀来访问超类版本的方法,属性或下标:
|
||||
|
||||
* 在方法 `someMethod()` 的重写实现中,可以通过 `super.someMethod()` 来调用超类版本的 `someMethod()` 方法。
|
||||
* 在属性 `someProperty` 的 getter 或 setter 的重写实现中,可以通过 `super.someProperty` 来访问超类版本的 `someProperty` 属性。
|
||||
* 在下标的重写实现中,可以通过 `super[someIndex]` 来访问超类版本中的相同下标。
|
||||
|
||||
### 重写方法 {#overriding-methods}
|
||||
|
||||
在子类中,你可以重写继承来的实例方法或类方法,提供一个定制或替代的方法实现。
|
||||
|
||||
下面的例子定义了 `Vehicle` 的一个新的子类,叫 `Train`,它重写了从 `Vehicle` 类继承来的 `makeNoise()` 方法:
|
||||
|
||||
```swift
|
||||
class Train: Vehicle {
|
||||
override func makeNoise() {
|
||||
print("Choo Choo")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
如果你创建一个 `Train` 的新实例,并调用了它的 `makeNoise()` 方法,你就会发现 `Train` 版本的方法被调用:
|
||||
|
||||
```swift
|
||||
let train = Train()
|
||||
train.makeNoise()
|
||||
// 打印“Choo Choo”
|
||||
```
|
||||
|
||||
### 重写属性 {#overriding-properties}
|
||||
|
||||
你可以重写继承来的实例属性或类型属性,提供自己定制的 getter 和 setter,或添加属性观察器,使重写的属性可以观察到底层的属性值什么时候发生改变。
|
||||
|
||||
#### 重写属性的 Getters 和 Setters {#overriding-property-etters-and-setters}
|
||||
|
||||
你可以提供定制的 getter(或 setter)来重写任何一个继承来的属性,无论这个属性是存储型还是计算型属性。子类并不知道继承来的属性是存储型的还是计算型的,它只知道继承来的属性会有一个名字和类型。你在重写一个属性时,必须将它的名字和类型都写出来。这样才能使编译器去检查你重写的属性是与超类中同名同类型的属性相匹配的。
|
||||
|
||||
你可以将一个继承来的只读属性重写为一个读写属性,只需要在重写版本的属性里提供 getter 和 setter 即可。但是,你不可以将一个继承来的读写属性重写为一个只读属性。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 如果你在重写属性中提供了 setter,那么你也一定要提供 getter。如果你不想在重写版本中的 getter 里修改继承来的属性值,你可以直接通过 `super.someProperty` 来返回继承来的值,其中 `someProperty` 是你要重写的属性的名字。
|
||||
|
||||
以下的例子定义了一个新类,叫 `Car`,它是 `Vehicle` 的子类。这个类引入了一个新的存储型属性叫做 `gear`,默认值为整数 `1`。`Car` 类重写了继承自 `Vehicle` 的 `description` 属性,提供包含当前档位的自定义描述:
|
||||
|
||||
```swift
|
||||
class Car: Vehicle {
|
||||
var gear = 1
|
||||
override var description: String {
|
||||
return super.description + " in gear \(gear)"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
重写的 `description` 属性首先要调用 `super.description` 返回 `Vehicle` 类的 `description` 属性。之后,`Car` 类版本的 `description` 在末尾增加了一些额外的文本来提供关于当前档位的信息。
|
||||
|
||||
如果你创建了 `Car` 的实例并且设置了它的 `gear` 和 `currentSpeed` 属性,你可以看到它的 `description` 返回了 `Car` 中的自定义描述:
|
||||
|
||||
```swift
|
||||
let car = Car()
|
||||
car.currentSpeed = 25.0
|
||||
car.gear = 3
|
||||
print("Car: \(car.description)")
|
||||
// 打印“Car: traveling at 25.0 miles per hour in gear 3”
|
||||
```
|
||||
|
||||
#### 重写属性观察器 {#overriding-property-observers}
|
||||
|
||||
你可以通过重写属性为一个继承来的属性添加属性观察器。这样一来,无论被继承属性原本是如何实现的,当其属性值发生改变时,你就会被通知到。关于属性观察器的更多内容,请看 [属性观察器](../02_language_guide/10_Properties.md#property-observers)。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 你不可以为继承来的常量存储型属性或继承来的只读计算型属性添加属性观察器。这些属性的值是不可以被设置的,所以,为它们提供 `willSet` 或 `didSet` 实现也是不恰当。
|
||||
此外还要注意,你不可以同时提供重写的 setter 和重写的属性观察器。如果你想观察属性值的变化,并且你已经为那个属性提供了定制的 setter,那么你在 setter 中就可以观察到任何值变化了。
|
||||
|
||||
下面的例子定义了一个新类叫 `AutomaticCar`,它是 `Car` 的子类。`AutomaticCar` 表示自动档汽车,它可以根据当前的速度自动选择合适的档位:
|
||||
|
||||
```swift
|
||||
class AutomaticCar: Car {
|
||||
override var currentSpeed: Double {
|
||||
didSet {
|
||||
gear = Int(currentSpeed / 10.0) + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
当你设置 `AutomaticCar` 的 `currentSpeed` 属性,属性的 `didSet` 观察器就会自动地设置 `gear` 属性,为新的速度选择一个合适的档位。具体来说就是,属性观察器将新的速度值除以 `10`,然后向下取得最接近的整数值,最后加 `1` 来得到档位 `gear` 的值。例如,速度为 `35.0` 时,档位为 `4`:
|
||||
|
||||
```swift
|
||||
let automatic = AutomaticCar()
|
||||
automatic.currentSpeed = 35.0
|
||||
print("AutomaticCar: \(automatic.description)")
|
||||
// 打印“AutomaticCar: traveling at 35.0 miles per hour in gear 4”
|
||||
```
|
||||
|
||||
## 防止重写 {#preventing-overrides}
|
||||
|
||||
你可以通过把方法,属性或下标标记为 *`final`* 来防止它们被重写,只需要在声明关键字前加上 `final` 修饰符即可(例如:`final var`、`final func`、`final class func` 以及 `final subscript`)。
|
||||
|
||||
任何试图对带有 `final` 标记的方法、属性或下标进行重写的代码,都会在编译时会报错。在类扩展中的方法,属性或下标也可以在扩展的定义里标记为 `final`。
|
||||
|
||||
可以通过在关键字 `class` 前添加 `final` 修饰符(`final class`)来将整个类标记为 final 。这样的类是不可被继承的,试图继承这样的类会导致编译报错。
|
||||
@ -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` 类拥有一个指定构造器 `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 {
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
## 析构过程原理 {#how-deinitialization-works}
|
||||
|
||||
Swift 会自动释放不再需要的实例以释放资源。如[自动引用计数](./23_Automatic_Reference_Counting.md)章节中所讲述,Swift 通过*自动引用计数(ARC)* 处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前手动去关闭该文件。
|
||||
Swift 会自动释放不再需要的实例以释放资源。如 [自动引用计数](./24_Automatic_Reference_Counting.md) 章节中所讲述,Swift 通过*自动引用计数(ARC)* 处理实例的内存管理。通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前手动去关闭该文件。
|
||||
|
||||
在类的定义中,每个类最多只能有一个析构器,而且析构器不带任何参数和圆括号,如下所示:
|
||||
|
||||
@ -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 {
|
||||
@ -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!` 来禁用错误传递,这会把调用包装在一个不会有错误抛出的运行时断言中。如果真的抛出了错误,你会得到一个运行时错误。
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -1,85 +1,85 @@
|
||||
# 嵌套类型
|
||||
|
||||
枚举常被用于为特定类或结构体实现某些功能。类似地,枚举可以方便的定义工具类或结构体,从而为某个复杂的类型所使用。为了实现这种功能,Swift 允许你定义*嵌套类型*,可以在支持的类型中定义嵌套的枚举、类和结构体。
|
||||
|
||||
要在一个类型中嵌套另一个类型,将嵌套类型的定义写在其外部类型的 `{}` 内,而且可以根据需要定义多级嵌套。
|
||||
|
||||
## 嵌套类型实践 {#nested-types-in-action}
|
||||
|
||||
下面这个例子定义了一个结构体 `BlackjackCard`(二十一点),用来模拟 `BlackjackCard` 中的扑克牌点数。`BlackjackCard` 结构体包含两个嵌套定义的枚举类型 `Suit` 和 `Rank`。
|
||||
|
||||
在 `BlackjackCard` 中,`Ace` 牌可以表示 `1` 或者 `11`,`Ace` 牌的这一特征通过一个嵌套在 `Rank` 枚举中的结构体 `Values` 来表示:
|
||||
|
||||
```swift
|
||||
struct BlackjackCard {
|
||||
|
||||
// 嵌套的 Suit 枚举
|
||||
enum Suit: Character {
|
||||
case spades = "♠", hearts = "♡", diamonds = "♢", clubs = "♣"
|
||||
}
|
||||
|
||||
// 嵌套的 Rank 枚举
|
||||
enum Rank: Int {
|
||||
case two = 2, three, four, five, six, seven, eight, nine, ten
|
||||
case jack, queen, king, ace
|
||||
struct Values {
|
||||
let first: Int, second: Int?
|
||||
}
|
||||
var values: Values {
|
||||
switch self {
|
||||
case .ace:
|
||||
return Values(first: 1, second: 11)
|
||||
case .jack, .queen, .king:
|
||||
return Values(first: 10, second: nil)
|
||||
default:
|
||||
return Values(first: self.rawValue, second: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BlackjackCard 的属性和方法
|
||||
let rank: Rank, suit: Suit
|
||||
var description: String {
|
||||
var output = "suit is \(suit.rawValue),"
|
||||
output += " value is \(rank.values.first)"
|
||||
if let second = rank.values.second {
|
||||
output += " or \(second)"
|
||||
}
|
||||
return output
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Suit` 枚举用来描述扑克牌的四种花色,并用一个 `Character` 类型的原始值表示花色符号。
|
||||
|
||||
`Rank` 枚举用来描述扑克牌从 `Ace`~`10`,以及 `J`、`Q`、`K`,这 `13` 种牌,并用一个 `Int` 类型的原始值表示牌的面值。(这个 `Int` 类型的原始值未用于 `Ace`、`J`、`Q`、`K` 这 `4` 种牌。)
|
||||
|
||||
如上所述,`Rank` 枚举在内部定义了一个嵌套结构体 `Values`。结构体 `Values` 中定义了两个属性,用于反映只有 `Ace` 有两个数值,其余牌都只有一个数值:
|
||||
|
||||
- `first` 的类型为 `Int`
|
||||
- `second` 的类型为 `Int?`,或者说“可选 `Int`”
|
||||
|
||||
`Rank` 还定义了一个计算型属性 `values`,它将会返回一个 `Values` 结构体的实例。这个计算型属性会根据牌的面值,用适当的数值去初始化 `Values` 实例。对于 `J`、`Q`、`K`、`Ace` 这四种牌,会使用特殊数值。对于数字面值的牌,使用枚举实例的 `Int` 类型的原始值。
|
||||
|
||||
`BlackjackCard` 结构体拥有两个属性——`rank` 与 `suit`。它也同样定义了一个计算型属性 `description`,`description` 属性用 `rank` 和 `suit` 中的内容来构建对扑克牌名字和数值的描述。该属性使用可选绑定来检查可选类型 `second` 是否有值,若有值,则在原有的描述中增加对 `second` 的描述。
|
||||
|
||||
因为 `BlackjackCard` 是一个没有自定义构造器的结构体,在[结构体的逐一成员构造器](./14_Initialization.md#memberwise_initializers_for_structure_types)中可知,结构体有默认的成员构造器,所以你可以用默认的构造器去初始化新常量 `theAceOfSpades`:
|
||||
|
||||
```swift
|
||||
let theAceOfSpades = BlackjackCard(rank: .ace, suit: .spades)
|
||||
print("theAceOfSpades: \(theAceOfSpades.description)")
|
||||
// 打印“theAceOfSpades: suit is ♠, value is 1 or 11”
|
||||
```
|
||||
|
||||
尽管 `Rank` 和 `Suit` 嵌套在 `BlackjackCard` 中,但它们的类型仍可从上下文中推断出来,所以在初始化实例时能够单独通过成员名称(`.ace` 和 `.spades`)引用枚举实例。在上面的例子中,`description` 属性正确地反映了黑桃 A 牌具有 `1` 和 `11` 两个值。
|
||||
|
||||
## 引用嵌套类型 {#referring-to-nested-types}
|
||||
|
||||
在外部引用嵌套类型时,在嵌套类型的类型名前加上其外部类型的类型名作为前缀:
|
||||
|
||||
```swift
|
||||
let heartsSymbol = BlackjackCard.Suit.hearts.rawValue
|
||||
// 红心符号为“♡”
|
||||
```
|
||||
|
||||
对于上面这个例子,这样可以使 `Suit`、`Rank` 和 `Values` 的名字尽可能的短,因为它们的名字可以由定义它们的上下文来限定。
|
||||
# 嵌套类型
|
||||
|
||||
枚举常被用于为特定类或结构体实现某些功能。类似地,枚举可以方便的定义工具类或结构体,从而为某个复杂的类型所使用。为了实现这种功能,Swift 允许你定义*嵌套类型*,可以在支持的类型中定义嵌套的枚举、类和结构体。
|
||||
|
||||
要在一个类型中嵌套另一个类型,将嵌套类型的定义写在其外部类型的 `{}` 内,而且可以根据需要定义多级嵌套。
|
||||
|
||||
## 嵌套类型实践 {#nested-types-in-action}
|
||||
|
||||
下面这个例子定义了一个结构体 `BlackjackCard`(二十一点),用来模拟 `BlackjackCard` 中的扑克牌点数。`BlackjackCard` 结构体包含两个嵌套定义的枚举类型 `Suit` 和 `Rank`。
|
||||
|
||||
在 `BlackjackCard` 中,`Ace` 牌可以表示 `1` 或者 `11`,`Ace` 牌的这一特征通过一个嵌套在 `Rank` 枚举中的结构体 `Values` 来表示:
|
||||
|
||||
```swift
|
||||
struct BlackjackCard {
|
||||
|
||||
// 嵌套的 Suit 枚举
|
||||
enum Suit: Character {
|
||||
case spades = "♠", hearts = "♡", diamonds = "♢", clubs = "♣"
|
||||
}
|
||||
|
||||
// 嵌套的 Rank 枚举
|
||||
enum Rank: Int {
|
||||
case two = 2, three, four, five, six, seven, eight, nine, ten
|
||||
case jack, queen, king, ace
|
||||
struct Values {
|
||||
let first: Int, second: Int?
|
||||
}
|
||||
var values: Values {
|
||||
switch self {
|
||||
case .ace:
|
||||
return Values(first: 1, second: 11)
|
||||
case .jack, .queen, .king:
|
||||
return Values(first: 10, second: nil)
|
||||
default:
|
||||
return Values(first: self.rawValue, second: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BlackjackCard 的属性和方法
|
||||
let rank: Rank, suit: Suit
|
||||
var description: String {
|
||||
var output = "suit is \(suit.rawValue),"
|
||||
output += " value is \(rank.values.first)"
|
||||
if let second = rank.values.second {
|
||||
output += " or \(second)"
|
||||
}
|
||||
return output
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Suit` 枚举用来描述扑克牌的四种花色,并用一个 `Character` 类型的原始值表示花色符号。
|
||||
|
||||
`Rank` 枚举用来描述扑克牌从 `Ace`~`10`,以及 `J`、`Q`、`K`,这 `13` 种牌,并用一个 `Int` 类型的原始值表示牌的面值。(这个 `Int` 类型的原始值未用于 `Ace`、`J`、`Q`、`K` 这 `4` 种牌。)
|
||||
|
||||
如上所述,`Rank` 枚举在内部定义了一个嵌套结构体 `Values`。结构体 `Values` 中定义了两个属性,用于反映只有 `Ace` 有两个数值,其余牌都只有一个数值:
|
||||
|
||||
- `first` 的类型为 `Int`
|
||||
- `second` 的类型为 `Int?`,或者说“可选 `Int`”
|
||||
|
||||
`Rank` 还定义了一个计算型属性 `values`,它将会返回一个 `Values` 结构体的实例。这个计算型属性会根据牌的面值,用适当的数值去初始化 `Values` 实例。对于 `J`、`Q`、`K`、`Ace` 这四种牌,会使用特殊数值。对于数字面值的牌,使用枚举实例的 `Int` 类型的原始值。
|
||||
|
||||
`BlackjackCard` 结构体拥有两个属性——`rank` 与 `suit`。它也同样定义了一个计算型属性 `description`,`description` 属性用 `rank` 和 `suit` 中的内容来构建对扑克牌名字和数值的描述。该属性使用可选绑定来检查可选类型 `second` 是否有值,若有值,则在原有的描述中增加对 `second` 的描述。
|
||||
|
||||
因为 `BlackjackCard` 是一个没有自定义构造器的结构体,在 [结构体的逐一成员构造器](./14_Initialization.md#memberwise-initializers-for-structure-types) 中可知,结构体有默认的成员构造器,所以你可以用默认的构造器去初始化新常量 `theAceOfSpades`:
|
||||
|
||||
```swift
|
||||
let theAceOfSpades = BlackjackCard(rank: .ace, suit: .spades)
|
||||
print("theAceOfSpades: \(theAceOfSpades.description)")
|
||||
// 打印“theAceOfSpades: suit is ♠, value is 1 or 11”
|
||||
```
|
||||
|
||||
尽管 `Rank` 和 `Suit` 嵌套在 `BlackjackCard` 中,但它们的类型仍可从上下文中推断出来,所以在初始化实例时能够单独通过成员名称(`.ace` 和 `.spades`)引用枚举实例。在上面的例子中,`description` 属性正确地反映了黑桃 A 牌具有 `1` 和 `11` 两个值。
|
||||
|
||||
## 引用嵌套类型 {#referring-to-nested-types}
|
||||
|
||||
在外部引用嵌套类型时,在嵌套类型的类型名前加上其外部类型的类型名作为前缀:
|
||||
|
||||
```swift
|
||||
let heartsSymbol = BlackjackCard.Suit.hearts.rawValue
|
||||
// 红心符号为“♡”
|
||||
```
|
||||
|
||||
对于上面这个例子,这样可以使 `Suit`、`Rank` 和 `Values` 的名字尽可能的短,因为它们的名字可以由定义它们的上下文来限定。
|
||||
@ -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`。
|
||||
|
||||
@ -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` 协议:
|
||||
|
||||
@ -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 {
|
||||
@ -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-type’s-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 {
|
||||
249
source/02_language_guide/23_Opaque_Types.md
Normal file
249
source/02_language_guide/23_Opaque_Types.md
Normal 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`。
|
||||
File diff suppressed because it is too large
Load Diff
@ -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}
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
# 访问控制
|
||||
|
||||
*访问控制*可以限定其它源文件或模块中的代码对你的代码的访问级别。这个特性可以让我们隐藏代码的一些实现细节,并且可以为其他人可以访问和使用的代码提供接口。
|
||||
*访问控制*可以限定其它源文件或模块对你的代码的访问。这个特性可以让你隐藏代码的实现细节,并且能提供一个接口来让别人访问和使用你的代码。
|
||||
|
||||
你可以明确地给单个类型(类、结构体、枚举)设置访问级别,也可以给这些类型的属性、方法、构造器、下标等设置访问级别。协议也可以被限定在一定的范围内使用,包括协议里的全局常量、变量和函数。
|
||||
你可以明确地给单个类型(类、结构体、枚举)设置访问级别,也可以给这些类型的属性、方法、构造器、下标等设置访问级别。协议也可以被限定在一定访问级别的范围内使用,包括协议里的全局常量、变量和函数。
|
||||
|
||||
Swift 不仅提供了多种不同的访问级别,还为某些典型场景提供了默认的访问级别,这样就不需要我们在每段代码中都申明显式访问级别。其实,如果只是开发一个单一 target 的应用程序,我们完全可以不用显式声明代码的访问级别。
|
||||
Swift 不仅提供了多种不同的访问级别,还为某些典型场景提供了默认的访问级别,这样就不需要我们在每段代码中都显式声明访问级别。如果你只是开发一个单 target 的应用程序,完全可以不用显式声明代码的访问级别。
|
||||
|
||||
> 注意
|
||||
>
|
||||
> 为了简单起见,对于代码中可以设置访问级别的特性(属性、基本类型、函数等),在下面的章节中我们会称之为“实体”。
|
||||
> 为了简单起见,对于代码中可以设置访问级别的特性(属性、基本类型、函数等),在下面的章节中我们会统一称之为“实体”。
|
||||
|
||||
## 模块和源文件 {#modules-and-source-files}
|
||||
|
||||
@ -16,29 +16,22 @@ 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}
|
||||
|
||||
@ -46,22 +39,22 @@ Swift 中的访问级别遵循一个基本原则:*实体不能定义在具有
|
||||
|
||||
例如:
|
||||
|
||||
- 一个 Public 的变量,其类型的访问级别不能是 Internal,File-private 或是 Private。因为无法保证变量的类型在使用变量的地方也具有访问权限。
|
||||
- 一个 public 的变量,其类型的访问级别不能是 internal,fileprivate 或是 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。
|
||||
|
||||
> 注意
|
||||
>
|
||||
@ -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,7 +89,7 @@ var someInternalConstant = 0 // 隐式 internal
|
||||
|
||||
## 自定义类型 {#custom-types}
|
||||
|
||||
如果想为一个自定义类型指定访问级别,在定义类型时进行指定即可。新类型只能在它的访问级别限制范围内使用。例如,你定义了一个 `fileprivate` 级别的类,那这个类就只能在定义它的源文件中使用,可以作为属性类型、函数参数类型或者返回类型,等等。
|
||||
如果想为一个自定义类型指定访问级别,在定义类型时进行指定即可。新类型只能在它的访问级别限制范围内使用。例如,你定义了一个 `fileprivate` 级别的类,那这个类就只能在定义它的源文件中使用,可以作为属性类型、函数参数类型或者返回类型等等。
|
||||
|
||||
一个类型的访问级别也会影响到类型*成员*(属性、方法、构造器、下标)的默认访问级别。如果你将类型指定为 `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` 类型的别名。
|
||||
|
||||
> 注意
|
||||
>
|
||||
@ -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
|
||||
|
||||

|
||||
|
||||
溢出也会发生在有符号整型上。针对有符号整型的所有溢出加法或者减法运算都是按位运算的方式执行的,符号位也需要参与计算,正如[按位左移、右移运算符](#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` 型整数所能容纳的最大值。
|
||||
|
||||

|
||||
|
||||
@ -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)。
|
||||
|
||||
> 注意
|
||||
>
|
||||
1
source/02_language_guide/28_Concurrency.md
Normal file
1
source/02_language_guide/28_Concurrency.md
Normal file
@ -0,0 +1 @@
|
||||
# 并发
|
||||
@ -1,3 +1,3 @@
|
||||
# Swift 教程
|
||||
# Swift 语言教程
|
||||
|
||||
本章介绍了 Swift 的各种特性及其使用方法,是全书的核心部分。
|
||||
@ -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) }
|
||||
>
|
||||
|
||||
645
source/03_language_reference/02_Lexical_Structure.md
Executable file
645
source/03_language_reference/02_Lexical_Structure.md
Executable 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+0000,U+0009,U+000B,U+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+00A8,U+00AA,U+00AD,U+00AF,U+00B2–U+00B5,或者 U+00B7–U+00BA
|
||||
>
|
||||
> *头部标识符* → U+00BC–U+00BE,U+00C0–U+00D6,U+00D8–U+00F6,或者 U+00F8–U+00FF
|
||||
>
|
||||
> *头部标识符* → U+0100–U+02FF,U+0370–U+167F,U+1681–U+180D,或者 U+180F–U+1DBF
|
||||
>
|
||||
> *头部标识符* → U+1E00–U+1FFF
|
||||
>
|
||||
> *头部标识符* → U+200B–U+200D,U+202A–U+202E,U+203F–U+2040,U+2054,或者 U+2060–U+206F
|
||||
>
|
||||
> *头部标识符* → U+2070–U+20CF,U+2100–U+218F,U+2460–U+24FF,或者 U+2776–U+2793
|
||||
>
|
||||
> *头部标识符* → U+2C00–U+2DFF 或者 U+2E80–U+2FFF
|
||||
>
|
||||
> *头部标识符* → U+3004–U+3007,U+3021–U+302F,U+3031–U+303F,或者 U+3040–U+D7FF
|
||||
>
|
||||
> *头部标识符* → U+F900–U+FD3D,U+FD40–U+FDCF,U+FDF0–U+FE1F,或者 U+FE30–U+FE44
|
||||
>
|
||||
> *头部标识符* → U+FE47–U+FFFD
|
||||
>
|
||||
> *头部标识符* → U+10000–U+1FFFD,U+20000–U+2FFFD,U+30000–U+3FFFD,或者 U+40000–U+4FFFD
|
||||
>
|
||||
> *头部标识符* → U+50000–U+5FFFD,U+60000–U+6FFFD,U+70000–U+7FFFD,或者 U+80000–U+8FFFD
|
||||
>
|
||||
> *头部标识符* → U+90000–U+9FFFD,U+A0000–U+AFFFD,U+B0000–U+BFFFD,或者 U+C0000–U+CFFFD
|
||||
>
|
||||
> *头部标识符* → U+D0000–U+DFFFD 或者 U+E0000–U+EFFFD
|
||||
>
|
||||
#### identifier-character {#identifier-character}
|
||||
>
|
||||
> *标识符字符* → 数值 0 - 9
|
||||
>
|
||||
> *标识符字符* → U+0300–U+036F,U+1DC0–U+1DFF,U+20D0–U+20FF,或者 U+FE20–U+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.25e-2` 表示 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+00A1–U+00A7
|
||||
>
|
||||
> *头部运算符* → U+00A9 或 U+00AB
|
||||
>
|
||||
> *头部运算符* → U+00AC 或 U+00AE
|
||||
>
|
||||
> *头部运算符* → U+00B0–U+00B1
|
||||
>
|
||||
> *头部运算符* → U+00B6,U+00BB,U+00BF,U+00D7,或 U+00F7
|
||||
>
|
||||
> *头部运算符* → U+2016–U+2017
|
||||
>
|
||||
> *头部运算符* → U+2020–U+2027
|
||||
>
|
||||
> *头部运算符* → U+2030–U+203E
|
||||
>
|
||||
> *头部运算符* → U+2041–U+2053
|
||||
>
|
||||
> *头部运算符* → U+2055–U+205E
|
||||
>
|
||||
> *头部运算符* → U+2190–U+23FF
|
||||
>
|
||||
> *头部运算符* → U+2500–U+2775
|
||||
>
|
||||
> *头部运算符* → U+2794–U+2BFF
|
||||
>
|
||||
> *头部运算符* → U+2E00–U+2E7F
|
||||
>
|
||||
> *头部运算符* → U+3001–U+3003
|
||||
>
|
||||
> *头部运算符* → U+3008–U+3020
|
||||
>
|
||||
> *头部运算符* → U+3030
|
||||
>
|
||||
>
|
||||
#### operator-character {#operator-character}
|
||||
>
|
||||
> *运算符字符* → [头部运算符](#operator-head)
|
||||
>
|
||||
> *运算符字符* → U+0300–U+036F
|
||||
>
|
||||
> *运算符字符* → U+1DC0–U+1DFF
|
||||
>
|
||||
> *运算符字符* → U+20D0–U+20FF
|
||||
>
|
||||
> *运算符字符* → U+FE00–U+FE0F
|
||||
>
|
||||
> *运算符字符* → U+FE20–U+FE2F
|
||||
>
|
||||
> *运算符字符* → U+E0100–U+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)
|
||||
@ -1,468 +1,525 @@
|
||||
# 类型(Types)
|
||||
|
||||
Swift 语言存在两种类型:命名型类型和复合型类型。*命名型类型*是指定义时可以给定名字的类型。命名型类型包括类、结构体、枚举和协议。比如,一个用户定义类 `MyClass` 的实例拥有类型 `MyClass`。除了用户定义的命名型类型,Swift 标准库也定义了很多常用的命名型类型,包括那些表示数组、字典和可选值的类型。
|
||||
|
||||
那些通常被其它语言认为是基本或原始的数据型类型,比如表示数字、字符和字符串的类型,实际上就是命名型类型,这些类型在 Swift 标准库中是使用结构体来定义和实现的。因为它们是命名型类型,因此你可以按照 [扩展](../chapter2/20_Extensions.md) 和 [扩展声明](./06_Declarations.md#extension_declaration) 中讨论的那样,声明一个扩展来增加它们的行为以满足你程序的需求。
|
||||
|
||||
*复合型类型*是没有名字的类型,它由 Swift 本身定义。Swift 存在两种复合型类型:函数类型和元组类型。一个复合型类型可以包含命名型类型和其它复合型类型。例如,元组类型 `(Int, (Int, Int))` 包含两个元素:第一个是命名型类型 `Int`,第二个是另一个复合型类型 `(Int, Int)`。
|
||||
|
||||
你可以在命名型类型和复合型类型使用小括号。但是在类型旁加小括号没有任何作用。举个例子,`(Int)` 等同于 `Int`。
|
||||
|
||||
本节讨论 Swift 语言本身定义的类型,并描述 Swift 中的类型推断行为。
|
||||
|
||||
#### type {#type}
|
||||
|
||||
> 类型语法
|
||||
>
|
||||
> *类型* → [*数组类型*](#array-type)
|
||||
>
|
||||
> *类型* → [*字典类型*](#dictionary-type)
|
||||
>
|
||||
> *类型* → [*函数类型*](#function-type)
|
||||
>
|
||||
> *类型* → [*类型标识*](#type-identifier)
|
||||
>
|
||||
> *类型* → [*元组类型*](#tuple-type)
|
||||
>
|
||||
> *类型* → [*可选类型*](#optional-type)
|
||||
>
|
||||
> *类型* → [*隐式解析可选类型*](#implicitly-unwrapped-optional-type)
|
||||
>
|
||||
> *类型* → [*协议合成类型*](#protocol-composition-type)
|
||||
>
|
||||
> *类型* → [*元型类型*](#metatype-type)
|
||||
>
|
||||
> *类型* → **任意类型**
|
||||
>
|
||||
> *类型* → **自身类型**
|
||||
>
|
||||
> *类型* → [*(类型)*](#type)
|
||||
>
|
||||
|
||||
## 类型注解 {#type-annotation}
|
||||
*类型注解*显式地指定一个变量或表达式的类型。类型注解始于冒号 `:` 终于类型,比如下面两个例子:
|
||||
|
||||
```swift
|
||||
let someTuple: (Double, Double) = (3.14159, 2.71828)
|
||||
func someFunction(a: Int) { /* ... */ }
|
||||
```
|
||||
|
||||
在第一个例子中,表达式 `someTuple` 的类型被指定为 `(Double, Double)`。在第二个例子中,函数 `someFunction` 的参数 `a` 的类型被指定为 `Int`。
|
||||
|
||||
类型注解可以在类型之前包含一个类型特性的可选列表。
|
||||
|
||||
> 类型注解语法
|
||||
>
|
||||
|
||||
#### type-annotation {#type-annotation}
|
||||
> *类型注解* → **:** [*特性列表*](./07_Attributes.md#attributes)<sub>可选</sub> **输入输出参数**<sub>可选</sub> [*类型*](#type)
|
||||
>
|
||||
|
||||
## 类型标识符 {#type-identifier}
|
||||
类型标识符引用命名型类型,还可引用命名型或复合型类型的别名。
|
||||
|
||||
大多数情况下,类型标识符引用的是与之同名的命名型类型。例如类型标识符 `Int` 引用命名型类型 `Int`,同样,类型标识符 `Dictionary<String, Int>` 引用命名型类型 `Dictionary<String, Int>`。
|
||||
|
||||
在两种情况下类型标识符不引用同名的类型。情况一,类型标识符引用的是命名型或复合型类型的类型别名。比如,在下面的例子中,类型标识符使用 `Point` 来引用元组 `(Int, Int)`:
|
||||
|
||||
```swift
|
||||
typealias Point = (Int, Int)
|
||||
let origin: Point = (0, 0)
|
||||
```
|
||||
|
||||
情况二,类型标识符使用点语法(`.`)来表示在其它模块或其它类型嵌套内声明的命名型类型。例如,下面例子中的类型标识符引用在 `ExampleModule` 模块中声明的命名型类型 `MyType`:
|
||||
|
||||
```swift
|
||||
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 {#type-name}
|
||||
> *类型名称* → [*标识符*](./02_Lexical_Structure.md#identifier)
|
||||
>
|
||||
|
||||
## 元组类型 {#tuple-type}
|
||||
元组类型是使用括号括起来的零个或多个类型,类型间用逗号隔开。
|
||||
|
||||
你可以使用元组类型作为一个函数的返回类型,这样就可以使函数返回多个值。你也可以命名元组类型中的元素,然后用这些名字来引用每个元素的值。元素的名字由一个标识符紧跟一个冒号 `(:)` 组成。[函数和多返回值](../chapter2/06_Functions.md#functions_with_multiple_return_values) 章节里有一个展示上述特性的例子。
|
||||
|
||||
当一个元组类型的元素有名字的时候,这个名字就是类型的一部分。
|
||||
|
||||
```swift
|
||||
var someTuple = (top: 10, bottom: 12) // someTuple 的类型为 (top: Int, bottom: Int)
|
||||
someTuple = (top: 4, bottom: 42) // 正确:命名类型匹配
|
||||
someTuple = (9, 99) // 正确:命名类型被自动推断
|
||||
someTuple = (left: 5, right: 5) // 错误:命名类型不匹配
|
||||
```
|
||||
|
||||
所有的元组类型都包含两个及以上元素, 除了 `Void`。`Void` 是空元组类型 `()` 的别名。
|
||||
|
||||
> 元组类型语法
|
||||
>
|
||||
|
||||
#### tuple-type {#tuple-type}
|
||||
> *元组类型* → **(** **)** | **(** [*元组类型元素*](#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}
|
||||
> *元组类型元素* → [*元素名*](#element-name) [*类型注解*](#type-annotation) | [*类型*](#type)
|
||||
>
|
||||
|
||||
#### element-name {#element-name}
|
||||
> *元素名* → [*标识符*](./02_Lexical_Structure.md#identifier)
|
||||
>
|
||||
|
||||
## 函数类型 {#function-type}
|
||||
函数类型表示一个函数、方法或闭包的类型,它由参数类型和返回值类型组成,中间用箭头(`->`)隔开:
|
||||
|
||||
> (`参数类型`)->(`返回值类型`)
|
||||
|
||||
*参数类型*是由逗号间隔的类型列表。由于*返回值类型*可以是元组类型,所以函数类型支持多返回值的函数与方法。
|
||||
|
||||
你可以对参数类型为 `() -> T`(其中 T 是任何类型)的函数使用 `autoclosure` 特性。这会自动将参数表达式转化为闭包,表达式的结果即闭包返回值。这从语法结构上提供了一种便捷:延迟对表达式的求值,直到其值在函数体中被调用。以自动闭包做为参数的函数类型的例子详见 [自动闭包](../chapter2/07_Closures.md#autoclosures)。
|
||||
|
||||
函数类型可以拥有一个可变长参数作为*参数类型*中的最后一个参数。从语法角度上讲,可变长参数由一个基础类型名字紧随三个点(`...`)组成,如 `Int...`。可变长参数被认为是一个包含了基础类型元素的数组。即 `Int...` 就是 `[Int]`。关于使用可变长参数的例子,请参阅 [可变参数](../chapter2/06_Functions.md#variadic_parameters)。
|
||||
|
||||
为了指定一个 `in-out` 参数,可以在参数类型前加 `inout` 前缀。但是你不可以对可变长参数或返回值类型使用 `inout`。关于这种参数的详细讲解请参阅 [输入输出参数](../chapter2/06_Functions.md#in_out_parameters)。
|
||||
|
||||
如果一个函数类型只有一个形式参数而且形式参数的类型是元组类型,那么元组类型在写函数类型的时候必须用圆括号括起来。比如说,`((Int, Int)) -> Void` 是接收一个元组 `(Int, Int)` 作为形式参数并且不返回任何值的函数类型。与此相对,不加括号的 `(Int, Int) -> Void` 是一个接收两个 `Int` 作为形式参数并且不返回任何值的函数类型。相似地,因为 `Void` 是空元组类型 `()` 的别名,函数类型 `(Void)-> Void` 与 `(()) -> ()` 是一样的 - 一个将空元组作为唯一参数的函数。但这些类型和无变量的函数类型 `() -> ()` 是不一样的。
|
||||
|
||||
函数和方法中的变量名并不是函数类型的一部分。例如:
|
||||
|
||||
```swift
|
||||
func someFunction(left: Int, right: Int) {}
|
||||
func anotherFunction(left: Int, right: Int) {}
|
||||
func functionWithDifferentLabels(top: Int, bottom: Int) {}
|
||||
|
||||
var f = someFunction // 函数 f 的类型为 (Int, Int) -> Void, 而不是 (left: Int, right: Int) -> Void.
|
||||
|
||||
f = anotherFunction // 正确
|
||||
f = functionWithDifferentLabels // 正确
|
||||
|
||||
func functionWithDifferentArgumentTypes(left: Int, right: String) {}
|
||||
f = functionWithDifferentArgumentTypes // 错误
|
||||
|
||||
func functionWithDifferentNumberOfArguments(left: Int, right: Int, top: Int) {}
|
||||
f = functionWithDifferentNumberOfArguments // 错误
|
||||
```
|
||||
|
||||
由于变量标签不是函数类型的一部分,你可以在写函数类型的时候省略它们。
|
||||
|
||||
```swift
|
||||
var operation: (lhs: Int, rhs: Int) -> Int // 错误
|
||||
var operation: (_ lhs: Int, _ rhs: Int) -> Int // 正确
|
||||
var operation: (Int, Int) -> Int // 正确
|
||||
```
|
||||
|
||||
如果一个函数类型包涵多个箭头(->),那么函数类型将从右向左进行组合。例如,函数类型 `(Int) -> (Int) -> Int` 可以理解为 `(Int) -> ((Int) -> Int)`,也就是说,该函数类型的参数为 `Int` 类型,其返回类型是一个参数类型为 `Int`,返回类型为 `Int` 的函数。
|
||||
|
||||
函数类型若要抛出错误就必须使用 `throws` 关键字来标记,若要重抛错误则必须使用 `rethrows` 关键字来标记。`throws` 关键字是函数类型的一部分,非抛出函数是抛出函数函数的一个子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。抛出和重抛函数的相关描述见章节 [抛出函数与方法](./06_Declarations.md#throwing_functions_and_methods) 和 [重抛函数与方法](./06_Declarations.md#rethrowing_functions_and_methods)。
|
||||
|
||||
### 对非逃逸闭包的限制 {#Restrictions for Nonescaping Closures}
|
||||
当非逃逸闭包函数是参数时,不能存储在属性、变量或任何 `Any` 类型的常量中,因为这可能导致值的逃逸。
|
||||
|
||||
当非逃逸闭包函数是参数时,不能作为参数传递到另一个非逃逸闭包函数中。这样的限制可以让 Swift 在编译时就完成更多的内存访问冲突检查,而不是在运行时。举个例子:
|
||||
|
||||
```swift
|
||||
let external: (Any) -> Void = { _ in () }
|
||||
func takesTwoFunctions(first: (Any) -> Void, second: (Any) -> Void) {
|
||||
first(first) // 错误
|
||||
second(second) // 错误
|
||||
|
||||
first(second) // 错误
|
||||
second(first) // 错误
|
||||
|
||||
first(external) // 正确
|
||||
external(first) // 正确
|
||||
}
|
||||
```
|
||||
|
||||
在上面代码里,`takesTwoFunctions(first:second:)` 的两个参数都是函数。它们都没有标记为 `@escaping`, 因此它们都是非逃逸的。
|
||||
|
||||
上述例子里的被标记为“错误”的四个函数调用会产生编译错误。因为参数 `first` 和 `second` 是非逃逸函数,它们不能够作为参数被传递到另一个非闭包函数。相对的, 标记“正确”的两个函数不会产生编译错误。这些函数调用不会违反限制,因为 `external` 不是 `takesTwoFunctions(first:second:)` 的参数之一。
|
||||
|
||||
如果你需要避免这个限制,标记其中之一的参数为逃逸,或者使用 `withoutActuallyEscaping(_:do:)` 函数临时地转换非逃逸函数的其中一个参数为逃逸函数。关于避免内存访问冲突,可以参阅[内存安全](../chapter2/24_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)
|
||||
>
|
||||
|
||||
#### function-type-argument-clause {#function-type-argument-clause}
|
||||
> *函数类型子句* → **(** **)**
|
||||
> *函数类型子句* → **(** [*函数类型参数列表*](#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}
|
||||
> *函数类型参数* → [*特性列表*](./07_Attributes.md#attributes)<sub>可选</sub> **输入输出参数**<sub>可选</sub> [*类型*](#type) | [*参数标签*](#argument-label) [*类型注解*](#type-annotation)
|
||||
>
|
||||
|
||||
#### argument-label {#argument-label}
|
||||
> *参数标签* → [*标识符*](./02_Lexical_Structure.md#identifier)
|
||||
>
|
||||
|
||||
## 数组类型 {#array-type}
|
||||
Swift 语言为标准库中定义的 `Array<Element>` 类型提供了如下语法糖:
|
||||
|
||||
> [`类型`]
|
||||
>
|
||||
|
||||
换句话说,下面两个声明是等价的:
|
||||
|
||||
```swift
|
||||
let someArray: Array<String> = ["Alex", "Brian", "Dave"]
|
||||
let someArray: [String] = ["Alex", "Brian", "Dave"]
|
||||
```
|
||||
|
||||
上面两种情况下,常量 `someArray` 都被声明为字符串数组。数组的元素也可以通过下标访问:`someArray[0]` 是指第 0 个元素 `"Alex"`。
|
||||
|
||||
你也可以嵌套多对方括号来创建多维数组,最里面的方括号中指明数组元素的基本类型。比如,下面例子中使用三对方括号创建三维整数数组:
|
||||
|
||||
```swift
|
||||
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)。
|
||||
|
||||
> 数组类型语法
|
||||
>
|
||||
|
||||
#### array-type {#array-type}
|
||||
> *数组类型* → **[** [*类型*](#type) **]**
|
||||
>
|
||||
|
||||
## 字典类型 {#dictionary-type}
|
||||
Swift 语言为标准库中定义的 `Dictionary<Key, Value>` 类型提供了如下语法糖:
|
||||
|
||||
> [`键类型` : `值类型`]
|
||||
>
|
||||
|
||||
换句话说,下面两个声明是等价的:
|
||||
|
||||
```swift
|
||||
let someDictionary: [String: Int] = ["Alex": 31, "Paul": 39]
|
||||
let someDictionary: Dictionary<String, Int> = ["Alex": 31, "Paul": 39]
|
||||
```
|
||||
|
||||
上面两种情况,常量 `someDictionary` 被声明为一个字典,其中键为 `String` 类型,值为 `Int` 类型。
|
||||
|
||||
字典中的值可以通过下标来访问,这个下标在方括号中指明了具体的键:`someDictionary["Alex"]` 返回键 `Alex` 对应的值。通过下标访问会获取对应值的可选类型。如果键在字典中不存在的话,则这个下标返回 `nil`。
|
||||
|
||||
字典中键的类型必须符合 Swift 标准库中的 `Hashable` 协议。
|
||||
|
||||
关于 Swift 标准库中 `Dictionary` 类型的详细讨论,请参阅 [字典](../chapter2/04_Collection_Types.md#dictionaries)。
|
||||
|
||||
> 字典类型语法
|
||||
>
|
||||
|
||||
#### dictionary-type {#dictionary-type}
|
||||
> *字典类型* → **[** [*类型*](#type) **:** [*类型*](#type) **]**
|
||||
>
|
||||
|
||||
## 可选类型 {#optional-type}
|
||||
Swift 定义后缀 `?` 来作为标准库中定义的命名型类型 `Optional<Wrapped>` 的语法糖。换句话说,下面两个声明是等价的:
|
||||
|
||||
```swift
|
||||
var optionalInteger: Int?
|
||||
var optionalInteger: Optional<Int>
|
||||
```
|
||||
|
||||
在上述两种情况下,变量 `optionalInteger` 都被声明为可选整型类型。注意在类型和 `?` 之间没有空格。
|
||||
|
||||
类型 `Optional<Wrapped>` 是一个枚举,有两个成员,`none` 和 `some(Wrapped)`,用来表示可能有也可能没有的值。任意类型都可以被显式地声明(或隐式地转换)为可选类型。如果你在声明可选变量或属性的时候没有提供初始值,它的值则会自动赋为默认值 `nil`。
|
||||
|
||||
如果一个可选类型的实例包含一个值,那么你就可以使用后缀运算符 `!` 来获取该值,正如下面描述的:
|
||||
|
||||
```swift
|
||||
optionalInteger = 42
|
||||
optionalInteger! // 42
|
||||
```
|
||||
|
||||
使用 `!` 运算符解包值为 `nil` 的可选值会导致运行错误。
|
||||
|
||||
你也可以使用可选链式调用和可选绑定来选择性地在可选表达式上执行操作。如果值为 `nil`,不会执行任何操作,因此也就没有运行错误产生。
|
||||
|
||||
更多细节以及更多如何使用可选类型的例子,请参阅 [可选类型](../chapter2/01_The_Basics.md#optionals)。
|
||||
|
||||
> 可选类型语法
|
||||
>
|
||||
|
||||
#### optional-type {#optional-type}
|
||||
> *可选类型* → [*类型*](#type) **?**
|
||||
>
|
||||
|
||||
## 隐式解析可选类型 {#implicitly-unwrapped-optional-type}
|
||||
当可以被访问时,Swift 语言定义后缀 `!` 作为标准库中命名类型 `Optional<Wrapped>` 的语法糖,来实现自动解包的功能。如果尝试对一个值为 `nil` 的可选类型进行隐式解包,将会产生运行时错误。因为隐式解包,下面两个声明等价:
|
||||
|
||||
```swift
|
||||
var implicitlyUnwrappedString: String!
|
||||
var explicitlyUnwrappedString: Optional<String>
|
||||
```
|
||||
|
||||
注意类型与 `!` 之间没有空格。
|
||||
|
||||
由于隐式解包会更改包含该类型的声明语义,嵌套在元组类型或泛型中可选类型(比如字典元素类型或数组元素类型),不能被标记为隐式解包。例如:
|
||||
|
||||
```swift
|
||||
let tupleOfImplicitlyUnwrappedElements: (Int!, Int!) // 错误
|
||||
let implicitlyUnwrappedTuple: (Int, Int)! // 正确
|
||||
|
||||
let arrayOfImplicitlyUnwrappedElements: [Int!] // 错误
|
||||
let implicitlyUnwrappedArray: [Int]! // 正确
|
||||
```
|
||||
|
||||
由于隐式解析可选类型和可选类型有同样的类型 `Optional<Wrapped>`,你可以在所有使用可选类型的地方使用隐式解析可选类型。比如,你可以将隐式解析可选类型的值赋给变量、常量和可选属性,反之亦然。
|
||||
|
||||
正如可选类型一样,如果你在声明隐式解析可选类型的变量或属性的时候没有指定初始值,它的值则会自动赋为默认值 `nil`。
|
||||
|
||||
可以使用可选链式调用对隐式解析可选表达式选择性地执行操作。如果值为 `nil`,就不会执行任何操作,因此也不会产生运行错误。
|
||||
|
||||
关于隐式解析可选类型的更多细节,请参阅 [隐式解析可选类型](../chapter2/01_The_Basics.md#implicityly_unwrapped_optionals)。
|
||||
|
||||
> 隐式解析可选类型语法
|
||||
>
|
||||
|
||||
#### implicitly-unwrapped-optional-type {#implicitly-unwrapped-optional-type}
|
||||
> *隐式解析可选类型* → [*类型*](#type) **!**
|
||||
>
|
||||
|
||||
## 协议合成类型 {#protocol-composition-type}
|
||||
协议合成类型定义了一种遵循协议列表中每个指定协议的类型,或者一个现有类型的子类并遵循协议列表中每个指定协议。协议合成类型只能用在类型注解、泛型参数子句和泛型 `where` 子句中指定类型。
|
||||
|
||||
协议合成类型的形式如下:
|
||||
|
||||
> `Protocol 1` & `Procotol 2`
|
||||
>
|
||||
|
||||
协议合成类型允许你指定一个值,其类型遵循多个协议的要求而不需要定义一个新的命名型协议来继承它想要符合的各个协议。比如,协议合成类型 `Protocol A & Protocol B & Protocol C` 等效于一个从 `Protocol A`,`Protocol B`,`Protocol C` 继承而来的新协议。同样的,你可以使用 `SuperClass & ProtocolA` 来取代申明一个新的协议作为 `SuperClass` 的子类并遵循 `ProtocolA`。
|
||||
|
||||
协议合成列表中的每一项都必须是下面所列情况之一,列表中最多只能包含一个类:
|
||||
|
||||
- 类名
|
||||
- 协议名
|
||||
- 一个类型别名,它的潜在类型是一个协议合成类型、一个协议或者一个类
|
||||
|
||||
当协议合成类型包含类型别名时,同一个协议可能多次出现在定义中 — 重复被忽略。例如,下面代码中定义的 `PQR` 等同于 `P & Q & R`。
|
||||
|
||||
```swift
|
||||
typealias PQ = P & Q
|
||||
typealias PQR = PQ & Q & R
|
||||
```
|
||||
|
||||
> 协议合成类型语法
|
||||
>
|
||||
|
||||
#### protocol-composition-type {#protocol-composition-type}
|
||||
> *协议合成类型* → [*协议标识符*](#protocol-identifier) & [*协议合成延续*](#protocol-composition-continuation)
|
||||
>
|
||||
|
||||
#### protocol-composition-continuation {#protocol-composition-continuation}
|
||||
> *协议合成延续* → [*协议标识符*](#protocol-identifier) | [*协议合成类型*](#protocol-composition-type)
|
||||
>
|
||||
|
||||
## 元类型 {#metatype-type}
|
||||
元类型是指任意类型的类型,包括类类型、结构体类型、枚举类型和协议类型。
|
||||
|
||||
类、结构体或枚举类型的元类型是相应的类型名紧跟 `.Type`。协议类型的元类型——并不是运行时遵循该协议的具体类型——是该协议名字紧跟 `.Protocol`。比如,类 `SomeClass` 的元类型就是 `SomeClass.Type`,协议 `SomeProtocol` 的元类型就是 `SomeProtocal.Protocol`。
|
||||
|
||||
你可以使用后缀 `self` 表达式来获取类型。比如,`SomeClass.self` 返回 `SomeClass` 本身,而不是 `SomeClass` 的一个实例。同样,`SomeProtocol.self` 返回 `SomeProtocol` 本身,而不是运行时遵循 `SomeProtocol` 的某个类型的实例。还可以对类型的实例使用 `type(of:)` 表达式来获取该实例动态的、在运行阶段的类型,如下所示:
|
||||
|
||||
```swift
|
||||
class SomeBaseClass {
|
||||
class func printClassName() {
|
||||
println("SomeBaseClass")
|
||||
}
|
||||
}
|
||||
class SomeSubClass: SomeBaseClass {
|
||||
override class func printClassName() {
|
||||
println("SomeSubClass")
|
||||
}
|
||||
}
|
||||
let someInstance: SomeBaseClass = SomeSubClass()
|
||||
// someInstance 在编译期是 SomeBaseClass 类型,
|
||||
// 但是在运行期则是 SomeSubClass 类型
|
||||
type(of: someInstance).printClassName()
|
||||
// 打印“SomeSubClass”
|
||||
```
|
||||
|
||||
更多信息可以查看 Swift 标准库里的 [type(of:)](https://developer.apple.com/documentation/swift/2885064-type)。
|
||||
|
||||
可以使用初始化表达式从某个类型的元类型构造出一个该类型的实例。对于类实例,被调用的构造器必须使用 `required` 关键字标记,或者整个类使用 `final` 关键字标记。
|
||||
|
||||
```swift
|
||||
class AnotherSubClass: SomeBaseClass {
|
||||
let string: String
|
||||
required init(string: String) {
|
||||
self.string = string
|
||||
}
|
||||
override class func printClassName() {
|
||||
print("AnotherSubClass")
|
||||
}
|
||||
}
|
||||
let metatype: AnotherSubClass.Type = AnotherSubClass.self
|
||||
let anotherInstance = metatype.init(string: "some string")
|
||||
```
|
||||
|
||||
> 元类型语法
|
||||
>
|
||||
|
||||
#### metatype-type {#metatype-type}
|
||||
> *元类型* → [*类型*](#type) **.** **Type** | [*类型*](#type) **.** **Protocol**
|
||||
>
|
||||
|
||||
## 类型继承子句 {#type-inheritance-clause}
|
||||
类型继承子句被用来指定一个命名型类型继承自哪个类、采纳哪些协议。类型继承子句开始于冒号 `:`,其后是类型标识符列表。
|
||||
|
||||
类可以继承自单个超类,并遵循任意数量的协议。当定义一个类时,超类的名字必须出现在类型标识符列表首位,然后跟上该类需要遵循的任意数量的协议。如果一个类不是从其它类继承而来,那么列表可以以协议开头。关于类继承更多的讨论和例子,请参阅 [继承](../chapter2/13_Inheritance.md)。
|
||||
|
||||
其它命名型类型只能继承自或采纳一系列协议。协议类型可以继承自任意数量的其他协议。当一个协议类型继承自其它协议时,其它协议中定义的要求会被整合在一起,然后从当前协议继承的任意类型必须符合所有这些条件。
|
||||
|
||||
枚举定义中的类型继承子句可以是一系列协议,或者是指定单一的命名类型,此时枚举为其用例分配原始值。在枚举定义中使用类型继承子句来指定原始值类型的例子,请参阅 [原始值](../chapter2/08_Enumerations.md#raw_values)。
|
||||
|
||||
> 类型继承子句语法
|
||||
>
|
||||
|
||||
#### type_inheritance_clause {#type-inheritance-clause}
|
||||
> *类型继承子句* → **:** [*类型继承列表*](#type-inheritance-list)
|
||||
>
|
||||
|
||||
#### type-inheritance-list {#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>`。
|
||||
|
||||
在上面的两个例子中,类型信息从表达式树的叶子节点传向根节点。也就是说,`var x: Int = 0` 中 `x` 的类型首先根据 `0` 的类型进行推断,然后将该类型信息传递到根节点(变量 `x`)。
|
||||
|
||||
在 Swift 中,类型信息也可以反方向流动——从根节点传向叶子节点。在下面的例子中,常量 `eFloat` 上的显式类型注解(`: Float`)将导致数字字面量 `2.71828` 的类型是 `Float` 而非 `Double`。
|
||||
|
||||
```swift
|
||||
let e = 2.71828 // e 的类型会被推断为 Double
|
||||
let eFloat: Float = 2.71828 // eFloat 的类型为 Float
|
||||
```
|
||||
|
||||
Swift 中的类型推断在单独的表达式或语句上进行。这意味着所有用于类型推断的信息必须可以从表达式或其某个子表达式的类型检查中获取到。
|
||||
# 类型(Types)
|
||||
|
||||
Swift 语言存在两种类型:命名型类型和复合型类型。*命名型类型*是指定义时可以给定名字的类型。命名型类型包括类、结构体、枚举和协议。比如,一个用户定义类 `MyClass` 的实例拥有类型 `MyClass`。除了用户定义的命名型类型,Swift 标准库也定义了很多常用的命名型类型,包括那些表示数组、字典和可选值的类型。
|
||||
|
||||
那些通常被其它语言认为是基本或原始的数据型类型,比如表示数字、字符和字符串的类型,实际上就是命名型类型,这些类型在 Swift 标准库中是使用结构体来定义和实现的。因为它们是命名型类型,因此你可以按照 [扩展](../02_language_guide/20_Extensions.md) 和 [扩展声明](./06_Declarations.md#extension-declaration) 中讨论的那样,声明一个扩展来增加它们的行为以满足你程序的需求。
|
||||
|
||||
*复合型类型*是没有名字的类型,它由 Swift 本身定义。Swift 存在两种复合型类型:函数类型和元组类型。一个复合型类型可以包含命名型类型和其它复合型类型。例如,元组类型 `(Int, (Int, Int))` 包含两个元素:第一个是命名型类型 `Int`,第二个是另一个复合型类型 `(Int, Int)`。
|
||||
|
||||
你可以在命名型类型和复合型类型使用小括号。但是在类型旁加小括号没有任何作用。举个例子,`(Int)` 等同于 `Int`。
|
||||
|
||||
本节讨论 Swift 语言本身定义的类型,并描述 Swift 中的类型推断行为。
|
||||
|
||||
#### type {#type}
|
||||
|
||||
> 类型语法
|
||||
>
|
||||
> *类型* → [函数类型](#function-type)
|
||||
>
|
||||
> *类型* → [数组类型](#array-type)
|
||||
>
|
||||
> *类型* → [字典类型](#dictionary-type)
|
||||
>
|
||||
> *类型* → [类型标识](#type-identifier)
|
||||
>
|
||||
> *类型* → [元组类型](#tuple-type)
|
||||
>
|
||||
> *类型* → [可选类型](#optional-type)
|
||||
>
|
||||
> *类型* → [隐式解析可选类型](#implicitly-unwrapped-optional-type)
|
||||
>
|
||||
> *类型* → [协议合成类型](#protocol-composition-type)
|
||||
>
|
||||
> *类型* →[不透明类型](#opaque-type)
|
||||
>
|
||||
> *类型* → [元型类型](#metatype-type)
|
||||
>
|
||||
> *类型* → [自身类型](#self-type)
|
||||
>
|
||||
> *类型* → **Any**
|
||||
>
|
||||
> *类型* → **(** [类型](#type) **)**
|
||||
|
||||
## 类型注解 {#type-annotation-h}
|
||||
*类型注解*显式地指定一个变量或表达式的类型。类型注解从冒号 (`:`)开始, 以类型结尾,比如下面两个例子:
|
||||
|
||||
```swift
|
||||
let someTuple: (Double, Double) = (3.14159, 2.71828)
|
||||
func someFunction(a: Int) { /* ... */ }
|
||||
```
|
||||
|
||||
在第一个例子中,表达式 `someTuple` 的类型被指定为 `(Double, Double)`。在第二个例子中,函数 `someFunction` 的形参 `a` 的类型被指定为 `Int`。
|
||||
|
||||
类型注解可以在类型之前包含一个类型特性的可选列表。
|
||||
|
||||
> 类型注解语法
|
||||
>
|
||||
|
||||
#### type-annotation {#type-annotation}
|
||||
> *类型注解* → **:** [特性列表](./07_Attributes.md#attributes)<sub>可选</sub> **输入输出参数**<sub>可选</sub> [类型](#type)
|
||||
|
||||
## 类型标识符 {#type-identifier-h}
|
||||
*类型标识符*可以引用命名型类型,还可引用命名型或复合型类型的别名。
|
||||
|
||||
大多数情况下,类型标识符引用的是与之同名的命名型类型。例如类型标识符 `Int` 引用命名型类型 `Int`,同样,类型标识符 `Dictionary<String, Int>` 引用命名型类型 `Dictionary<String, Int>`。
|
||||
|
||||
在两种情况下类型标识符不引用同名的类型。情况一,类型标识符引用的是命名型或复合型类型的类型别名。比如,在下面的例子中,类型标识符使用 `Point` 来引用元组 `(Int, Int)`:
|
||||
|
||||
```swift
|
||||
typealias Point = (Int, Int)
|
||||
let origin: Point = (0, 0)
|
||||
```
|
||||
|
||||
情况二,类型标识符使用点语法(`.`)来表示在其它模块或其它类型嵌套内声明的命名型类型。例如,下面例子中的类型标识符引用在 `ExampleModule` 模块中声明的命名型类型 `MyType`:
|
||||
|
||||
```swift
|
||||
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 {#type-name}
|
||||
> *类型名称* → [标识符](./02_Lexical_Structure.md#identifier)
|
||||
|
||||
## 元组类型 {#tuple-type-h}
|
||||
*元组类型*是使用括号括起来的零个或多个类型,类型间用逗号隔开。
|
||||
|
||||
你可以使用元组类型作为一个函数的返回类型,这样就可以使函数返回多个值。你也可以命名元组类型中的元素,然后用这些名字来引用每个元素的值。元素的名字由一个标识符紧跟一个冒号 `(:)` 组成。[函数和多返回值](../02_language_guide/06_Functions.md#functions-with-multiple-return-values) 章节里有一个展示上述特性的例子。
|
||||
|
||||
当一个元组类型的元素有名字的时候,这个名字就是类型的一部分。
|
||||
|
||||
```swift
|
||||
var someTuple = (top: 10, bottom: 12) // someTuple 的类型为 (top: Int, bottom: Int)
|
||||
someTuple = (top: 4, bottom: 42) // 正确:命名类型匹配
|
||||
someTuple = (9, 99) // 正确:命名类型被自动推断
|
||||
someTuple = (left: 5, right: 5) // 错误:命名类型不匹配
|
||||
```
|
||||
|
||||
所有的元组类型都包含两个及以上元素, 除了 `Void`。`Void` 是空元组类型 `()` 的别名。
|
||||
|
||||
> 元组类型语法
|
||||
>
|
||||
|
||||
#### tuple-type {#tuple-type}
|
||||
> *元组类型* → **(** **)** | **(** [元组类型元素](#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}
|
||||
> *元组类型元素* → [元素名](#element-name) [类型注解](#type-annotation) | [类型](#type)
|
||||
>
|
||||
|
||||
#### element-name {#element-name}
|
||||
> *元素名* → [标识符](./02_Lexical_Structure.md#identifier)
|
||||
>
|
||||
|
||||
## 函数类型 {#function-type-h}
|
||||
*函数类型*表示一个函数、方法或闭包的类型,它由形参类型和返回值类型组成,中间用箭头(`->`)隔开:
|
||||
|
||||
> (`形参类型`)->(`返回值类型`)
|
||||
|
||||
*形参类型*是由逗号间隔的类型列表。由于*返回值类型*可以是元组类型,所以函数类型支持多返回值的函数与方法。
|
||||
|
||||
你可以对形参类型为 `() -> T`(其中 T 是任何类型)的函数使用 `autoclosure` 特性,这会在调用侧隐式创建一个闭包。这从语法结构上提供了一种便捷:延迟对表达式的求值,直到其值在函数体中被调用。以自动闭包做为形参的函数类型的例子详见 [自动闭包](../02_language_guide/07_Closures.md#autoclosures)。
|
||||
|
||||
函数类型可以拥有一个可变参数在*形参类型*中。从语法角度上讲,可变参数由一个基础类型名字紧随三个点(`...`)组成,如 `Int...`。可变参数被认为是一个包含了基础类型元素的数组。即 `Int...` 就是 `[Int]`。关于使用可变参数的例子,请参阅 [可变参数](../02_language_guide/06_Functions.md#variadic-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` 与 `(()) -> ()` 是一样的 - 一个将空元组作为唯一实参的函数。但这些类型和 `() -> ()` 是不一样的 - 一个无实参的函数。
|
||||
|
||||
函数和方法中的实参名并不是函数类型的一部分。例如:
|
||||
|
||||
```swift
|
||||
func someFunction(left: Int, right: Int) {}
|
||||
func anotherFunction(left: Int, right: Int) {}
|
||||
func functionWithDifferentLabels(top: Int, bottom: Int) {}
|
||||
|
||||
var f = someFunction // 函数 f 的类型为 (Int, Int) -> Void, 而不是 (left: Int, right: Int) -> Void.
|
||||
|
||||
f = anotherFunction // 正确
|
||||
f = functionWithDifferentLabels // 正确
|
||||
|
||||
func functionWithDifferentArgumentTypes(left: Int, right: String) {}
|
||||
f = functionWithDifferentArgumentTypes // 错误
|
||||
|
||||
func functionWithDifferentNumberOfArguments(left: Int, right: Int, top: Int) {}
|
||||
f = functionWithDifferentNumberOfArguments // 错误
|
||||
```
|
||||
|
||||
由于实参标签不是函数类型的一部分,你可以在写函数类型的时候省略它们。
|
||||
|
||||
```swift
|
||||
var operation: (lhs: Int, rhs: Int) -> Int // 错误
|
||||
var operation: (_ lhs: Int, _ rhs: Int) -> Int // 正确
|
||||
var operation: (Int, Int) -> Int // 正确
|
||||
```
|
||||
|
||||
如果一个函数类型包涵多个箭头(->),那么函数类型将从右向左进行组合。例如,函数类型 `(Int) -> (Int) -> Int` 可以理解为 `(Int) -> ((Int) -> Int)`,也就是说,该函数传入 `Int`,并返回另一个传入并返回 `Int` 的函数。
|
||||
|
||||
函数类型若要抛出或重抛错误就必须使用 `throws` 关键字来标记。`throws` 关键字是函数类型的一部分,非抛出函数是抛出函数的子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。抛出和重抛函数的相关描述见章节 [抛出函数与方法](./06_Declarations.md#throwing-functions-and-methods) 和 [重抛函数与方法](./06_Declarations.md#rethrowing-functions-and-methods)。
|
||||
|
||||
### 对非逃逸闭包的限制 {#Restrictions for Nonescaping Closures}
|
||||
当非逃逸闭包函数是形参时,不能存储在属性、变量或任何 `Any` 类型的常量中,因为这可能导致值的逃逸。
|
||||
|
||||
当非逃逸闭包函数是形参时,不能作为实参传递到另一个非逃逸闭包函数中。这样的限制可以让 Swift 在编译时就完成更好的内存访问冲突检查,而不是在运行时。举个例子:
|
||||
|
||||
```swift
|
||||
let external: (Any) -> Void = { _ in () }
|
||||
func takesTwoFunctions(first: (Any) -> Void, second: (Any) -> Void) {
|
||||
first(first) // 错误
|
||||
second(second) // 错误
|
||||
|
||||
first(second) // 错误
|
||||
second(first) // 错误
|
||||
|
||||
first(external) // 正确
|
||||
external(first) // 正确
|
||||
}
|
||||
```
|
||||
|
||||
在上面代码里,`takesTwoFunctions(first:second:)` 的两个形参都是函数。它们都没有标记为 `@escaping`, 因此它们都是非逃逸的。
|
||||
|
||||
上述例子里的被标记为“错误”的四个函数调用会产生编译错误。因为形参 `first` 和 `second` 是非逃逸函数,它们不能够作为实参被传递到另一个非闭包函数。相对的, 标记“正确”的两个函数不会产生编译错误。这些函数调用不会违反限制,因为 `external` 不是 `takesTwoFunctions(first:second:)` 的形参之一。
|
||||
|
||||
如果你需要避免这个限制,标记其中一个形参为逃逸,或者使用 `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)
|
||||
|
||||
#### function-type-argument-clause {#function-type-argument-clause}
|
||||
> *函数类型子句* → **(** **)**
|
||||
> *函数类型子句* → **(** [函数类型实参列表](#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}
|
||||
|
||||
> *函数类型实参* → [特性列表](./07_Attributes.md#attributes)<sub>可选</sub> **输入输出参数**<sub>可选</sub> [类型](#type) | [实参标签](#argument-label) [类型注解](#type-annotation)
|
||||
|
||||
#### argument-label {#argument-label}
|
||||
> *形参标签* → [标识符](./02_Lexical_Structure.md#identifier)
|
||||
|
||||
## 数组类型 {#array-type-h}
|
||||
Swift 语言为标准库中定义的 `Array<Element>` 类型提供了如下语法糖:
|
||||
|
||||
> [`类型`]
|
||||
>
|
||||
|
||||
换句话说,下面两个声明是等价的:
|
||||
|
||||
```swift
|
||||
let someArray: Array<String> = ["Alex", "Brian", "Dave"]
|
||||
let someArray: [String] = ["Alex", "Brian", "Dave"]
|
||||
```
|
||||
|
||||
上面两种情况下,常量 `someArray` 都被声明为字符串数组。数组的元素也可以通过下标访问:`someArray[0]` 是指第 0 个元素 `"Alex"`。
|
||||
|
||||
你也可以嵌套多对方括号来创建多维数组,最里面的方括号中指明数组元素的基本类型。比如,下面例子中使用三对方括号创建三维整数数组:
|
||||
|
||||
```swift
|
||||
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` 类型的详细讨论,请参阅 [数组](../02_language_guide/04_Collection_Types.md#arrays)。
|
||||
|
||||
> 数组类型语法
|
||||
>
|
||||
|
||||
#### array-type {#array-type}
|
||||
> *数组类型* → **[** [类型](#type) **]**
|
||||
>
|
||||
|
||||
## 字典类型 {#dictionary-type-h}
|
||||
Swift 语言为标准库中定义的 `Dictionary<Key, Value>` 类型提供了如下语法糖:
|
||||
|
||||
> [`键类型` : `值类型`]
|
||||
>
|
||||
|
||||
换句话说,下面两个声明是等价的:
|
||||
|
||||
```swift
|
||||
let someDictionary: [String: Int] = ["Alex": 31, "Paul": 39]
|
||||
let someDictionary: Dictionary<String, Int> = ["Alex": 31, "Paul": 39]
|
||||
```
|
||||
|
||||
上面两种情况,常量 `someDictionary` 被声明为一个字典,其中键为 `String` 类型,值为 `Int` 类型。
|
||||
|
||||
字典中的值可以通过下标来访问,这个下标在方括号中指明了具体的键:`someDictionary["Alex"]` 返回键 `Alex` 对应的值。通过下标访问会获取对应值的可选类型。如果键在字典中不存在的话,则这个下标返回 `nil`。
|
||||
|
||||
字典中键的类型必须符合 Swift 标准库中的 `Hashable` 协议。
|
||||
|
||||
关于 Swift 标准库中 `Dictionary` 类型的详细讨论,请参阅 [字典](../02_language_guide/04_Collection_Types.md#dictionaries)。
|
||||
|
||||
> 字典类型语法
|
||||
>
|
||||
|
||||
#### dictionary-type {#dictionary-type}
|
||||
> *字典类型* → **[** [类型](#type) **:** [类型](#type) **]**
|
||||
>
|
||||
|
||||
## 可选类型 {#optional-type-h}
|
||||
Swift 定义后缀 `?` 来作为标准库中定义的命名型类型 `Optional<Wrapped>` 的语法糖。换句话说,下面两个声明是等价的:
|
||||
|
||||
```swift
|
||||
var optionalInteger: Int?
|
||||
var optionalInteger: Optional<Int>
|
||||
```
|
||||
|
||||
在上述两种情况下,变量 `optionalInteger` 都被声明为可选整型类型。注意在类型和 `?` 之间没有空格。
|
||||
|
||||
类型 `Optional<Wrapped>` 是一个枚举,有两个成员,`none` 和 `some(Wrapped)`,用来表示可能有也可能没有的值。任意类型都可以被显式地声明(或隐式地转换)为可选类型。如果你在声明可选变量或属性的时候没有提供初始值,它的值则会自动赋为默认值 `nil`。
|
||||
|
||||
如果一个可选类型的实例包含一个值,那么你就可以使用后缀运算符 `!` 来获取该值,正如下面描述的:
|
||||
|
||||
```swift
|
||||
optionalInteger = 42
|
||||
optionalInteger! // 42
|
||||
```
|
||||
|
||||
使用 `!` 运算符解包值为 `nil` 的可选值会导致运行错误。
|
||||
|
||||
你也可以使用可选链式调用和可选绑定来选择性在可选表达式上执行操作。如果值为 `nil`,不会执行任何操作,因此也就没有运行错误产生。
|
||||
|
||||
更多细节以及更多如何使用可选类型的例子,请参阅 [可选类型](../02_language_guide/01_The_Basics.md#optionals)。
|
||||
|
||||
> 可选类型语法
|
||||
>
|
||||
|
||||
#### optional-type {#optional-type}
|
||||
> *可选类型* → [类型](#type) **?**
|
||||
>
|
||||
|
||||
## 隐式解析可选类型 {#implicitly-unwrapped-optional-type-h}
|
||||
当可以被访问时,Swift 语言定义后缀 `!` 作为标准库中命名类型 `Optional<Wrapped>` 的语法糖,来实现自动解包的功能。如果尝试对一个值为 `nil` 的可选类型进行隐式解包,将会产生运行时错误。因为隐式解包,下面两个声明等价:
|
||||
|
||||
```swift
|
||||
var implicitlyUnwrappedString: String!
|
||||
var explicitlyUnwrappedString: Optional<String>
|
||||
```
|
||||
|
||||
注意类型与 `!` 之间没有空格。
|
||||
|
||||
由于隐式解包会更改包含该类型的声明语义,嵌套在元组类型或泛型中可选类型(比如字典元素类型或数组元素类型),不能被标记为隐式解包。例如:
|
||||
|
||||
```swift
|
||||
let tupleOfImplicitlyUnwrappedElements: (Int!, Int!) // 错误
|
||||
let implicitlyUnwrappedTuple: (Int, Int)! // 正确
|
||||
|
||||
let arrayOfImplicitlyUnwrappedElements: [Int!] // 错误
|
||||
let implicitlyUnwrappedArray: [Int]! // 正确
|
||||
```
|
||||
|
||||
由于隐式解析可选类型和可选类型有同样的类型 `Optional<Wrapped>`,你可以在所有使用可选类型的地方使用隐式解析可选类型。比如,你可以将隐式解析可选类型的值赋给变量、常量和可选属性,反之亦然。
|
||||
|
||||
正如可选类型一样,如果你在声明隐式解析可选类型的变量或属性的时候没有指定初始值,它的值则会自动赋为默认值 `nil`。
|
||||
|
||||
可以使用可选链式调用对隐式解析可选表达式选择性地执行操作。如果值为 `nil`,就不会执行任何操作,因此也不会产生运行错误。
|
||||
|
||||
关于隐式解析可选类型的更多细节,请参阅 [隐式解析可选类型](../02_language_guide/01_The_Basics.md#implicityly-unwrapped-optionals)。
|
||||
|
||||
> 隐式解析可选类型语法
|
||||
>
|
||||
|
||||
#### implicitly-unwrapped-optional-type {#implicitly-unwrapped-optional-type}
|
||||
> *隐式解析可选类型* → [类型](#type) **!**
|
||||
>
|
||||
|
||||
## 协议合成类型 {#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`。
|
||||
|
||||
协议合成列表中的每一项都必须是下面所列情况之一,列表中最多只能包含一个类:
|
||||
|
||||
- 类名
|
||||
- 协议名
|
||||
- 一个类型别名,它的潜在类型是一个协议合成类型、一个协议或者一个类
|
||||
|
||||
当协议合成类型包含类型别名时,同一个协议可能多次出现在定义中 — 重复被忽略。例如,下面代码中定义的 `PQR` 等同于 `P & Q & R`。
|
||||
|
||||
```swift
|
||||
typealias PQ = P & Q
|
||||
typealias PQR = PQ & Q & R
|
||||
```
|
||||
|
||||
> 协议合成类型语法
|
||||
>
|
||||
|
||||
#### protocol-composition-type {#protocol-composition-type}
|
||||
> *协议合成类型* → [协议标识符](#protocol-identifier) & [协议合成延续](#protocol-composition-continuation)
|
||||
>
|
||||
|
||||
#### protocol-composition-continuation {#protocol-composition-continuation}
|
||||
> *协议合成延续* → [协议标识符](#protocol-identifier) | [协议合成类型](#protocol-composition-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`。
|
||||
|
||||
你可以使用后缀 `self` 表达式来获取类型。比如,`SomeClass.self` 返回 `SomeClass` 本身,而不是 `SomeClass` 的一个实例。同样,`SomeProtocol.self` 返回 `SomeProtocol` 本身,而不是运行时遵循 `SomeProtocol` 的某个类型的实例。还可以对类型的实例使用 `type(of:)` 表达式来获取该实例动态的、在运行阶段的类型,如下所示:
|
||||
|
||||
```swift
|
||||
class SomeBaseClass {
|
||||
class func printClassName() {
|
||||
println("SomeBaseClass")
|
||||
}
|
||||
}
|
||||
class SomeSubClass: SomeBaseClass {
|
||||
override class func printClassName() {
|
||||
println("SomeSubClass")
|
||||
}
|
||||
}
|
||||
let someInstance: SomeBaseClass = SomeSubClass()
|
||||
// someInstance 在编译期是 SomeBaseClass 类型,
|
||||
// 但是在运行期则是 SomeSubClass 类型
|
||||
type(of: someInstance).printClassName()
|
||||
// 打印“SomeSubClass”
|
||||
```
|
||||
|
||||
更多信息可以查看 Swift 标准库里的 [type(of:)](https://developer.apple.com/documentation/swift/2885064-type)。
|
||||
|
||||
可以使用初始化表达式从某个类型的元类型构造出一个该类型的实例。对于类实例,被调用的构造器必须使用 `required` 关键字标记,或者整个类使用 `final` 关键字标记。
|
||||
|
||||
```swift
|
||||
class AnotherSubClass: SomeBaseClass {
|
||||
let string: String
|
||||
required init(string: String) {
|
||||
self.string = string
|
||||
}
|
||||
override class func printClassName() {
|
||||
print("AnotherSubClass")
|
||||
}
|
||||
}
|
||||
let metatype: AnotherSubClass.Type = AnotherSubClass.self
|
||||
let anotherInstance = metatype.init(string: "some string")
|
||||
```
|
||||
|
||||
> 元类型语法
|
||||
>
|
||||
|
||||
#### metatype-type {#metatype-type}
|
||||
> *元类型* → [类型](#type) **.** **Type** | [类型](#type) **.** **Protocol**
|
||||
|
||||
## 自身类型 {#self-type-h}
|
||||
|
||||
`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)。
|
||||
|
||||
其它命名型类型只能继承自或采纳一系列协议。协议类型可以继承自任意数量的其他协议。当一个协议类型继承自其它协议时,其它协议中定义的要求会被整合在一起,然后从当前协议继承的任意类型必须符合所有这些条件。
|
||||
|
||||
枚举定义中的类型继承子句可以是一系列协议,或者是指定单一的命名类型,此时枚举为其用例分配原始值。在枚举定义中使用类型继承子句来指定原始值类型的例子,请参阅 [原始值](../02_language_guide/08_Enumerations.md#raw-values)。
|
||||
|
||||
> 类型继承子句语法
|
||||
>
|
||||
|
||||
#### type-inheritance-clause {#type-inheritance-clause}
|
||||
> *类型继承子句* → **:** [类型继承列表](#type-inheritance-list)
|
||||
>
|
||||
|
||||
#### type-inheritance-list {#type-inheritance-list}
|
||||
> *类型继承列表* → [类型标识符](#type-identifier) | [类型标识符](#type-identifier) **,** [类型继承列表](#type-inheritance-list)
|
||||
|
||||
## 类型推断
|
||||
|
||||
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`)。
|
||||
|
||||
在 Swift 中,类型信息也可以反方向流动——从根节点传向叶子节点。在下面的例子中,常量 `eFloat` 上的显式类型注解(`: Float`)将导致数字字面量 `2.71828` 的类型是 `Float` 而非 `Double`。
|
||||
|
||||
```swift
|
||||
let e = 2.71828 // e 的类型会被推断为 Double
|
||||
let eFloat: Float = 2.71828 // eFloat 的类型为 Float
|
||||
```
|
||||
|
||||
Swift 中的类型推断在单独的表达式或语句上进行。这意味着所有用于类型推断的信息必须可以从表达式或其某个子表达式的类型检查中获取到。
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
505
source/03_language_reference/07_Attributes.md
Executable file
505
source/03_language_reference/07_Attributes.md
Executable 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> **}**
|
||||
>
|
||||
> *均衡令牌* → 任意标识符,关键字,字面量或运算符
|
||||
>
|
||||
> *均衡令牌* → 任意标点除了 **(**,**)**,**[**,**]**,**{**,或 **}**
|
||||
>
|
||||
@ -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)
|
||||
>
|
||||
@ -1,138 +1,148 @@
|
||||
# 泛型参数(Generic Parameters and Arguments)
|
||||
|
||||
本节涉及泛型类型、泛型函数以及泛型构造器的参数,包括形参和实参。声明泛型类型、函数或构造器时,须指定相应的类型参数。类型参数相当于一个占位符,当实例化泛型类型、调用泛型函数或泛型构造器时,就用具体的类型实参替代之。
|
||||
|
||||
关于 Swift 语言的泛型概述,请参阅 [泛型](../chapter2/22_Generics.md)。
|
||||
|
||||
## 泛型形参子句 {#generic-parameter}
|
||||
*泛型形参子句*指定泛型类型或函数的类型形参,以及这些参数相关的约束和要求。泛型形参子句用尖括号(`<>`)包住,形式如下:
|
||||
|
||||
> <`泛型形参列表`>
|
||||
>
|
||||
|
||||
泛型形参列表中泛型形参用逗号分开,其中每一个采用以下形式:
|
||||
|
||||
> `类型形参` : `约束`
|
||||
>
|
||||
|
||||
泛型形参由两部分组成:类型形参及其后的可选约束。类型形参只是占位符类型(如 `T`,`U`,`V`,`Key`,`Value` 等)的名字而已。你可以在泛型类型、函数的其余部分或者构造器声明,包括函数或构造器的签名中使用它(以及它的关联类型)。
|
||||
|
||||
约束用于指明该类型形参继承自某个类或者符合某个协议或协议组合。例如,在下面的泛型函数中,泛型形参 `T: Comparable` 表示任何用于替代类型形参 `T` 的类型实参必须满足 `Comparable` 协议。
|
||||
|
||||
```swift
|
||||
func simpleMax<T: Comparable>(_ x: T, _ y: T) -> T {
|
||||
>
|
||||
if x < y {
|
||||
return y
|
||||
}
|
||||
return x
|
||||
}
|
||||
```
|
||||
|
||||
例如,因为 `Int` 和 `Double` 均满足 `Comparable` 协议,所以该函数可以接受这两种类型。与泛型类型相反,调用泛型函数或构造器时不需要指定泛型实参子句。类型实参由传递给函数或构造器的实参推断而出。
|
||||
|
||||
```swift
|
||||
simpleMax(17, 42) // T 被推断为 Int 类型
|
||||
simpleMax(3.14159, 2.71828) // T 被推断为 Double 类型
|
||||
```
|
||||
|
||||
### Where 子句 {#where-clauses}
|
||||
要想对类型形参及其关联类型指定额外要求,可以在函数体或者类型的大括号之前添加 `where` 子句。`where` 子句由关键字 `where` 及其后的用逗号分隔的一个或多个要求组成。
|
||||
|
||||
> `where` : `类型要求`
|
||||
>
|
||||
|
||||
`where` 子句中的要求用于指明该类型形参继承自某个类或符合某个协议或协议组合。尽管 `where` 子句提供了语法糖使其有助于表达类型形参上的简单约束(如 `<T: Comparable>` 等同于 `<T> where T: Comparable`,等等),但是依然可以用来对类型形参及其关联类型提供更复杂的约束,例如你可以强制形参的关联类型遵守协议,如,`<S: Sequence> where S.Iterator.Element: Equatable` 表示泛型类型 `S` 遵守 `Sequence` 协议并且关联类型 `S.Iterator.Element` 遵守 `Equatable` 协议,这个约束确保队列的每一个元素都是符合 `Equatable` 协议的。
|
||||
>
|
||||
|
||||
也可以用操作符 `==` 来指定两个类型必须相同。例如,泛型形参子句 `<S1: Sequence, S2: Sequence> where S1.Iterator.Element == S2.Iterator.Element` 表示 `S1` 和 `S2` 必须都符合 `SequenceType` 协议,而且两个序列中的元素类型必须相同。
|
||||
>
|
||||
|
||||
当然,替代类型形参的类型实参必须满足所有的约束和要求。
|
||||
|
||||
泛型函数或构造器可以重载,但在泛型形参子句中的类型形参必须有不同的约束或要求,抑或二者皆不同。当调用重载的泛型函数或构造器时,编译器会根据这些约束来决定调用哪个重载函数或构造器。
|
||||
|
||||
更多关于泛型 where 从句的信息和关于泛型函数声明的例子,可以看一看 [泛型 where 子句](../chapter2/22_Generics.md#where_clauses)
|
||||
|
||||
> 泛型形参子句语法
|
||||
>
|
||||
|
||||
#### generic-parameter-clause {#generic-parameter-clause}
|
||||
> *泛型形参子句* → **<** [*泛型形参列表*](#generic-parameter-list) [*约束子句*](#requirement-clause)<sub>可选</sub> **>**
|
||||
>
|
||||
|
||||
#### generic-parameter-list {#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-identifier)
|
||||
>
|
||||
> *泛形形参* → [*类型名称*](./03_Types.md#type-name) **:** [*协议合成类型*](./03_Types.md#protocol-composition-type)
|
||||
>
|
||||
>
|
||||
#### requirement-clause {#requirement-clause}
|
||||
>
|
||||
> *约束子句* → **where** [*约束列表*](#requirement-list)
|
||||
>
|
||||
|
||||
#### requirement-list {#requirement-list}
|
||||
> *约束列表* → [*约束*](#requirement) | [*约束*](#requirement) **,** [*约束列表*](#requirement-list)
|
||||
>
|
||||
|
||||
#### requirement {#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#protocol-composition-type)
|
||||
>
|
||||
|
||||
#### same-type-requirement {#same-type-requirement}
|
||||
> *同类型约束* → [*类型标识符*](./03_Types.md#type-identifier) **==** [*类型*](./03_Types.md#type)
|
||||
>
|
||||
|
||||
## 泛型实参子句 {#generic-argument}
|
||||
*泛型实参子句*指定泛型类型的类型实参。泛型实参子句用尖括号(`<>`)包住,形式如下:
|
||||
|
||||
> <`泛型实参列表`>
|
||||
>
|
||||
|
||||
泛型实参列表中类型实参用逗号分开。类型实参是实际具体类型的名字,用来替代泛型类型的泛型形参子句中的相应的类型形参。从而得到泛型类型的一个特化版本。例如,Swift 标准库中的泛型字典类型的的简化定义如下:
|
||||
|
||||
```swift
|
||||
struct Dictionary<Key: Hashable, Value>: CollectionType, DictionaryLiteralConvertible {
|
||||
/* ... */
|
||||
}
|
||||
```
|
||||
|
||||
泛型 `Dictionary` 类型的特化版本,`Dictionary<String, Int>` 就是用具体的 `String` 和 `Int` 类型替代泛型类型 `Key: Hashable` 和 `Value` 产生的。每一个类型实参必须满足它所替代的泛型形参的所有约束,包括任何 `where` 子句所指定的额外的关联类型要求。上面的例子中,类型形参 `Key` 的类型必须符合 `Hashable` 协议,因此 `String` 也必须满足 `Hashable` 协议。
|
||||
|
||||
可以用本身就是泛型类型的特化版本的类型实参替代类型形参(假设已满足合适的约束和关联类型要求)。例如,为了生成一个元素类型是整型数组的数组,可以用数组的特化版本 `Array<Int>` 替代泛型类型 `Array<T>` 的类型形参 `T` 来实现。
|
||||
|
||||
```swift
|
||||
let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
|
||||
>
|
||||
```
|
||||
|
||||
如 [泛型形参子句](#generic_parameter) 所述,不能用泛型实参子句来指定泛型函数或构造器的类型实参。
|
||||
|
||||
> 泛型实参子句语法
|
||||
>
|
||||
|
||||
#### generic-argument-clause {#generic-argument-clause}
|
||||
> *泛型实参子句* → **<** [*泛型实参列表*](#generic-argument-list) **>**
|
||||
>
|
||||
|
||||
#### generic-argument-list {#generic-argument-list}
|
||||
> *泛型实参列表* → [*泛型实参*](#generic-argument) | [*泛型实参*](#generic-argument) **,** [*泛型实参列表*](#generic-argument-list)
|
||||
>
|
||||
|
||||
#### generic-argument {#generic-argument}
|
||||
> *泛型实参* → [*类型*](./03_Types.md#type)
|
||||
>
|
||||
# 泛型参数(Generic Parameters and Arguments)
|
||||
|
||||
本节涉及泛型类型、泛型函数以及泛型构造器的参数,包括形参和实参。声明泛型类型、函数或构造器时,须指定相应的类型参数。类型参数相当于一个占位符,当实例化泛型类型、调用泛型函数或泛型构造器时,就用具体的类型实参替代之。
|
||||
|
||||
关于 Swift 语言的泛型概述,请参阅 [泛型](../02_language_guide/22_Generics.md)。
|
||||
|
||||
## 泛型形参子句 {#generic-parameter}
|
||||
*泛型形参子句*指定泛型类型或函数的类型形参,以及这些参数相关的约束和要求。泛型形参子句用尖括号(`<>`)包住,形式如下:
|
||||
|
||||
> <`泛型形参列表`>
|
||||
>
|
||||
|
||||
泛型形参列表中泛型形参用逗号分开,其中每一个采用以下形式:
|
||||
|
||||
> `类型形参` : `约束`
|
||||
>
|
||||
|
||||
泛型形参由两部分组成:类型形参及其后的可选约束。类型形参只是占位符类型(如 `T`,`U`,`V`,`Key`,`Value` 等)的名字而已。你可以在泛型类型、函数的其余部分或者构造器声明,包括函数或构造器的签名中使用它(以及它的关联类型)。
|
||||
|
||||
约束用于指明该类型形参继承自某个类或者符合某个协议或协议组合。例如,在下面的泛型函数中,泛型形参 `T: Comparable` 表示任何用于替代类型形参 `T` 的类型实参必须满足 `Comparable` 协议。
|
||||
|
||||
```swift
|
||||
func simpleMax<T: Comparable>(_ x: T, _ y: T) -> T {
|
||||
if x < y {
|
||||
return y
|
||||
}
|
||||
return x
|
||||
}
|
||||
```
|
||||
|
||||
例如,因为 `Int` 和 `Double` 均满足 `Comparable` 协议,所以该函数可以接受这两种类型。与泛型类型相反,调用泛型函数或构造器时不需要指定泛型实参子句。类型实参由传递给函数或构造器的实参推断而出。
|
||||
|
||||
```swift
|
||||
simpleMax(17, 42) // T 被推断为 Int 类型
|
||||
simpleMax(3.14159, 2.71828) // T 被推断为 Double 类型
|
||||
```
|
||||
|
||||
### Where 子句 {#where-clauses}
|
||||
要想对类型形参及其关联类型指定额外要求,可以在函数体或者类型的大括号之前添加 `where` 子句。`where` 子句由关键字 `where` 及其后的用逗号分隔的一个或多个要求组成。
|
||||
|
||||
> `where` : `类型要求`
|
||||
>
|
||||
|
||||
`where` 子句中的要求用于指明该类型形参继承自某个类或符合某个协议或协议组合。尽管 `where` 子句提供了语法糖使其有助于表达类型形参上的简单约束(如 `<T: Comparable>` 等同于 `<T> where T: Comparable`,等等),但是依然可以用来对类型形参及其关联类型提供更复杂的约束,例如你可以强制形参的关联类型遵守协议,如,`<S: Sequence> where S.Iterator.Element: Equatable` 表示泛型类型 `S` 遵守 `Sequence` 协议并且关联类型 `S.Iterator.Element` 遵守 `Equatable` 协议,这个约束确保队列的每一个元素都是符合 `Equatable` 协议的。
|
||||
>
|
||||
|
||||
也可以用操作符 `==` 来指定两个类型必须相同。例如,泛型形参子句 `<S1: Sequence, S2: Sequence> where S1.Iterator.Element == S2.Iterator.Element` 表示 `S1` 和 `S2` 必须都符合 `SequenceType` 协议,而且两个序列中的元素类型必须相同。
|
||||
>
|
||||
|
||||
当然,替代类型形参的类型实参必须满足所有的约束和要求。
|
||||
|
||||
`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 子句](../02_language_guide/22_Generics.md#where-clauses)。
|
||||
|
||||
> 泛型形参子句语法
|
||||
>
|
||||
|
||||
#### generic-parameter-clause {#generic-parameter-clause}
|
||||
> *泛型形参子句* → **<** [泛型形参列表](#generic-parameter-list) [约束子句](#requirement-clause)<sub>可选</sub> **>**
|
||||
>
|
||||
|
||||
#### generic-parameter-list {#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-identifier)
|
||||
>
|
||||
> *泛形形参* → [类型名称](./03_Types.md#type-name) **:** [协议合成类型](./03_Types.md#protocol-composition-type)
|
||||
>
|
||||
>
|
||||
#### requirement-clause {#requirement-clause}
|
||||
>
|
||||
> *约束子句* → **where** [约束列表](#requirement-list)
|
||||
>
|
||||
|
||||
#### requirement-list {#requirement-list}
|
||||
> *约束列表* → [约束](#requirement) | [约束](#requirement) **,** [约束列表](#requirement-list)
|
||||
>
|
||||
|
||||
#### requirement {#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#protocol-composition-type)
|
||||
>
|
||||
|
||||
#### same-type-requirement {#same-type-requirement}
|
||||
> *同类型约束* → [类型标识符](./03_Types.md#type-identifier) **==** [类型](./03_Types.md#type)
|
||||
>
|
||||
|
||||
## 泛型实参子句 {#generic-argument}
|
||||
*泛型实参子句*指定泛型类型的类型实参。泛型实参子句用尖括号(`<>`)包住,形式如下:
|
||||
|
||||
> <`泛型实参列表`>
|
||||
>
|
||||
|
||||
泛型实参列表中类型实参用逗号分开。类型实参是实际具体类型的名字,用来替代泛型类型的泛型形参子句中的相应的类型形参。从而得到泛型类型的一个特化版本。例如,Swift 标准库中的泛型字典类型的的简化定义如下:
|
||||
|
||||
```swift
|
||||
struct Dictionary<Key: Hashable, Value>: CollectionType, DictionaryLiteralConvertible {
|
||||
/* ... */
|
||||
}
|
||||
```
|
||||
|
||||
泛型 `Dictionary` 类型的特化版本,`Dictionary<String, Int>` 就是用具体的 `String` 和 `Int` 类型替代泛型类型 `Key: Hashable` 和 `Value` 产生的。每一个类型实参必须满足它所替代的泛型形参的所有约束,包括任何 `where` 子句所指定的额外的关联类型要求。上面的例子中,类型形参 `Key` 的类型必须符合 `Hashable` 协议,因此 `String` 也必须满足 `Hashable` 协议。
|
||||
|
||||
可以用本身就是泛型类型的特化版本的类型实参替代类型形参(假设已满足合适的约束和关联类型要求)。例如,为了生成一个元素类型是整型数组的数组,可以用数组的特化版本 `Array<Int>` 替代泛型类型 `Array<T>` 的类型形参 `T` 来实现。
|
||||
|
||||
```swift
|
||||
let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
|
||||
```
|
||||
|
||||
如 [泛型形参子句](#generic-parameter) 所述,不能用泛型实参子句来指定泛型函数或构造器的类型实参。
|
||||
|
||||
> 泛型实参子句语法
|
||||
>
|
||||
|
||||
#### generic-argument-clause {#generic-argument-clause}
|
||||
> *泛型实参子句* → **<** [泛型实参列表](#generic-argument-list) **>**
|
||||
>
|
||||
|
||||
#### generic-argument-list {#generic-argument-list}
|
||||
> *泛型实参列表* → [泛型实参](#generic-argument) | [泛型实参](#generic-argument) **,** [泛型实参列表](#generic-argument-list)
|
||||
>
|
||||
|
||||
#### generic-argument {#generic-argument}
|
||||
> *泛型实参* → [类型](./03_Types.md#type)
|
||||
>
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,3 @@
|
||||
# Swift 教程
|
||||
# Swift 语言参考
|
||||
|
||||
本章描述了 Swift 的语言参考。
|
||||
278
source/04_revision_history/04_revision_history.md
Normal file
278
source/04_revision_history/04_revision_history.md
Normal 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) 的示例。
|
||||
|
||||
3
source/04_revision_history/chapter4.md
Normal file
3
source/04_revision_history/chapter4.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Swift 文档修订历史
|
||||
|
||||
本章描述了 Swift 文档修订历史。
|
||||
421
source/README.md
421
source/README.md
@ -1,149 +1,278 @@
|
||||
# 文档翻译 & 校对工作记录
|
||||
|
||||
Swift 官方文档中文翻译工作由[numbbbbb](https://github.com/numbbbbb)发起并主导,该工作已经得到了苹果官方的认可。下面是各个版本官方文档翻译和校对工作的主要贡献者,排名不分先后。
|
||||
|
||||
## Swift 5.x 主要贡献者
|
||||
|
||||
- [Adolf-L](https://github.com/Adolf-L)
|
||||
- [BigNerdCoding](https://github.com/bignerdcoding)
|
||||
- [bqlin](https://github.com/bqlin)
|
||||
- [Byelaney](https://github.com/Byelaney)
|
||||
- [CMB](https://github.com/chenmingbiao)
|
||||
- [DarrenChen123](https://github.com/DarrenChen123)
|
||||
- [dzyding](https://github.com/dzyding)
|
||||
- [Hale](https://github.com/wuqiuhao)
|
||||
- [jojotov](https://github.com/jojotov)
|
||||
- [Khala-wan](https://github.com/Khala-wan)
|
||||
- [Nemocdz](https://github.com/Nemocdz)
|
||||
- [numbbbbb](https://github.com/numbbbbb)
|
||||
- [WAMaker](https://github.com/WAMaker)
|
||||
- [Yousanflics](https://github.com/Yousanflics)
|
||||
|
||||
## Swift 4.x 主要贡献者
|
||||
|
||||
- [Adolf-L](https://github.com/Adolf-L)
|
||||
- [BigNerdCoding](https://github.com/bignerdcoding)
|
||||
- [bqlin](https://github.com/bqlin)
|
||||
- [Cee](https://github.com/Cee)
|
||||
- [CMB](https://github.com/chenmingbiao)
|
||||
- [Damonwong](https://github.com/Damonvvong)
|
||||
- [Desgard](https://github.com/Desgard)
|
||||
- [dzyding](https://github.com/dzyding)
|
||||
- [EyreFree](https://www.eyrefree.org/)
|
||||
- [Forelas](https://github.com/ForelaxX)
|
||||
- [Hale](https://github.com/wuqiuhao)
|
||||
- [kemchenj](https://kemchenj.github.io)
|
||||
- [jojotov](https://github.com/jojotov)
|
||||
- [Meler](https://github.com/pmtao)
|
||||
- [mobilefellow](https://github.com/mobilefellow)
|
||||
- [muhlenXi](https://github.com/muhlenxi)
|
||||
- [mylittleswift](https://github.com/mylittleswift)
|
||||
- [Nemocdz](https://github.com/Nemocdz)
|
||||
- [numbbbbb](https://github.com/numbbbbb)
|
||||
- [rain2540](https://github.com/rain2540)
|
||||
- [Rsenjoyer](https://github.com/Rsenjoyer)
|
||||
- [WAMaker](https://github.com/WAMaker)
|
||||
- [YiYiZheng](https://github.com/YiYiZheng)
|
||||
- [ZhangChi](https://github.com/zhangchi25806)
|
||||
|
||||
## Swift 3.x 主要贡献者
|
||||
|
||||
- [bqlin](https://github.com/bqlin)
|
||||
- [chenmingjia](https://github.com/chenmingjia)
|
||||
- [CMB](https://github.com/chenmingbiao)
|
||||
- [crayygy](https://github.com/crayygy)
|
||||
- [kemchenj](https://kemchenj.github.io)
|
||||
- [Lanford](https://github.com/LanfordCai)
|
||||
- [mmoaay](https://github.com/mmoaay)
|
||||
- [mylittleswift](https://github.com/mylittleswift)
|
||||
- [qhd](https://github.com/qhd)
|
||||
- [shanks](https://github.com/shanksyang)
|
||||
|
||||
## Swift 2.x 主要贡献者
|
||||
|
||||
- [100mango](https://github.com/100mango)
|
||||
- [175](https://github.com/Brian175)
|
||||
- [BridgeQ](https://github.com/WXGBridgeQ)
|
||||
- [buginux](https://github.com/buginux)
|
||||
- [Cee](https://github.com/Cee)
|
||||
- [Channe](https://github.com/Channe)
|
||||
- [CMB](https://github.com/chenmingbiao)
|
||||
- [DianQK](https://github.com/DianQK)
|
||||
- [dreamkidd](https://github.com/dreamkidd)
|
||||
- [EudeMorgen](https://github.com/EudeMorgen)
|
||||
- [futantan](https://github.com/futantan)
|
||||
- [JackAlan](https://github.com/AlanMelody)
|
||||
- [KYawn](https://github.com/KYawn)
|
||||
- [Lanford](https://github.com/LanfordCai)
|
||||
- [Lenhoon](https://github.com/Lenhoon)
|
||||
- [littledogboy](https://github.com/littledogboy)
|
||||
- [LinusLing](https://github.com/linusling)
|
||||
- [lyojo](https://github.com/lyojo)
|
||||
- [miaosiqi](https://github.com/miaosiqi)
|
||||
- [mmoaay](https://github.com/mmoaay)
|
||||
- [overtrue](https://github.com/overtrue)
|
||||
- [pmst](https://github.com/colourful987)
|
||||
- [Prayer](https://github.com/futantan)
|
||||
- [qhd](https://github.com/qhd)
|
||||
- [ray16897188](https://github.com/ray16897188)
|
||||
- [Realank](https://github.com/realank)
|
||||
- [saitjr](https://github.com/saitjr)
|
||||
- [SergioChan](https://github.com/SergioChan)
|
||||
- [shanks](https://github.com/shanksyang)
|
||||
- [SketchK](https://github.com/SketchK)
|
||||
- [SkyJean](https://github.com/SkyJean)
|
||||
- [wardenNScaiyi](https:github.com/wardenNScaiyi)
|
||||
- [xtymichael](https://github.com/xtymichael)
|
||||
- [yangsiy](https://github.com/yangsiy)
|
||||
- [星夜暮晨](https://github.com/semperidem)
|
||||
- [小铁匠 Linus](https://github.com/kevin833752)
|
||||
|
||||
## Swift 1.x 主要贡献者
|
||||
|
||||
- [bruce0505](https://github.com/bruce0505)
|
||||
- [changkun](http://changkun.us/about/)
|
||||
- [ChildhoodAndy](http://childhood.logdown.com)
|
||||
- [coverxit](https://github.com/coverxit)
|
||||
- [dabing1022](https://github.com/dabing1022)
|
||||
- [EvilCome](https://github.com/Evilcome)
|
||||
- [feiin](https://github.com/feiin)
|
||||
- [fd5788](https://github.com/fd5788)
|
||||
- [geek5nan](https://github.com/geek5nan)
|
||||
- [happyming](https://github.com/happyming)
|
||||
- [Hawstein](https://github.com/Hawstein)
|
||||
- [honghaoz](https://github.com/honghaoz)
|
||||
- [JaceFu](http://www.devtalking.com/)
|
||||
- [Jasonbroker](https://github.com/Jasonbroker)
|
||||
- [JaySurplus](https://github.com/JaySurplus)
|
||||
- [Lenhoon](https://github.com/marsprince)
|
||||
- [lifedim](https://github.com/lifedim)
|
||||
- [Lin-H](https://github.com/Lin-H)
|
||||
- [lslxdx](https://github.com/lslxdx)
|
||||
- [LunaticM](https://github.com/LunaticM)
|
||||
- [lyuka](https://github.com/lyuka)
|
||||
- [marsprince](https://github.com/marsprince)
|
||||
- [menlongsheng](https://github.com/menlongsheng)
|
||||
- [NicePiao](https://github.com/NicePiao)
|
||||
- [numbbbbb](https://github.com/numbbbbb)
|
||||
- [pp-prog](https://github.com/pp-prog)
|
||||
- [sg552](https://github.com/sg552)
|
||||
- [stanzhai](https://github.com/stanzhai)
|
||||
- [shinyzhu](https://github.com/shinyzhu)
|
||||
- [superkam](https://github.com/superkam)
|
||||
- [takalard](https://github.com/takalard)
|
||||
- [TimothyYe](https://github.com/TimothyYe)
|
||||
- [vclwei](https://github.com/vclwei)
|
||||
- [wh1100717](https://github.com/wh1100717)
|
||||
- [xiehurricane](https://github.com/xiehurricane)
|
||||
- [XieLingWang](https://github.com/xielingwang)
|
||||
- [yangsiy](https://github.com/yangsiy)
|
||||
- [yankuangshi](https://github.com/yankuangshi)
|
||||
- [yeahdongcn](https://github.com/yeahdongcn)
|
||||
- [yangsiy](https://github.com/yangsiy)
|
||||
- [zqp](https://github.com/zqp)
|
||||
- [成都老码团队翻译组-Arya](http://weibo.com/littlekok/)
|
||||
- [成都老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)
|
||||
|
||||
# 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) 的示例。
|
||||
|
||||
|
||||
@ -1,49 +1,49 @@
|
||||
# Summary
|
||||
|
||||
* [Introduction](README.md)
|
||||
* 欢迎使用 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)
|
||||
|
||||
* [翻译贡献者](contributors.md)
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
# 版本兼容性
|
||||
|
||||
本书描述的是在 Xcode 10.2 中的默认 Swift 版本 Swift 5。你可以使用 Xcode10.2 来构建 Swift 5、Swift 4.2 或 Swift 4 写的项目
|
||||
|
||||
当您使用 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。
|
||||
@ -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-type’s-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)的示例。
|
||||
Binary file not shown.
Binary file not shown.
@ -1,646 +0,0 @@
|
||||
# 集合类型
|
||||
|
||||
Swift 语言提供 `Arrays`、`Sets` 和 `Dictionaries` 三种基本的*集合类型*用来存储集合数据。数组(Arrays)是有序数据的集。集合(Sets)是无序无重复数据的集。字典(Dictionaries)是无序的键值对的集。
|
||||
|
||||

|
||||
|
||||
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`,以及通过阴影部分的区域显示集合各种操作的结果。
|
||||
|
||||

|
||||
|
||||
* 使用 `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` 彼此不关联,因为它们之间没有共同的元素。
|
||||
|
||||

|
||||
|
||||
* 使用“是否相等”运算符(`==`)来判断两个集合是否包含全部相同的值。
|
||||
* 使用 `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()` 方法。
|
||||
@ -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+0000,U+0009,U+000B,U+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+00A8,U+00AA,U+00AD,U+00AF,U+00B2–U+00B5,或者 U+00B7–U+00BA
|
||||
>
|
||||
> *头部标识符* → U+00BC–U+00BE,U+00C0–U+00D6,U+00D8–U+00F6,或者 U+00F8–U+00FF
|
||||
>
|
||||
> *头部标识符* → U+0100–U+02FF,U+0370–U+167F,U+1681–U+180D,或者 U+180F–U+1DBF
|
||||
>
|
||||
> *头部标识符* → U+1E00–U+1FFF
|
||||
>
|
||||
> *头部标识符* → U+200B–U+200D,U+202A–U+202E,U+203F–U+2040,U+2054,或者 U+2060–U+206F
|
||||
>
|
||||
> *头部标识符* → U+2070–U+20CF,U+2100–U+218F,U+2460–U+24FF,或者 U+2776–U+2793
|
||||
>
|
||||
> *头部标识符* → U+2C00–U+2DFF 或者 U+2E80–U+2FFF
|
||||
>
|
||||
> *头部标识符* → U+3004–U+3007,U+3021–U+302F,U+3031–U+303F,或者 U+3040–U+D7FF
|
||||
>
|
||||
> *头部标识符* → U+F900–U+FD3D,U+FD40–U+FDCF,U+FDF0–U+FE1F,或者 U+FE30–U+FE44
|
||||
>
|
||||
> *头部标识符* → U+FE47–U+FFFD
|
||||
>
|
||||
> *头部标识符* → U+10000–U+1FFFD,U+20000–U+2FFFD,U+30000–U+3FFFD,或者 U+40000–U+4FFFD
|
||||
>
|
||||
> *头部标识符* → U+50000–U+5FFFD,U+60000–U+6FFFD,U+70000–U+7FFFD,或者 U+80000–U+8FFFD
|
||||
>
|
||||
> *头部标识符* → U+90000–U+9FFFD,U+A0000–U+AFFFD,U+B0000–U+BFFFD,或者 U+C0000–U+CFFFD
|
||||
>
|
||||
> *头部标识符* → U+D0000–U+DFFFD 或者 U+E0000–U+EFFFD
|
||||
>
|
||||
> *标识符字符* → 数值 0 - 9
|
||||
>
|
||||
>
|
||||
#### identifier-character {#identifier-character}
|
||||
>
|
||||
> *标识符字符* → U+0300–U+036F,U+1DC0–U+1DFF,U+20D0–U+20FF,或者 U+FE20–U+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.25e-2` 表示 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+00A1–U+00A7
|
||||
>
|
||||
> *头部运算符* → U+00A9 或 U+00AB
|
||||
>
|
||||
> *头部运算符* → U+00AC 或 U+00AE
|
||||
>
|
||||
> *头部运算符* → U+00B0–U+00B1,U+00B6,U+00BB,U+00BF,U+00D7,或 U+00F7
|
||||
>
|
||||
> *头部运算符* → U+2016–U+2017 或 U+2020–U+2027
|
||||
>
|
||||
> *头部运算符* → U+2030–U+203E
|
||||
>
|
||||
> *头部运算符* → U+2041–U+2053
|
||||
>
|
||||
> *头部运算符* → U+2055–U+205E
|
||||
>
|
||||
> *头部运算符* → U+2190–U+23FF
|
||||
>
|
||||
> *头部运算符* → U+2500–U+2775
|
||||
>
|
||||
> *头部运算符* → U+2794–U+2BFF
|
||||
>
|
||||
> *头部运算符* → U+2E00–U+2E7F
|
||||
>
|
||||
> *头部运算符* → U+3001–U+3003
|
||||
>
|
||||
> *头部运算符* → U+3008–U+3030
|
||||
>
|
||||
>
|
||||
#### operator-character {#operator-character}
|
||||
>
|
||||
> *运算符字符* → [*头部运算符*](#operator-head)
|
||||
>
|
||||
> *运算符字符* → U+0300–U+036F
|
||||
>
|
||||
> *运算符字符* → U+1DC0–U+1DFF
|
||||
>
|
||||
> *运算符字符* → U+20D0–U+20FF
|
||||
>
|
||||
> *运算符字符* → U+FE00–U+FE0F
|
||||
>
|
||||
> *运算符字符* → U+FE20–U+FE2F
|
||||
>
|
||||
> *运算符字符* → U+E0100–U+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)
|
||||
@ -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> **}**
|
||||
>
|
||||
> *均衡令牌* → 任意标识符,关键字,字面量或运算符
|
||||
>
|
||||
> *均衡令牌* → 任意标点除了 **(**,**)**,**[**,**]**,**{**,或 **}**
|
||||
>
|
||||
@ -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)
|
||||
|
||||
|
||||
BIN
source/cover.jpg
BIN
source/cover.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 175 KiB After Width: | Height: | Size: 576 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 20 KiB |
@ -11,7 +11,7 @@
|
||||
|
||||
> 小明说哇靠学姐你还会妖法......
|
||||
|
||||
Swift 语言从 Xcode 6 beta 5版本起,加入了对权限控制(Access Control)的支持。其实权限控制和小明的物品一样,你可以设定水壶是只有自己能用,还是只有宿舍里的人能用,还是全校都可以用。
|
||||
Swift 语言从 Xcode 6 beta 5 版本起,加入了对权限控制(Access Control)的支持。其实权限控制和小明的物品一样,你可以设定水壶是只有自己能用,还是只有宿舍里的人能用,还是全校都可以用。
|
||||
|
||||
从此以后,你可以好像神盾局局长一样,完全掌控自己的代码块的”保密级别“,哪些是只能在本文件引用,哪些能用在整个项目里,你还可以发挥大爱精神,把它开源成只要导入你的框架,大家都可以使用的 API。
|
||||
|
||||
Reference in New Issue
Block a user