3 Commits

Author SHA1 Message Date
47598b3c8e Patch 1 (#927)
* Update contributors.md

* 校对: chapter2/03_Strings_and_Characters 方法名变化 (#921)

index(of:) 已更名为: firstIndex(of:)

* Update 01_The_Basics.md

* fix invalid url (#923)

* test gitbook anchor

* Revert "test gitbook anchor"

This reverts commit e82cea3866.

* Update 21_Protocols.md (#924)

* Update 25_Access_Control.md (#925)

* Update 02_version_compatibility.md
2019-05-03 18:58:57 -05:00
1c973d61a1 更新电子书封面和introduce
Signed-off-by: Harry Twan <gua@desgard.com>
2019-04-12 09:55:43 +08:00
7a53af48c1 适配 gitbook
Signed-off-by: Harry Twan <gua@desgard.com>
2019-04-12 08:31:57 +08:00
53 changed files with 8564 additions and 8800 deletions

12
.github/FUNDING.yml vendored
View File

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

1
CNAME Executable file
View File

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

View File

@ -5,19 +5,19 @@
[英文原版在线版](https://docs.swift.org/swift-book/)
[英文原版ePub版](https://docs.swift.org/swift-book/TheSwiftProgrammingLanguageSwift5.epub)
# 在线阅读
使用 GitBook 制作,可以直接 [在线阅读](https://swiftgg.gitbook.io/swift/)。
# 当前阶段
- 更新到 Swift 5.12019-07-10
- 更新到 Swift 5.02019-04-05
- 更新到 Swift 4.22019-01-29
- 更新到 Swift 4.12018-04-12感谢 [@Mylittleswift](https://github.com/Mylittleswift)
- 更新到 Swift 3.02016-09-23
# 贡献力量
如果想做出贡献的话,你可以:
@ -59,22 +59,22 @@ diff 操作如下:
| --- | --- |
| argument | 实参 |
| parameter | 形参 |
| associated type | 关联类型 |
| associatedtype | 关联类型 |
| 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 +87,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 +109,7 @@ diff 操作如下:
| tuple | 元组 |
| first-class | 一等 |
| deinitializer | 析构器 |
| initializer | 构造器 |
| Initializer | 构造器 |
| initialization | 构造过程 |
| deinitialization | 析构过程 |
| getter | 不翻译 |
@ -118,18 +118,15 @@ 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 | 运行时 |
# 贡献者

4
SUMMARY.md Normal file
View File

@ -0,0 +1,4 @@
# Summary
* [Introduction](README.md)

22
change_cdn.py Executable file
View File

@ -0,0 +1,22 @@
#!/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())

9
config.json Normal file
View File

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

1
googleb0a4f5a22e9cb82f.html Executable file
View File

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

View File

@ -1,8 +1,19 @@
<!DOCTYPE HTML>
<html lang="en-US" manifest="./manifest.appcache">
<head>
<meta http-equiv="refresh" content="0; url=https://swiftgg.gitbook.io/swift/" />
<meta http-equiv="refresh" content="0; url=http://wiki.jikexueyuan.com/project/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 Normal file
View File

@ -0,0 +1,4 @@
_book
*.pdf
*.epub
*.mobi

View File

@ -1,22 +1,149 @@
> 2016.9.23: 已经更新到 Swift 3.0。
# 3.0 更新说明
Swift 3.0 是自 Swift 开源以来第一个大的版本更新。从语言角度不兼容之前的 Swift 2.2 和 Swift 2.3 版本。Swift 3.0 的更新说明,大家可以查看[官方 blog 的说明](https://swift.org/blog/swift-3-0-released/),也可以关注 [SwiftGG](http://swift.gg) 最新的文章。学习官方文档,是掌握语言特性点的最佳途径,感谢翻译的小伙伴们为 Swift 社区所做贡献!
# 文档翻译 & 校对工作记录
Swift 官方文档中文翻译工作由[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)
# 3.0 译者记录
相关[issue](https://github.com/numbbbbb/the-swift-programming-language-in-chinese/issues/628)
- Functions: [crayygy](https://github.com/crayygy)
- Control Flow: [Realank](https://github.com/Realank)
- Closures: [LanfordCai](https://github.com/LanfordCai)
- Protocols: [chenmingbiao](https://github.com/chenmingbiao)
- The Basics:[chenmingbiao](https://github.com/chenmingbiao)
- Advanced Operators: [mmoaay](https://github.com/mmoaay)
Language Reference:
- Attributes: [WhoJave](https://github.com/WhoJave)
- Statements: [chenmingjia](https://github.com/chenmingjia)
- Declarations: [chenmingjia](https://github.com/chenmingjia)
- Expressions: [chenmingjia](https://github.com/chenmingjia)
- Types: [lettleprince](https://github.com/lettleprince)
- Generic Parameters and Arguments: [chenmingjia](https://github.com/chenmingjia)
感谢阅读!

View File

@ -1,48 +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](chapter1/01_about_swift.md)
* [版本兼容性](chapter1/02_version_compatibility.md)
* [Swift 初见](chapter1/03_a_swift_tour.md)
* [Swift 版本历史记录](chapter1/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/27_Opaque_Types.md)
* [自动引用计数](chapter2/23_Automatic_Reference_Counting.md)
* [内存安全](chapter2/24_Memory_Safety.md)
* [访问控制](chapter2/25_Access_Control.md)
* [高级运算符](chapter2/26_Advanced_Operators.md)
* [基础部分](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)
* 语言参考
* [关于语言参考](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)
* [关于语言参考](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)
* 翻译贡献者
* [翻译贡献者](contributors.md)
* [翻译贡献者](contributors.md)

View File

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

View File

@ -65,7 +65,7 @@ let fruitSummary = "I have \(apples + oranges) pieces of fruit."
>
> 使用 `\()` 来把一个浮点计算转换成字符串,并加上某人的名字,和他打个招呼。
使用三个引号(`"""`)来包含多行字符串内容,字符串中的内容(包括引号、空格、换行符等)都会保留下来。举个例子:
使用一对三个引号(`"""`)来包含多行字符串内容,字符串中的内容(包括引号、空格、换行符等)都会保留下来。举个例子:
```swift
let quotation = """

View File

@ -1,267 +1,256 @@
# Swift 文档修订历史
### 2019-06-03
### 2019-01-24
* 更新 Swift 5.1
* 在 [不透明类型](../chapter2/27_Opaque_Types.md) 篇章中新增了有关函数返回值遵循指定协议,而不需要提供指定返回类型的内容。
* 新增 [隐式返回的函数](../chapter2/06_Functions.md#functions-with-an-implicit-return) 和 [简化 Getter 声明](../chapter2/10_Properties.md#shorthand-getter-declaration) 章节,其中包含函数省略 `return` 的内容
* 在 [类型下标](../chapter2/12_Subscripts.md#type-subscripts) 章节中新增有关在类型中使用下标的内容
* 更新 [结构体的逐一成员构造器](../chapter2/14_Initialization.md#memberwise-initializers-for-structure-types) 章节,现在逐一成员构造器支持在属性有默认值时省略形参
* [动态查找成员](../chapter3/07_Attributes.md#dynamicmemberlookup) 章节新增了有关在运行时用 key path 查找动态成员的内容。
* 更新 [自身类型](../chapter3/03_Types.md#self-type-h) 章节,现在 `Self` 可以指向当前类,结构体或者枚举声明时的类型。
### 2019-03-25
* 更新至 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) 章节新增了有关小于比较符 `<` 的内容。
* 更新 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` 内容。
* 在[遍历枚举情形](../chapter2/08_Enumerations.md#iterating-over-enumeration-cases)章节添加了访问所有枚举情形的内容。
* 在[编译诊断](../chapter3/05_Statements.md#compile-time-diagnostic-statement)章节添加了有关 `#error``#warning` 相关内容。
* 在[属性声明](../chapter3/07_Attributes.md#Ideclaration-attributes)章节中补充了 `inlinable``usableFromInline` 属性相关的内联信息
* 在[属性声明](../chapter3/07_Attributes.md#Ideclaration-attributes)章节中添加了 `requires_stored_property_inits``warn_unqualified_access` 属性相关的信息
* 在[可选编译块](../chapter3/05_Statements.md#Conditional-Compilation-Block)章节新增了如何根据 Swift 编译器版本对代码进行对应编译处理的内容。
* 在[字面量语法](../chapter3/04_Expressions.md#literal-expression)章节补充了 `#dsohandle` 相关内容。
### 2018-03-29
* 更新至 Swift 4.1。
* [等价运算符](../chapter2/26_Advanced_Operators.md#equivalence-operators) 章节新增了有关等价运算符的合成实现的内容
* [声明](../chapter3/06_Declarations.md) 篇章中 [申明拓展](../chapter3/06_Declarations.md#extension-declaration) 章节和 [协议](../chapter2/21_Protocols.md) 篇章中 [有条件地遵循协议](../chapter2/21_Protocols.md#Conditionally-Conforming-to-a-Protocol) 章节新增了有关协议有条件遵循内容。
* [关联类型约束中使用协议](../chapter2/22_Generics.md#using-a-protocol-in-its-associated-types-constraints) 章节中新增了有关递归协议约束的内容。
* [条件编译块](../chapter3/05_Statements.md#Conditional-Compilation-Block) 章节中新增了有关 `canImport()``targetEnvironment()` 平台条件内容。
* 在[等价运算符](../chapter2/26_Advanced_Operators.md#equivalence-operators)章节添加了等价运算符的合成实现信息
* 在[声明](../chapter3/06_Declarations.md)一章的[申明拓展](../chapter3/06_Declarations.md#extension-declaration)部分和[协议](../chapter2/21_Protocols.md)一章的[有条件地遵循协议](../chapter2/21_Protocols.md#Conditionally-Conforming-to-a-Protocol)部分添加了协议有条件遵循相关内容。
* 在[关联类型约束中使用协议](../chapter2/22_Generics.md##using-a-protocol-in-its-associated-types-constraints)章节中添加了递归协议约束的内容。
* 在[条件编译块](../chapter3/05_Statements.md#Conditional-Compilation-Block)章节中添加了 `canImport()``targetEnvironment()` 平台条件相关内容。
### 2017-12-04
* 更新至 Swift 4.0.3。
* 更新 [Key-Path 表达式](../chapter3/04_Expressions.md#key-path-expression) 章节,现在 key path 支持下标子路径。
* 更新[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-type-h) 章节的讨论,现在协议组合类型支持进行父类约束了。
* 更新 [拓展声明](../chapter3/06_Declarations.md#extension-declaration) 中有关协议扩展的讨论,现在它们不支持 `final` 特性了。
* [断言和前置条件](../chapter2/01_TheBasics.md#assertions-and-preconditions) 章节中新增了部分前置条件和致命错误的内容。
* 在[内存安全](../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) 章节中新增了区间迭代的例子
* 在 [到可失败构造器](http://typora-app/chapter2/14_Initialization.md#failable-initializers) 章节中新增了可失败数值转换的例子
* [声明特性](../chapter3/07_Attributes.md#Ideclaration-attributes) 章节中新增了有关使用 Swift 语言版本的 `available` 特性的内容
* 更新 [函数类型](../chapter3/03_Types.md#function-type-h) 章节中的讨论,注意在写函数类型时不允许使用参数标签。
* 更新 [条件编译块](../chapter3/05_Statements.md#Conditional-Compilation-Block) 章节中的 Swift 语言版本号的讨论,现在可以使用可选的补丁版本号。
* 更新 [函数类型](../chapter3/03_Types.md#function-type-h) 章节的讨论,现在 Swift 区分了采用多参数的函数和采用元组类型的单个参数的函数。
* [表达式](../chapter3/04_Expressions.md)章中删除了动态表达式的章节,现在 `type(of:)` 是 Swift 标准库函数。
* 增加[范型 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) 章节,把括号表达式和元组表达式的描述分开。
* 更新[自动引用计数](../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) 篇章中 [泛型 Where 语句](../chapter2/22_Generics.md#extensions-with-a-generic-where-clause) 章节和 [泛型形参和实参](../chapter3/09_Generic_Parameters_And_Arguments.md) 篇章的讨论,现在泛型的 where 语句写在一个声明的最后。
* 更新 [逃逸闭包](../chapter2/07_Closures.md#escaping-closures) 章节中的讨论,现在闭包默认为非逃逸的。
* 更新 [基础部分](../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-h) 章节有关现在函数参数标签不包含在函数类型中的讨论
* 更新 [协议](../chapter2/21_Protocols.md) 篇章中 [协议组合](../chapter2/21_Protocols.md#protocol-composition) 章节和 [类型](../chapter3/03_Types.md) 篇章中 [协议组合类型](../chapter3/03_Types.md#protocol-composition-type-h) 章节中有关使用新的 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-h) 章节中有关函数类型的讨论,标明函数类型作为参数类型必须使用括号包裹
* 更新 [属性](../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` 协议的内容
* 更新[函数](../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` 循环,`++` 前缀和后缀运算符,以及 `--` 前缀和后缀运算符。
* 删除对变量函数参数和柯里化函数的特殊语法的讨论。
* 增加了[编译配置语句](../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) 篇章中新增了一个使用 `??` 操作符的例子。
* 更新[字符串插值](../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-h) 章节中新增了关元类型值的比较和使用它们通过构造器表达式构造实例相关内容。
* [断言调试](../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-h) 章节新增了一处说明,有关如何从元类型值中构造类实例相关内容。
* [弱引用](../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 标量更精确定义。
* 在[错误处理](../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-04-08
### 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) 必须出现在后缀表达式中。
* 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)。
* 在运行时可能会失败的类型转换可以使用 `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) 章节删除了重复的运算符有效字符集合。
* 增加了关于[失败构造器](../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 的新文档。Swift 是苹果公司发布的全新编程语言,用于 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) 类型推导内容的内容
* 新增更多有关柯里化函数的内容
* 新增 [权限控制](../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进制数。
* 发布新的文档用以详述 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) 的示例。
* 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.

File diff suppressed because it is too large Load Diff

View File

@ -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 等于 1y 等于 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` 中:
![Art/remainderInteger_2x.png](https://docs.swift.org/swift-book/_images/remainderInteger_2x.png "Art/remainderInteger_2x.png")
你可以在 `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 等于 4dog 等于 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 的溢出运算符来实现溢出。详情参见[溢出运算符](./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 等于 1y 等于 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` 中:
![Art/remainderInteger_2x.png](https://docs.swift.org/swift-book/_images/remainderInteger_2x.png "Art/remainderInteger_2x.png")
你可以在 `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 等于 4dog 等于 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!”
```
这括号使得前两个值被看成整个逻辑表达中独立的一个部分。虽然有括号和没括号的输出结果是一样的,但对于读代码的人来说有括号的代码更清晰。可读性比简洁性更重要,请在可以让你代码变清晰的地方加个括号吧!

View File

@ -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 = #"""
@ -458,7 +458,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,7 +556,7 @@ print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
> 注意
>
> `hasPrefix(_:)` 和 `hasSuffix(_:)` 方法都是在每个字符串中逐字符比较其可扩展的字符群集是否标准相等,详细描述在 [字符串/字符相等](#string_and_character_equality)。
> `hasPrefix(_:)` 和 `hasSuffix(_:)` 方法都是在每个字符串中逐字符比较其可扩展的字符群集是否标准相等,详细描述在[字符串/字符相等](#string_and_character_equality)。
## 字符串的 Unicode 表示形式 {#unicode-representations-of-strings}

File diff suppressed because it is too large Load Diff

View File

@ -30,12 +30,12 @@ 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
@ -324,7 +324,7 @@ default:
// 输出“The last letter of the alphabet”
```
在这个例子中,第一个 case 分支用于匹配第一个英文字母 `a`,第二个 case 分支用于匹配最后一个字母 `z`。因为 `switch` 语句必须有一个 case 分支用于覆盖所有可能的字符,而不仅仅是所有的英文字母,所以 switch 语句使用 `default` 分支来匹配除了 `a``z` 外的所有值,这个分支保证了 switch 语句的完备性。
在这个例子中,第一个 case 分支用于匹配第一个英文字母 `a`,第二个 case 分支用于匹配最后一个字母 `z`。因为 `switch` 语句必须有一个 case 分支用于覆盖所有可能的字符,而不仅仅是所有的英文字母,所以 switch 语句使用 `default` 分支来匹配除了 `a``z` 外的所有值,这个分支保证了 swith 语句的完备性。
#### 不存在隐式的贯穿 {#no-implicit-fallthrough}
@ -332,7 +332,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 +363,11 @@ default:
// 输出“The letter A”
```
为了可读性,符合匹配可以写成多行形式,详情请参考 [复合匹配](#compound_cases)
为了可读性,符合匹配可以写成多行形式,详情请参考[复合匹配](#compound_cases)
> 注意
>
> 如果想要显式贯穿 case 分支,请使用 `fallthrough` 语句,详情请参考 [贯穿](#fallthrough)。
> 如果想要显式贯穿 case 分支,请使用 `fallthrough` 语句,详情请参考[贯穿](#fallthrough)。
#### 区间匹配 {#interval-matching}
@ -527,7 +527,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` 语句会在[错误抛出](./18_Error_Handling.md#throwing_errors)章节讨论。
### Continue {#continue}
@ -754,7 +754,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`——请访问[声明属性](../chapter3/06_Attributes.html)来获取完整列表。除了指定像 iOS 8 或 macOS 10.10 的大版本号,也可以指定像 iOS 11.2.6 以及 macOS 10.13.3 的小版本号。
```swift
if #available(平台名称 版本号, ..., *) {

View File

@ -202,30 +202,6 @@ 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*。参数标签在调用函数的时候使用;调用的时候需要将函数的参数标签写在对应的参数前面。参数名称在函数的实现中使用。默认情况下,函数参数使用参数名称来作为它们的参数标签。
@ -318,7 +294,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` 关键字。一个 `输入输出参数`有传入函数的值,这个值被函数修改,然后被传出函数,替换原来的值。想获取更多的关于输入输出参数的细节和相关的编译器优化,请查看[输入输出参数](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID545)一节。
你只能传递变量给输入输出参数。你不能传入常量或者字面量,因为这些量是不能被修改的。当传入的参数作为输入输出参数时,需要在参数名前加 `&` 符,表示这个值可以被函数修改。

View File

@ -6,9 +6,9 @@
> 注意
>
> 如果你不熟悉捕获capturing这个概念也不用担心 [值捕获](#capturing_values) 章节有它更详细的介绍。
> 如果你不熟悉捕获capturing这个概念也不用担心在[值捕获](#capturing_values)章节有它更详细的介绍。
[函数](./06_Functions.md) 章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采用如下三种形式之一:
在[函数](./06_Functions.md)章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采用如下三种形式之一:
* 全局函数是一个有名字但不会捕获任何值的闭包
* 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
@ -23,7 +23,7 @@ Swift 的闭包表达式拥有简洁的风格,并鼓励在常见场景中进
## 闭包表达式 {#closure-expressions}
[嵌套函数](./06_Functions.md#Nested_Functions) 作为复杂函数的一部分时,它自包含代码块式的定义和命名形式在使用上带来了方便。当然,编写未完整声明和没有函数名的类函数结构代码是很有用的,尤其是在编码中涉及到函数作为参数的那些方法时。
[嵌套函数](./06_Functions.md#Nested_Functions)作为复杂函数的一部分时,它自包含代码块式的定义和命名形式在使用上带来了方便。当然,编写未完整声明和没有函数名的类函数结构代码是很有用的,尤其是在编码中涉及到函数作为参数的那些方法时。
*闭包表达式*是一种构建内联闭包的方式,它的语法简洁。在保证不丢失它语法清晰明了的同时,闭包表达式提供了几种优化的语法简写形式。下面通过对 `sorted(by:)` 这一个案例的多次迭代改进来展示这个过程,每次迭代都使用了更加简明的方式描述了相同功能。。
@ -129,7 +129,7 @@ reversedNames = names.sorted(by: { $0 > $1 } )
reversedNames = names.sorted(by: >)
```
更多关于运算符方法的内容请查看 [运算符方法](./26_Advanced_Operators.md#operator_methods)。
更多关于运算符方法的内容请查看[运算符方法](./26_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 使用捕获列表来打破这种循环强引用。更多信息,请参考[闭包引起的循环强引用](./23_Automatic_Reference_Counting.md#strong_reference_cycles_for_closures)。
## 闭包是引用类型 {#closures-are-reference-types}
@ -397,7 +397,7 @@ serve(customer: customersInLine.remove(at: 0))
>
> 过度使用 `autoclosures` 会让你的代码变得难以理解。上下文和函数名应该能够清晰地表明求值是被延迟执行的。
如果你想让一个自动闭包可以“逃逸”,则应该同时使用 `@autoclosure``@escaping` 属性。`@escaping` 属性的讲解见上面的 [逃逸闭包](#escaping_closures)。
如果你想让一个自动闭包可以“逃逸”,则应该同时使用 `@autoclosure``@escaping` 属性。`@escaping` 属性的讲解见上面的[逃逸闭包](#escaping_closures)。
```swift
// customersInLine i= ["Barry", "Daniella"]

View File

@ -8,7 +8,7 @@
在 Swift 中枚举类型是一等first-class类型。它们采用了很多在传统上只被类class所支持的特性例如计算属性computed properties用于提供枚举值的附加信息实例方法instance methods用于提供和枚举值相关联的功能。枚举也可以定义构造函数initializers来提供一个初始值可以在原始实现的基础上扩展它们的功能还可以遵循协议protocols来提供标准的功能。
想了解更多相关信息,请参见 [属性](./10_Properties.md)[方法](./11_Methods.md)[构造过程](./14_Initialization.md)[扩展](./20_Extensions.md)[协议](./21_Protocols.md)。
想了解更多相关信息,请参见[属性](./10_Properties.md)[方法](./11_Methods.md)[构造过程](./14_Initialization.md)[扩展](./20_Extensions.md)[协议](./21_Protocols.md)。
## 枚举语法 {#enumeration-syntax}
@ -84,7 +84,7 @@ case .west:
……以此类推。
正如在 [控制流](./05_Control_Flow.md) 中介绍的那样,在判断一个枚举类型的值时,`switch` 语句必须穷举所有情况。如果忽略了 `.west` 这种情况,上面那段代码将无法通过编译,因为它没有考虑到 `CompassPoint` 的全部成员。强制穷举确保了枚举成员不会被意外遗漏。
正如在[控制流](./05_Control_Flow.md)中介绍的那样,在判断一个枚举类型的值时,`switch` 语句必须穷举所有情况。如果忽略了 `.west` 这种情况,上面那段代码将无法通过编译,因为它没有考虑到 `CompassPoint` 的全部成员。强制穷举确保了枚举成员不会被意外遗漏。
当不需要匹配每个枚举成员的时候,你可以提供一个 `default` 分支来涵盖所有未明确处理的枚举成员:
@ -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)
> 原始值构造器是一个可失败构造器,因为并不是每一个原始值都有与之对应的枚举成员。更多信息请参见[可失败构造器](../chapter3/05_Declarations.html#failable_initializers)
如果你试图寻找一个位置为 `11` 的行星,通过原始值构造器返回的可选 `Planet` 值将是 `nil`

View File

@ -47,7 +47,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`),以便和类型名区分。
以下是定义结构体和定义类的示例:

View File

@ -10,7 +10,7 @@
简单来说,一个存储属性就是存储在特定类或结构体实例里的一个常量或变量。存储属性可以是*变量存储属性*(用关键字 `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` 的结构体,该结构体用于描述整数的区间,且这个范围值在被创建后不能被修改。
@ -174,28 +174,6 @@ 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 的计算属性叫*只读计算属性*。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。
@ -224,7 +202,7 @@ print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,即使新值和当前值相同的时候也不例外。
你可以为除了延时加载存储属性之外的其他存储属性添加属性观察器,你也可以在子类中通过重写属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。你不必为非重写的计算属性添加属性观察器,因为你可以直接通过它的 setter 监控和响应值的变化。属性重写请参考 [重写](./13_Inheritance.md#overriding)。
你可以为除了延时加载存储属性之外的其他存储属性添加属性观察器,你也可以在子类中通过重写属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。你不必为非重写的计算属性添加属性观察器,因为你可以直接通过它的 setter 监控和响应值的变化。属性重写请参考[重写](./13_Inheritance.md#overriding)。
可以为属性添加其中一个或两个观察器:
@ -239,7 +217,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` 的类,用来统计一个人步行时的总步数。这个类可以跟计步器或其他日常锻炼的统计装置的输入数据配合使用。
@ -278,7 +256,7 @@ 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 参数详细的介绍,请参考[输入输出参数](../chapter3/05_Declarations.html#in-out_parameters)
## 全局变量和局部变量 {#global-and-local-variables}
@ -290,7 +268,7 @@ stepCounter.totalSteps = 896
> 注意
>
> 全局的常量或变量都是延迟计算的,跟 [延时加载存储属性](#lazy_stored_properties) 相似,不同的地方在于,全局的常量或变量不需要标记 `lazy` 修饰符。
> 全局的常量或变量都是延迟计算的,跟[延时加载存储属性](#lazy_stored_properties)相似,不同的地方在于,全局的常量或变量不需要标记 `lazy` 修饰符。
>
> 局部范围的常量和变量从不延迟计算。

View File

@ -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` 结构体定义了一个可变方法 `moveByxy :)` 来移动 `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` 结构体定义了一个可变方法 `moveByxy :)` 来移动 `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”
```

View File

@ -72,7 +72,7 @@ numberOfLegs["bird"] = 2
## 下标选项 {#subscript-options}
下标可以接受任意数量的入参,并且这些入参可以是任意类型。下标的返回值也可以是任意类型。下标可以使用可变参数,并且可以提供默认参数数值,但不能使用输入输出参数。
下标可以接受任意数量的入参,并且这些入参可以是任意类型。下标的返回值也可以是任意类型。下标可以使用变量参数和可变参数,但不能使用输入输出参数,也不能给参数设置默认值
一个类或结构体可以根据自身需要提供多个下标实现,使用下标时将通过入参的数量和类型进行区分,自动匹配合适的下标,这就是*下标的重载*。
@ -139,18 +139,4 @@ func indexIsValid(row: Int, column: Int) -> Bool {
```swift
let someValue = matrix[2, 2]
// 断言将会触发,因为 [2, 2] 已经超过了 matrix 的范围
```
## 类型下标{#type-subscripts}
正如上节所述,实例下标是在特定类型的一个实例上调用的下标。你也可以定义一种在这个类型本身上调用的下标。这种下标的类型被称作类型下标。你可以通过在 `subscript` 关键字之前写下 `static` 关键字的方式来表示一个类型下标。类可以使用 `class` 关键字来允许子类重写父类中对那个下标的实现。下面的例子展示了如何定义和调用一个类型下标:
```
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
static subscript(n: Int) -> Planet {
return Planet(rawValue: n)!
}
}
let mars = Planet[4]
print(mars)
```
```

View File

@ -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}
你可以通过重写属性为一个继承来的属性添加属性观察器。这样一来,无论被继承属性原本是如何实现的,当其属性值发生改变时,你就会被通知到。关于属性观察器的更多内容,请看[属性观察器](../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 。这样的类是不可被继承的,试图继承这样的类会导致编译报错。

View File

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

View File

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

View File

@ -156,7 +156,7 @@ class Address {
## 通过可选链式调用访问属性 {#accessing-properties-through-optional-chaining}
正如 [使用可选链式调用代替强制展开](#optional_chaining_as_an_alternative_to_forced_unwrapping) 中所述,可以通过可选链式调用在一个可选值上访问它的属性,并判断访问是否成功。
正如[使用可选链式调用代替强制展开](#optional_chaining_as_an_alternative_to_forced_unwrapping)中所述,可以通过可选链式调用在一个可选值上访问它的属性,并判断访问是否成功。
使用前面定义过的类,创建一个 `Person` 实例,然后像之前一样,尝试访问 `numberOfRooms` 属性:
@ -212,7 +212,7 @@ func printNumberOfRooms() {
}
```
这个方法没有返回值。然而,没有返回值的方法具有隐式的返回类型 `Void`,如 [无返回值函数](./06_Functions.md#functions_without_return_values) 中所述。这意味着没有返回值的方法也会返回 `()`,或者说空的元组。
这个方法没有返回值。然而,没有返回值的方法具有隐式的返回类型 `Void`,如[无返回值函数](./06_Functions.md#functions_without_return_values)中所述。这意味着没有返回值的方法也会返回 `()`,或者说空的元组。
如果在可选值上通过可选链式调用来调用这个方法,该方法的返回类型会是 `Void?`,而不是 `Void`,因为通过可选链式调用得到的返回值都是可选的。这样我们就可以使用 `if` 语句来判断能否成功调用 `printNumberOfRooms()` 方法,即使方法本身没有定义返回值。通过判断返回值是否为 `nil` 可以判断调用是否成功:
@ -225,7 +225,7 @@ if john.residence?.printNumberOfRooms() != nil {
// 打印“It was not possible to print the number of rooms.”
```
同样的,可以据此判断通过可选链式调用为属性赋值是否成功。在上面的 [通过可选链式调用访问属性](#accessing_properties_through_optional_chaining) 的例子中,我们尝试给 `john.residence` 中的 `address` 属性赋值,即使 `residence``nil`。通过可选链式调用给属性赋值会返回 `Void?`,通过判断返回值是否为 `nil` 就可以知道赋值是否成功:
同样的,可以据此判断通过可选链式调用为属性赋值是否成功。在上面的[通过可选链式调用访问属性](#accessing_properties_through_optional_chaining)的例子中,我们尝试给 `john.residence` 中的 `address` 属性赋值,即使 `residence``nil`。通过可选链式调用给属性赋值会返回 `Void?`,通过判断返回值是否为 `nil` 就可以知道赋值是否成功:
```swift
if (john.residence?.address = someAddress) != nil {

View File

@ -114,7 +114,7 @@ func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
}
```
上例中,`buyFavoriteSnack(person:vendingMachine:)` 函数会查找某人最喜欢的零食,并通过调用 `vend(itemNamed:)` 方法来尝试为他们购买。因为 `vend(itemNamed:)` 方法能抛出错误,所以在调用的时候在它前面加了 `try` 关键字。
上例中,`buyFavoriteSnack(person:vendingMachine:)` 函数会查找某人最喜欢的零食,并通过调用 `vend(itemNamed:)` 方法来尝试为他们购买。因为 `vend(itemNamed:)` 方法能抛出错误,所以在调用的时候在它前面加了 `try` 关键字。
`throwing` 构造器能像 `throwing` 函数一样传递错误。例如下面代码中的 `PurchasedSnack` 构造器在构造过程中调用了 throwing 函数,并且通过传递到它的调用者来处理这些错误。

View File

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

View File

@ -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` 的名字尽可能的短,因为它们的名字可以由定义它们的上下文来限定。

View File

@ -242,7 +242,7 @@ class SomeSubClass: SomeSuperClass, SomeProtocol {
## 协议作为类型 {#protocols-as-types}
尽管协议本身并未实现任何功能,但是协议可以被当做一个功能完备的类型来使用。协议作为类型使用,有时被称作「存在类型」,这个名词来自「存在着一个类型 T该类型遵循协议 T」。
尽管协议本身并未实现任何功能,但是协议可以被当做一个功能完备的类型来使用。
协议可以像其他普通类型一样使用,使用场景如下:
@ -272,7 +272,7 @@ class Dice {
例子中定义了一个 `Dice` 类,用来代表桌游中拥有 N 个面的骰子。`Dice` 的实例含有 `sides``generator` 两个属性,前者是整型,用来表示骰子有几个面,后者为骰子提供一个随机数生成器,从而生成随机点数。
`generator` 属性的类型为 `RandomNumberGenerator`,因此任何遵循了 `RandomNumberGenerator` 协议的类型的实例都可以赋值给 `generator`,除此之外并无其他要求。并且由于其类型是 `RandomNumberGenerator`,在 `Dice` 类中与 `generator` 交互的代码,必须适用于所有 `generator` 实例都遵循的方法。这句话的意思是不能使用由 `generator` 底层类型提供的任何方法或属性。但是你可以通过向下转型,从协议类型转换成底层实现类型,比如从父类向下转型为子类。请参考 [向下转型](./18_Type_Casting#downcasting)。
`generator` 属性的类型为 `RandomNumberGenerator`,因此任何遵循了 `RandomNumberGenerator` 协议的类型的实例都可以赋值给 `generator`,除此之外并无其他要求。
`Dice` 类还有一个构造器,用来设置初始状态。构造器有一个名为 `generator`,类型为 `RandomNumberGenerator` 的形参。在调用构造方法创建 `Dice` 的实例时,可以传入任何遵循 `RandomNumberGenerator` 协议的实例给 `generator`
@ -656,7 +656,7 @@ beginConcert(in: seattle)
## 检查协议一致性 {#checking-for-protocol-conformance}
你可以使用 [类型转换](./18_Type_Casting.md) 中描述的 `is``as` 操作符来检查协议一致性,即是否符合某协议,并且可以转换到指定的协议类型。检查和转换协议的语法与检查和转换类型是完全一样的:
你可以使用[类型转换](./18_Type_Casting.md)中描述的 `is``as` 操作符来检查协议一致性,即是否符合某协议,并且可以转换到指定的协议类型。检查和转换协议的语法与检查和转换类型是完全一样的:
* `is` 用来检查实例是否符合某个协议,若符合则返回 `true`,否则返回 `false`
* `as?` 返回一个可选值,当实例符合某个协议时,返回类型为协议类型的可选值,否则返回 `nil`
@ -733,7 +733,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 +772,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)
在调用 `increment(forCount:)` 方法后,`Int?` 型的返回值通过可选绑定解包并赋值给常量 `amount`。如果可选值确实包含一个数值,也就是说,数据源和方法都存在,数据源方法返回了一个有效值。之后便将解包后的 `amount` 加到 `count` 上,增量操作完成。
@ -881,7 +881,7 @@ extension PrettyTextRepresentable {
### 为协议扩展添加限制条件 {#adding-constraints-to-protocol-extensions}
在扩展协议的时候,可以指定一些限制条件,只有遵循协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用 `where` 子句来描述,正如 [泛型 Where 子句](./22_Generics.md#where_clauses) 中所描述的。
在扩展协议的时候,可以指定一些限制条件,只有遵循协议的类型满足这些限制条件时,才能获得协议扩展提供的默认实现。这些限制条件写在协议名之后,使用 `where` 子句来描述,正如[泛型 Where 子句](./22_Generics.md#where_clauses)中所描述的。
例如,你可以扩展 `Collection` 协议,适用于集合中的元素遵循了 `Equatable` 协议的情况。通过限制集合元素遵 `Equatable` 协议, 作为标准库的一部分, 你可以使用 `==``!=` 操作符来检查两个元素的等价性和非等价性。

View File

@ -1,6 +1,6 @@
# 泛型
*泛型代码*让你能根据自定义的需求,编写出适用于任意类型的、灵活可复用的函数及类型。你可避免编写重复的代码,而是用一种清晰抽象的方式来表达代码的意图。
*泛型代码*让你能根据自定义的需求,编写出适用于任意类型的、灵活可复用的函数及类型。你可避免编写重复的代码,用一种清晰抽象的方式来表达代码的意图。
泛型是 Swift 最强大的特性之一,很多 Swift 标准库是基于泛型代码构建的。实际上,即使你没有意识到,你也一直在*语言指南*中使用泛型。例如Swift 的 `Array``Dictionary` 都是泛型集合。你可以创建一个 `Int` 类型数组,也可创建一个 `String` 类型数组,甚至可以是任意其他 Swift 类型的数组。同样,你也可以创建一个存储任意指定类型的字典,并对该类型没有限制。
@ -16,7 +16,7 @@ func swapTwoInts(_ a: inout Int, _ b: inout Int) {
}
```
这个函数使用输入输出参数(`inout`)来交换 `a``b` 的值,具体请参考 [输入输出参数](./06_Functions.md#in_out_parameters)。
这个函数使用输入输出参数(`inout`)来交换 `a``b` 的值,具体请参考[输入输出参数](./06_Functions.md#in_out_parameters)。
`swapTwoInts(_:_:)` 函数将 `b` 的原始值换成了 `a`,将 `a` 的原始值换成了 `b`,你可以调用这个函数来交换两个 `Int` 类型变量:
@ -226,8 +226,6 @@ if let topItem = stackOfStrings.topItem {
// 打印“The top item on the stack is tres.”
```
泛型类型的扩展,还可以包括类型扩展需要额外满足的条件,从而对类型添加新功能,这一部分将在[具有泛型 Where 子句的扩展](#extensions-with-a-generic-where-clause)中进行讨论。
## 类型约束 {#type-constraints}
`swapTwoValues(_:_:)` 函数和 `Stack` 适用于任意类型。不过,如果能对泛型函数或泛型类型中添加特定的*类型约束*,这将在某些情况下非常有用。类型约束指定类型参数必须继承自指定类、遵循特定的协议或协议组合。
@ -410,7 +408,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 协议。你可以通过一个空扩展来实现这点,正如通过扩展采纳协议中的描述:
@ -435,7 +433,7 @@ protocol Container {
要遵守 `Container` 协议,`Item` 类型也必须遵守 `Equatable` 协议。
### 在关联类型约束里使用协议 {#using-a-protocol-in-its-associated-types-constraints}
### 在关联类型约束里使用协议 {#using-a-protocol-in-its-associated-types-constraints}
协议可以作为它自身的要求出现。例如,有一个协议细化了 `Container` 协议,添加了一个` suffix(_:)` 方法。`suffix(_:)` 方法返回容器中从后往前给定数量的元素,并把它们存储在一个 `Suffix` 类型的实例里。
@ -446,9 +444,9 @@ protocol SuffixableContainer: Container {
}
```
在这个协议里,`Suffix` 是一个关联类型,就像上边例子中 `Container``Item` 类型一样。`Suffix` 拥有两个约束:它必须遵循 `SuffixableContainer` 协议(就是当前定义的协议),以及它的 `Item` 类型必须是和容器里的 `Item` 类型相同。`Item` 的约束是一个 `where` 分句,它在下面[有泛型 Where 子句的扩展](#extensions-with-a-generic-where-clause)中有讨论。
在这个协议里,`Suffix` 是一个关联类型,就像上边例子中 `Container``Item` 类型一样。`Suffix` 拥有两个约束:它必须遵循 `SuffixableContainer` 协议(就是当前定义的协议),以及它的 `Item` 类型必须是和容器里的 `Item` 类型相同。`Item` 的约束是一个 `where` 分句,它在下面有泛型 `Where` 分句的扩展中有讨论。
这是上面 [泛型类型](#generic-types) 中 `Stack` 类型的扩展,它遵循了 SuffixableContainer 协议:
这是上面 [强引用循环闭包](./23_Automatic_Reference_Counting.md#strong_reference_cycles_for_closures) 中 `Stack` 类型的扩展,它遵循了 SuffixableContainer 协议:
```swift
extension Stack: SuffixableContainer {
@ -486,7 +484,7 @@ extension IntStack: SuffixableContainer {
## 泛型 Where 语句 {#where-clauses}
[类型约束](#type_constraints) 让你能够为泛型函数、下标、类型的类型参数定义一些强制要求。
[类型约束](#type_constraints)让你能够为泛型函数、下标、类型的类型参数定义一些强制要求。
对关联类型添加约束通常是非常有用的。你可以通过定义一个泛型 `where` 子句来实现。通过泛型 `where` 子句让关联类型遵从某个特定的协议,以及某个特定的类型参数和关联类型必须类型相同。你可以通过将 `where` 关键字紧跟在类型参数列表后面来定义 `where` 子句,`where` 子句后跟一个或者多个针对关联类型的约束,以及一个或多个类型参数和关联类型间的相等关系。你可以在函数体或者类型的大括号之前添加 `where` 子句。

File diff suppressed because it is too large Load Diff

View File

@ -87,7 +87,7 @@ fileprivate func someFilePrivateFunction() {}
private func somePrivateFunction() {}
```
除非专门指定,否则实体默认的访问级别为 `internal`,可以查阅 [默认访问级别](#default_access_levels) 这一节。这意味着在不使用修饰符显式声明访问级别的情况下,`SomeInternalClass``someInternalConstant` 仍然拥有隐式的 `internal`
除非专门指定,否则实体默认的访问级别为 `internal`,可以查阅[默认访问级别](#default_access_levels)这一节。这意味着在不使用修饰符显式声明访问级别的情况下,`SomeInternalClass``someInternalConstant` 仍然拥有隐式的 `internal`
```swift
class SomeInternalClass {} // 隐式 internal
@ -147,7 +147,7 @@ func someFunction() -> (SomeInternalClass, SomePrivateClass) {
}
```
我们可以看到,这个函数的返回类型是一个元组,该元组中包含两个自定义的类(可查阅 [自定义类型](#custom_types))。其中一个类的访问级别是 `internal`,另一个的访问级别是 `private`,所以根据元组访问级别的原则,该元组的访问级别是 `private`(元组的访问级别与元组中访问级别最低的类型一致)。
我们可以看到,这个函数的返回类型是一个元组,该元组中包含两个自定义的类(可查阅[自定义类型](#custom_types))。其中一个类的访问级别是 `internal`,另一个的访问级别是 `private`,所以根据元组访问级别的原则,该元组的访问级别是 `private`(元组的访问级别与元组中访问级别最低的类型一致)。
因为该函数返回类型的访问级别是 `private`,所以你必须使用 `private` 修饰符,明确指定该函数的访问级别:
@ -188,7 +188,7 @@ public enum CompassPoint {
此外,你可以在符合当前访问级别的条件下重写任意类成员(方法、属性、构造器、下标等)。
可以通过重写为继承来的类成员提供更高的访问级别。下面的例子中,类 `A` 的访问级别是 `public`,它包含一个方法 `someMethod()`,访问级别为 `fileprivate`。类 `B` 继承自类 `A`,访问级别为 `internal`,但是在类 `B` 中重写了类 `A` 中访问级别为 `fileprivate` 的方法 `someMethod()`,并重新指定为 `internal` 级别。通过这种方式,我们就可以将某类中 `fileprivate` 级别的类成员重新指定为更高的访问级别,以便其他人使用:
可以通过重写为继承来的类成员提供更高的访问级别。下面的例子中,类 `A` 的访问级别是 `public`,它包含一个方法 `someMethod()`,访问级别为 `private`。类 `B` 继承自类 `A`,访问级别为 `internal`,但是在类 `B` 中重写了类 `A` 中访问级别为 `private` 的方法 `someMethod()`,并重新指定为 `internal` 级别。通过这种方式,我们就可以将某类中 `private` 级别的类成员重新指定为更高的访问级别,以便其他人使用:
```swift
public class A {
@ -200,7 +200,7 @@ internal class B: A {
}
```
我们甚至可以在子类中,用子类成员去访问访问级别更低的父类成员,只要这一操作在相应访问级别的限制范围内(也就是说,在同一源文件中访问父类 `fileprivate` 级别的成员,在同一模块内访问父类 `internal` 级别的成员):
我们甚至可以在子类中,用子类成员去访问访问级别更低的父类成员,只要这一操作在相应访问级别的限制范围内(也就是说,在同一源文件中访问父类 `private` 级别的成员,在同一模块内访问父类 `internal` 级别的成员):
```swift
public class A {
@ -282,13 +282,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` 访问级别的无参数构造器。

View File

@ -1,6 +1,6 @@
# 高级运算符
除了之前介绍过的 [基本运算符](./02_Basic_Operators.md)Swift 还提供了数种可以对数值进行复杂运算的高级运算符。它们包含了在 C 和 Objective-C 中已经被大家所熟知的位运算符和移位运算符。
除了之前介绍过的[基本运算符](./02_Basic_Operators.md)Swift 还提供了数种可以对数值进行复杂运算的高级运算符。它们包含了在 C 和 Objective-C 中已经被大家所熟知的位运算符和移位运算符。
与 C 语言中的算术运算符不同Swift 中的算术运算符默认是不会溢出的。所有溢出行为都会被捕获并报告为错误。如果想让系统允许溢出行为,可以选择使用 Swift 中另一套默认支持溢出的运算符,比如溢出加法运算符(`&+`)。所有的这些溢出运算符都是以 `&` 开头的。
@ -210,7 +210,7 @@ unsignedOverflow = unsignedOverflow &- 1
![Art/overflowUnsignedSubtraction_2x.png](https://docs.swift.org/swift-book/_images/overflowUnsignedSubtraction_2x.png)
溢出也会发生在有符号整型上。针对有符号整型的所有溢出加法或者减法运算都是按位运算的方式执行的,符号位也需要参与计算,正如 [按位左移、右移运算符](#bitwise_left_and_right_shift_operators) 所描述的。
溢出也会发生在有符号整型上。针对有符号整型的所有溢出加法或者减法运算都是按位运算的方式执行的,符号位也需要参与计算,正如[按位左移、右移运算符](#bitwise_left_and_right_shift_operators)所描述的。
```Swift
var signedOverflow = Int8.min
@ -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)。
> 注意
>
@ -414,7 +414,7 @@ if twoThreeFour == anotherTwoThreeFour {
## 自定义运算符 {#custom-operators}
除了实现标准运算符,在 Swift 中还可以声明和实现*自定义运算符*。可以用来自定义运算符的字符列表请参考 [运算符](../chapter3/02_Lexical_Structure.html#operators)。
除了实现标准运算符,在 Swift 中还可以声明和实现*自定义运算符*。可以用来自定义运算符的字符列表请参考[运算符](../chapter3/02_Lexical_Structure.html#operators)。
新的运算符要使用 `operator` 关键字在全局作用域内进行定义,同时还要指定 `prefix``infix` 或者 `postfix` 修饰符:
@ -440,7 +440,7 @@ let afterDoubling = +++toBeDoubled
### 自定义中缀运算符的优先级 {#precedence-and-associativity-for-custom-infix-operators}
每个自定义中缀运算符都属于某个优先级组。优先级组指定了这个运算符相对于其他中缀运算符的优先级和结合性。[优先级和结合性](#precedence_and_associativity) 中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。
每个自定义中缀运算符都属于某个优先级组。优先级组指定了这个运算符相对于其他中缀运算符的优先级和结合性。[优先级和结合性](#precedence_and_associativity)中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。
而没有明确放入某个优先级组的自定义中缀运算符将会被放到一个默认的优先级组内,其优先级高于三元运算符。
@ -459,7 +459,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)。而更多关于优先级组以及自定义操作符和优先级组的语法,请参考[运算符声明](./06_Declarations.md#operator_declaration)。
> 注意
>

View File

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

View File

@ -162,9 +162,9 @@ Swift 的*“词法结构lexical structure”* 描述了能构成该语言
true // 布尔值字面量
```
字面量本身并不包含类型信息。事实上,一个字面量会被解析为拥有无限的精度,然后 Swift 的类型推导会尝试去推导出这个字面量的类型。比如,在 `let x: Int8 = 42` 这个声明中Swift 使用了显式类型注`: Int8`)来推导出 `42` 这个整数字面量的类型是 `Int8`。如果没有可用的类型信息, Swift 则会从标准库中定义的字面量类型中推导出一个默认的类型。整数字面量的默认类型是 `Int`,浮点数字面量的默认类型是 `Double`,字符串字面量的默认类型是 `String`,布尔值字面量的默认类型是 `Bool`。比如,在 `let str = "Hello, world"` 这个声明中,字符串 `"Hello, world"` 的默认推导类型就是 `String`。
字面量本身并不包含类型信息。事实上,一个字面量会被解析为拥有无限的精度,然后 Swift 的类型推导会尝试去推导出这个字面量的类型。比如,在 `let x: Int8 = 42` 这个声明中Swift 使用了显式类型注(`: Int8`)来推导出 `42` 这个整数字面量的类型是 `Int8`。如果没有可用的类型信息, Swift 则会从标准库中定义的字面量类型中推导出一个默认的类型。整数字面量的默认类型是 `Int`,浮点数字面量的默认类型是 `Double`,字符串字面量的默认类型是 `String`,布尔值字面量的默认类型是 `Bool`。比如,在 `let str = "Hello, world"` 这个声明中,字符串 `"Hello, world"` 的默认推导类型就是 `String`。
当为一个字面量值指定了类型注的时候,这个标注的类型必须能通过这个字面量值实例化。也就是说,这个类型必须符合这些 Swift 标准库协议中的一个:整数字面量的 `IntegerLiteralConvertible` 协议、浮点数字面量的 `FloatingPointLiteralConvertible` 协议、字符串字面量的 `StringLiteralConvertible` 协议以及布尔值字面量的 `BooleanLiteralConvertible` 协议。比如,`Int8` 符合 `IntegerLiteralConvertible` 协议,因此它能在 `let x: Int8 = 42` 这个声明中作为整数字面量 `42` 的类型注
当为一个字面量值指定了类型注的时候,这个标注的类型必须能通过这个字面量值实例化。也就是说,这个类型必须符合这些 Swift 标准库协议中的一个:整数字面量的 `IntegerLiteralConvertible` 协议、浮点数字面量的 `FloatingPointLiteralConvertible` 协议、字符串字面量的 `StringLiteralConvertible` 协议以及布尔值字面量的 `BooleanLiteralConvertible` 协议。比如,`Int8` 符合 `IntegerLiteralConvertible` 协议,因此它能在 `let x: Int8 = 42` 这个声明中作为整数字面量 `42` 的类型注。
> 字面量语法
>

View File

@ -1,526 +1,468 @@
# 类型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}
> 类型语法
>
> *类型* → [函数类型](#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}
*元组类型*是使用括号括起来的零个或多个类型,类型间用逗号隔开。
你可以使用元组类型作为一个函数的返回类型,这样就可以使函数返回多个值。你也可以命名元组类型中的元素,然后用这些名字来引用每个元素的值。元素的名字由一个标识符紧跟一个冒号 `(:)` 组成。[函数和多返回值](../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-h}
*函数类型*表示一个函数、方法或闭包的类型,它由参数类型和返回值类型组成,中间用箭头(`->`)隔开:
> `参数类型`->`返回值类型`
*参数类型*是由逗号间隔的类型列表。由于*返回值类型*可以是元组类型,所以函数类型支持多返回值的函数与方法。
你可以对参数类型为 `() -> 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-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` 类型的详细讨论,请参阅 [数组](../chapter2/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` 类型的详细讨论,请参阅 [字典](../chapter2/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`,不会执行任何操作,因此也就没有运行错误产生。
更多细节以及更多如何使用可选类型的例子,请参阅 [可选类型](../chapter2/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`,就不会执行任何操作,因此也不会产生运行错误。
关于隐式解析可选类型的更多细节,请参阅 [隐式解析可选类型](../chapter2/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}
*类型继承子句*被用来指定一个命名型类型继承自哪个类、采纳哪些协议。类型继承子句开始于冒号 `:`,其后是类型标识符列表。
类可以继承自单个超类,并遵循任意数量的协议。当定义一个类时,超类的名字必须出现在类型标识符列表首位,然后跟上该类需要遵循的任意数量的协议。如果一个类不是从其它类继承而来,那么列表可以以协议开头。关于类继承更多的讨论和例子,请参阅 [继承](../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)
>
## 类型推断
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 标准库中是使用结构体来定义和实现的。因为它们是命名型类型,因此你可以按照 [扩展](../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 中的类型推断在单独的表达式或语句上进行。这意味着所有用于类型推断的信息必须可以从表达式或其某个子表达式的类型检查中获取到。

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

View File

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

View File

@ -4,7 +4,7 @@
Swift 中的模式分为两类:一种能成功匹配任何类型的值,另一种在运行时匹配某个特定值时可能会失败。
第一类模式用于解构简单变量、常量和可选绑定中的值。此类模式包括通配符模式、标识符模式,以及包含前两种模式的值绑定模式和元组模式。你可以为这类模式指定一个类型注,从而限制它们只能匹配某种特定类型的值。
第一类模式用于解构简单变量、常量和可选绑定中的值。此类模式包括通配符模式、标识符模式,以及包含前两种模式的值绑定模式和元组模式。你可以为这类模式指定一个类型注,从而限制它们只能匹配某种特定类型的值。
第二类模式用于全模式匹配,这种情况下你试图匹配的值在运行时可能不存在。此类模式包括枚举用例模式、可选模式、表达式模式和类型转换模式。你在 `switch` 语句的 `case` 标签中,`do` 语句的 `catch` 子句中,或者在 `if``while``guard``for-in` 语句的 `case` 条件句中使用这类模式。
@ -12,13 +12,13 @@ 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)
>
> *模式* → [*元组模式*](#tuple-pattern) [*类型注*](03_Types.md#type-annotation)<sub>可选</sub>
> *模式* → [*元组模式*](#tuple-pattern) [*类型注*](03_Types.md#type-annotation)<sub>可选</sub>
>
> *模式* → [*枚举用例模式*](#enum-case-pattern)
>
@ -91,7 +91,7 @@ case let (x, y):
## 元组模式 {#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` 是一个表达式模式:
@ -228,6 +228,7 @@ default:
```swift
// 重载 ~= 运算符对字符串和整数进行比较
func ~=(pattern: String, value: Int) -> Bool {
>
return pattern == "\(value)"
}

View File

@ -1,136 +1,138 @@
# 泛型参数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 语言的泛型概述,请参阅 [泛型](../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)
>

View File

@ -12,16 +12,11 @@ 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)
- [Khala-wan](https://github.com/Khala-wan)
- [Nemocdz](https://github.com/Nemocdz)
- [numbbbbb](https://github.com/numbbbbb)
- [pmst](https://github.com/colourful987)
- [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 主要贡献者
@ -148,3 +143,4 @@ Swift 官方文档中文翻译由 [numbbbbb](https://github.com/numbbbbb) 发起
- [zqp](https://github.com/zqp)
- [成都老码团队翻译组-Arya](http://weibo.com/littlekok/)
- [成都老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 576 KiB

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB