可选类型完美解决占位问题
最后更新于:2022-04-01 05:05:27
> 翻译:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
校对:[老码团队翻译组-Ayra](http://weibo.com/littlekok/)
# 可选类型完美解决占位问题
可选类型是Swift中新引入的,功能很强大。在这篇博文里讨论的,是在Swift里,如何通过可选类型来保证强类型的安全性。作为例子,我们来创建一个Objective-C API的Swift版本,但实际上Swift本身并不需要这样的API。
#### 为Dictionary增加objectsForKeys函数
在Objective-C中,`NSDictionary`有一个方法`-objectsForKeys:NoFoundMarker:`, 这个方法需要一个`NSArray`数组作为键值参数,然后返回一个包含相关值的数组。文档里写到:"返回数组中的第N个值,和输入数组中的第N个值相对应",那如果有某个键值在字典里不存在呢?于是就有了`notFoundMarker`作为返回提示。比如第三个键值没有找到,那么在返回数组中第三个值就是这个`notFoundMarker`,而不是字典中的第三个值,但是这个值只是用来提醒原字典中没有找到对应值,但在返回数组中该元素存在,且用`notFoundMarker`作为占位符,因为这个对象不能直接使用,所以在Foundation框架中有个专门的类处理这个情况:`NSNull`。
在Swift中,`Dictionary`类没有类似`objectsForKeys`的函数,为了说明问题,我们动手加一个,并且使其成为操作字典值的通用方法。我们可以用`extension`来实现:
~~~
extension Dictionary{
func valuesForKeys(keys:[K], notFoundMarker: V )->[V]{
//具体实现代码后面会写到
}
}
~~~
以上就是我们实现的Swift版本,这个和Objective-C版本有很大区别。在Swift中,因为其强类型的原因限制了返回的结果数组只能包含单一类型的元素,所以我们不能放`NSNull`在字符串数组中,但是,Swift有更好的选择,我们可以返回一个可选类型数据。我们所有的值都封包在可选类型中,而不是`NSNull`, 我们只用`nil`就可以了。
~~~
extension Dictionary{
func valuesForKeys(keys: [Key]) -> [Value?] {
var result = [Value?]()
result.reserveCapacity(keys.count)
for key in keys{
result.append(self[key])
}
return result
}
}
~~~
[]()
#### Swift中更简便的方法
小伙伴们可能会问,为什么Swift中不需要实现这么一个API呢?其实其有更简单的实现,如下面代码所示:
~~~
extension Dictionary {
func valuesForKeys(keys: [Key]) -> [Value?] {
return keys.map { self[$0] }
}
}
~~~
上述方式实现的功能和最开始的方法实现的功能相同,虽然核心的功能是封装了`map`的调用,这个例子也说明了为什么Swift没有提供轻量级的API接口,因为小伙伴们简单的调用`map`就可以实现。
接下来,我们实验几个例子:
~~~
var dic: Dictionary = [ "1": 2, "3":3, "4":5 ]
var t = dic.valuesForKeys(["1", "4"])
//结果为:[Optional(2), Optional(5)]
var t = dict.valuesForKeys(["3", "9"])
// 结果为:[Optional(3), nil]
t = dic.valuesForKeys([])
//结果为:[]
~~~
[]()
#### 内嵌可选类型
现在,如果我们为每一个结果调用`last`方法,看下结果如何?
~~~
var dic: Dictionary = [ "1": 2, "3":3, "4":5 ]
var t = dic.valuesForKeys(["1", "4"]).last //结果为:Optional(Optional(5))
// Optional(Optional("Ching"))
var t = dict.valuesForKeys(["3", "9"]).last
// 结果为:Optional(nil)
var t = dict.valuesForKeys([]).last
// 结果为:nil
~~~
小伙伴们立马迷糊了,为什么会出现两层包含的可选类型呢?,特别对第二种情况的`Optional(nil)`,这是什么节奏?
我们回过头看看`last`属性的定义:
~~~
var last:T? { get }
~~~
很明显`last`属性的类型是数组元素类型的可选类型,这种情况下,因为元素类型是`(String?)`,那么再结合返回的类型,于是其结果就是`String??`了,这就是所谓的嵌套可选类型。但嵌套可选类型本质是什么意思呢?
如果在Objective-C中重新调用上述方法,我们将使用`NSNull`作为占位符,Objective-C的调用语法如下所示:
~~~
[dict valuesForKeys:@[@"1", @"4"] notFoundMarker:[NSNull null]].lastObject
// 5
[dict valuesForKeys:@[@"1", @"3"] notFoundMarker:[NSNull null]].lastObject
// NSNull
[dict valuesForKeys:@[] notFoundMarker:[NSNull null]].lastObject
// nil
~~~
不管是Swift版本还是Objective-C版本,返回值为`nil`都意味数组是空的,所以它就没有最后一个元素。 但是如果返回是`Optional(nil)`或者Objective-C中的`NSNull`都表示数组中的最后一个元素存在,但是元素的内容是空的。在Objective-C中只能借助`NSNull`作为占位符来达到这个目的,但是Swift却可以语言系统类型的角度的实现。
[]()
#### 提供一个默认值
进一步封装,如果我字典中的某个或某些元素不存在,我们想提供一个默认值怎么办呢?实现方法很简单:
~~~
extension Dictionary {
func valuesForKeys( keys:[Key], notFoundMarker: Value)->[Value]{
return self.valueForKeys(kes).map{ $0 ?? notFoundMarker }
}
}
~~~
~~~
dict.valuesForKeys(["1", "5"], notFoundMarker: "Anonymous")
~~~
和Objective-C相比,其需要占位符来达到占位的目的,但是Swift却已经从语言类型系统的层面原生的支持了这种用法,同时提供了丰富的语法功能。这就是Swift可选类型的强大之处。同时注意上述例子中用到了空合运算符`??`。
本章节不是老码的原创,是老码认真的阅读了苹果的官方博客,自己的练习总结,如果小伙伴们费了吃奶的劲还是看不懂,请找度娘谷歌。还是看不懂?请到老码[官方微博](http://weibo.com/u/5241713117)咆哮。
##### 本文由翻译自Apple Swift Blog :[Optionals Case Study: valuesForKeys](https://developer.apple.com/swift/blog/?id=12)
访问控制和Protected
最后更新于:2022-04-01 05:05:25
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
校对:[老码团队翻译组-Jame](http://weibo.com/u/5241713117)
# 访问控制和protected
原文再续,书折第一回。
很多其他编程语言都有一种”protected“设定,可以限制某些类方法只能被它的子类所使用。
Swift支持了访问控制后,大家给我们的反馈都很不错。而有的开发者问我们:“为什么Swift没有类似protected的选项?”
**当我们在设计Swift访问控制的不同等级时,我们认为有两种主要场景:**
- 在一个APP里:隐藏某个类的私密细节。
- 在一个开源框架里:不让导入这个框架的APP,随便接触框架的内部实现细节。
上面的两种常见情况,对应着private和internal这两个等级。
而protected相当于把访问控制和继承特性混在一起,把访问控制的等级设定增加了一个维度,使之复杂化。即使设定了protected,子类还是可以通过新的公开方法、新的属性来接触到所谓“protected”了的API。另一方面,我们可以在各种地方重写一个方法,所谓的保护却没有提供优化机制。这种设定往往在做不必要的限制 一 protected允许了子类,但又禁止所有其他别的类(包括那些帮助子类实现某些功能的类)接触父类的成员。
有的开发者指出,apple的框架有时候也会把给子类用的API分隔出来。这时候protected不就有用了吗?我们研究后发现,这些方法一般属于下面两种情况:一是这些方法对子类以外的类没啥用,所以不需要严格保护(例如上面说的协助实现某些功能的类)。二是这些方法就是设计出来被重写,而不是直接用的。举个例子,`drawRect(_:)`就是在UIKit基础上使用的方法,但它不能在UIKit以外应用。
除此之外,如果有了protected,它要怎么样和extension相互作用呢?一个类的extension能接触它的protected成员吗?一个子类的extension可以接触父类的protected成员吗?extension声明的位置对访问控制等级有没有影响呢?(复杂到要哭了是不是?)
对访问控制的设计,也依循了Objective-C开发者(包括apple内外的)的常规做法。Objective-C方法和属性一般在.h头文件里声明,但也可以写在.m实现文件里。假如有一个公开的类,想把里面某些部分设为只有框架内可以获取时,开发者一般会创建另一个头文件给内部使用。以上三种访问级别,就对应了Swift里面的public,private和internal。
Swift的访问控制等级和继承无关,是单维度、非常清楚明了的。我们认为这样的模式更简洁,同时满足了最主要的需求:将一个类、或一个框架的实现细节隔离保护起来。这可能和你以前用过的不同,但我们鼓励你试试看。
本章节不是老码的原创,是老码认真的阅读了苹果的官方博客,自己的练习总结,如果小伙伴们费了吃奶的劲还是看不懂,请找度娘谷歌。还是看不懂?请到老码[官方微博](http://weibo.com/u/5241713117)咆哮。
##### 本文由翻译自Apple Swift Blog :[Access Control and Protected](#)
引用类型和值类型的恩怨
最后更新于:2022-04-01 05:05:22
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
校对:[老码团队翻译组-Jame](http://weibo.com/u/5241713117)
# Swift里的值类型与引用类型
### Swift里面的类型分为两种:
- **值类型(Value Types)**:每个实例都保留了一分独有的数据拷贝,一般以结构体 `(struct)`、`枚举(enum)` 或者`元组(tuple)`的形式出现。
- **引用类型(Reference Type)**:每个实例共享同一份数据来源,一般以`类(class)`的形式出现。
在这篇博文里面,我们会介绍两种类型各自的优点,以及应该怎么选择使用。
[]()
#### 值类型与引用类型的区别
值类型和引用类型最基本的分别在复制之后的结果。当一个值类型被复制的时候,相当于创造了一个完全独立的实例,这个实例保有属于自己的独有数据,数据不会受到其他实例的数据变化影响:
~~~
// 下面是一个值类型的例子
structS{ var data: Int = -1 }
var a = S()
var b = a // b是a的拷贝
a.data = 42 // 更改a的数据,b的不受影响
println("\(a.data), \(b.data)") // 输出结果 "42, -1"
~~~
值类型就好像身份证复印件一样,复印出来之后,修改原件上面的内容,复印件上的内容不会变。
另一方面,复制一个引用类型的时候,实际上是默默地创造了一个共享的实例分身,两者是共用一套数据。因此修改其中任何一个实例的数据,也会影响到另外那个。
~~~
// 下面是一个引用类型的例子
classC{ var data: Int = -1 }
var x = C()
var y = x // y是x的拷贝
x.data = 42 // 更改x的数据,等于同时修改了y
println("\(x.data), \(y.data)") // 输出结果 "42, 42"
~~~
[]()
#### Mutation(修改)在安全中扮演的角色
值类型较引用类型来说,会让你更容易在大量代码中理清状况。如果你总是得到一个独立的拷贝出来的实例,你就可以放心它不会被你app里面的其他部分代码默默地修改。这在多线程的环境里面是尤为重要的,因为另外一个线程可能会在暗地里修改你的数据。因此可能会造成严重的程序错误,这在调试过程中非常难以排除。
由于差别主要在于修改数据的后果,那么当实例的数据只读,不存在需要更改的情况下,用哪种类型都是没有分别的。
你可能在想,有的时候我可能也需要一个完全不变的类。这样使用`Cocoa NSObject`对象的时候会比较容易,又可以保留值语义的好处。在今天,你可以通过只使用不可变的存储属性,和避开任何可以修改状态的API,用Swift写出一个不可变类`(immutable class)`。实际上,很多基本的Cocoa类,例如`NSURL`,都是设计成不可变类的。然而,Swift语言目前只强制`struct`和`enum`这种值类型的不可变性,对类这种引用类型则没有。(例如还不支持强制将子类的限制为不可变类)
[]()
#### 如何选择类型?
所以当我们想要建立一个新的类型的时候,怎么决定用值类型还是引用类型呢?当你使用Cocoa框架的时候,很多API都要通过NSObject的子类使用,所以这时候必须要用到引用类型class。在其他情况下,有下面几个准则:
-
**什么时候该用值类型**:
- 要用==运算符来比较实例的数据时
- 你希望那个实例的拷贝能保持独立的状态时
- 数据会被多个线程使用时
-
**什么时候该用引用类型(class)**:
- 要用==运算符来比较实例身份的时候
- 你希望有创建一个共享的、可变对象的时候
在Swift里面,数组(Array)、字符串(String)、字典(Dictionary)都属于值类型。它们就像C语言里面简单的int值,是一个个独立的数据个体。你不需要花任何功夫来防范其他代码在暗地里修改它们。更重要的是,你可以在线程之间安全的传递变量,而不需要特地去同步。在Swift高安全性的精神下,这个模式会帮助你用Swift写出更可控的代码。
本章节不是老码的原创,老码认真的阅读了苹果的官方博客,且自己的练习总结,如果小伙伴们费了吃奶的劲还是看不懂,请找度娘谷歌,还是看不懂请到老码[官方微博](http://weibo.com/u/5241713117)咆哮。
##### 本文由翻译自Apple Swift Blog :[Value and Reference Types](https://developer.apple.com/swift/blog/?id=10)
Swift与C语言指针友好合作
最后更新于:2022-04-01 05:05:20
> 翻译:[老码团队翻译组-Relly](http://weibo.com/penguinliong/)
校对:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117)
# Swift与C语言指针友好合作
本页包含内容:
[TOC=2,2]
Objective-C和C的API常常会需要用到指针。Swift中的数据类型都原生支持基于指针的Cocoa API,不仅如此,Swift会自动处理部分最常用的将指针作为参数传递的情况。这篇文章中,我们将着眼于在Swift中让C语言指针与变量、数组和字符串共同工作。
## 用以输入/输出的参数指针
C和Objective-C并不支持多返回值,所以Cocoa API中常常将指针作为一种在方法间传递额外数据的方式。Swift允许指针被当作`inout`参数使用,所以你可以用符号`&`将对一个变量的引用作为指针参数传递。举例来说:`UIColor`中的`getRed(_:green:blue:alpha:)`方法需要四个`CGFloat*`指针来接收颜色的组成信息,我们使用`&`来将这些组成信息捕获为本地变量:
~~~
var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
color.getRed(&r, green: &g, blue: &b, alpha: &a)
~~~
另一种常见的情况是Cocoa中`NSError`的习惯用法。许多方法会使用一个`NSError**`参数来储存可能的错误的信息。举例来说:我们用`NSFileManager`的`contentOfDirectoryAtPath(_:error:)`方法来将目录下的内容列表,并将潜在的错误指向一个`NSError?`变量:
~~~
var maybeError: NSError?
if let contents = NSFileManager.defaultManager()
.contentsOfDirectoryAtPath("/usr/bin", error: &maybeError) {
// Work with the directory contents
} else if let error = maybeError {
// Handle the error
}
~~~
为了安全性,Swift要求被使用`&`传递的变量已经初始化。因为无法确定这个方法会不会在写入数据前尝试从指针中读取数据。
## 作为数组使用的参数指针
在C语言中,数组和指针的联系十分紧密,而Swift允许数组能够作为指针使用,从而与基于数组的C语言API协同工作更加简单。一个固定的数组可以使用一个常量指针直接传递,一个变化的数组可以用`&`运算符将一个非常量指针传递。就和输入/输出参数指针一样。举例来说:我们可以用Accelerate框架中的`vDSP_vadd`方法让两个数组`a`和`b`相加,并将结果写入第三个数组`result`。
~~~
import Accelerate
let a: [Float] = [1, 2, 3, 4]
let b: [Float] = [0.5, 0.25, 0.125, 0.0625]
var result: [Float] = [0, 0, 0, 0]
vDSP_vadd(a, 1, b, 1, &result, 1, 4)
// result now contains [1.5, 2.25, 3.125, 4.0625]
~~~
## 用作字符串参数的指针
C语言中用`cont char*`指针来作为传递字符串的基本方式。Swift中的`String`可以被当作一个无限长度UTF-8编码的`const char*`指针来传递给方法。举例来说:我们可以直接传递一个字符串给一个标准C和POSIX库方法
~~~
puts("Hello from libc")
let fd = open("/tmp/scratch.txt", O_WRONLY|O_CREAT, 0o666)
if fd < 0 {
perror("could not open /tmp/scratch.txt")
} else {
let text = "Hello World"
write(fd, text, strlen(text))
close(fd)
}
~~~
## 指针参数转换的安全性
Swift很努力地使与C语言指针的交互更加便利,因为它们广泛地存在于Cocoa之中,同时保持一定的安全性。然而,相比你的其他Swift代码与C语言的指针交互具有潜在的不安全性,所以务必要小心使用。其中特别要注意:
-
如果被调用者为了在其返回值之后再次使用而保存了C指针的数据,那么这些转换使用起来并不安全。转换后的指针仅在调用期间保证有效。甚至你将同样的变量、数组或字符串作为多指针参数再次传递,你每次都会收到一个不同的指针。这个异常将全局或静态地储存为变量。你可以安全地将这段地址当作永久唯一的指针使用。例如:作为一个KVO上下文参数使用的时候。
-
当指针类型为`Array`或`String`时,溢出检查不是强制进行的。 基于C语言的API无法增加数组和字符串大小,所以在你将其传递到基于C语言的API之前,你必须确保数组或字符的大小正确。
如果你需要使用基于指针的API时没有遵守以上指导,或是你重写了接受指针参数的Cocoa方法,于是你可以在Swift中直接用不安全的指针来使用未经处理的内存。在未来的文章中我们将着眼于更加高级的情况。
WWDC里面的那个“大炮打气球”
最后更新于:2022-04-01 05:05:18
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/)
校对:[老码团队翻译组-](#)
# WWDC里面的那个“大炮打气球”
![Ballon playground](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-07_559b6e730f39a.jpg)
很多小伙伴说,对WWDC上介绍Swift语言时,演示的那个“大炮打气球”的Ballons项目很感兴趣。
Ballons不但展现了playgrounds许多很赞的特性,还让我们看到写代码的过程,原来可以这么互动,这么好玩。
现在你可以下载这个[Ballons.playground](https://developer.apple.com/swift/blog/downloads/Balloons.zip)的教学版本,学习这些有趣的效果是怎么实现的。教学版本里除了源文件,还有相关说明文档,我们还出了一些小小的实验题,你可以动手修改代码,然后在右侧马上看到效果。
这个playground文件用到了SpriteKit的新特性,因此需要最新beta版本的Xcode 6和Yosemite系统来支持它运行。
本文由翻译自Apple Swift Blog的博文:[Ballons](https://developer.apple.com/swift/blog/?id=9)
造个类型不是梦-白话Swift类型创建
最后更新于:2022-04-01 05:05:15
> 翻译:[老码团队翻译组-Tyrion](http://weibo.com/u/5241713117) 校对:[老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)
本页包含内容:
[TOC=2]
小伙伴们,Swift中的Bool类型有着非常重要的语法功能,并支撑起了整个Swift体系中的逻辑判断体系,经过老码的研究和学习, Bool类型本身其实是对基础Boolean类型封装,小伙伴们可能咬着手指头问老码,怎么一会Bool类型,一会Boolean类型,其区别在于,前者是基于枚举的组合类型,而后者则是基本类型,只有两种true和false。
## 自定义原型
接下老码根据Bool的思想来创建一个OCBool类型,来让小伙伴们了解一下Swift中到底是怎么玩儿的。 来我们先看一下OCBool的定义。
### 代码示例如下:
~~~
enum OCBool{
case ocTrue
case ocFalse
}
~~~
### 注意:
* 代码中第2行和第3行,可以合并到一行写,如苹果官方Blog所写的一样
* 代码中命名需要注意:OCBool是类型名,所以首字母必须大写,而case中的ocTrue和ocFalse是小类型则需要首字母小写。
## 实现默认值
行,我们给了一个漂亮的定义,不过按照传统语言的经验,Bool值默认情况下是假, 所以我们的OCBool也应该如此,我们使用类型扩展技术增加这个默认特性:
~~~
extension OCBool{
init(){
self =.ocFalse
}
}
~~~
### 注意:
* 代码中第1行:extension关键字,非常强大,小伙伴们可以通过此创造出许多好玩的东西,建议各位去Github上看一个名为“Swiftz”的项目,它将扩展用到了极致。
* 代码中第3行:self = .ocFalse语法,刚入门的小伙伴们很迷糊,为什么会有奇怪的点语法,因为大牛Chris在Swift中增加了类型智能推断功能,在苹果Blog中,提到了“Context”概念,就是这个意思,因为这行语句是在枚举OCBool中的,其上下文就是OCBool的定义体,编译器当然知道.ocFalse就是OCBool.ocFalse了,所以这里直接点语法,非常整齐。 现在我们可以使用如下方法使用这个Bool类型。
### 代码示例如下:
~~~
var result:OCBool = OCBool()
var result1:OCBool = .ocTrue
~~~
## 支持基本布尔型初始化
正如上述代码所述,我们只能通过类型或者枚举项目赋值,这是组合类型的用法,但是编码的日子里,我们总是希望和true,false直接打交道,也就是说,我们希望这么做, 代码示例如下:
~~~
var isSuccess:OCBool = true
~~~
如果小伙伴们直接这么用,则会出现如下错误:
~~~
/Users/tyrion-OldCoder/Documents/Learning/BoolType/BoolType/main.swift:30:24: Type 'OCBool' does not conform to protocol 'BooleanLiteralConvertible'
~~~
编译器咆哮的原因是,我们的类型没有遵从“布尔字面量转换协议”,接下来修正这个问题,
### 代码示例如下:
~~~
import Foundation
println("Hello, World!")
enum OCBool{
case ocTrue
case ocFalse
}
extension OCBool: BooleanLiteralConvertible{
static func convertFromBooleanLiteral( value: Bool) ->OCBool{
return value ? ocTrue : ocFalse
}
}
var isSuccess:OCBool = true
~~~
### 注意:
* 代码中的第11行是重点,我的类型OCBool支持了BooleanLiteralConvertible协议,这个协到底是干什么的呢,小伙伴们在Xcode代码编辑器,按住Command键,然后点击第11行中的BooleanLiteralConvertible协议名,则会进入它的定义,
##### 其定义如下:
~~~
protocol BooleanLiteralConvertible {
typealias BooleanLiteralType
class func convertFromBooleanLiteral(value: BooleanLiteralType) -> Self
}
~~~
* 这个定义中有个类方法convertFromBooleanLiteral,它的参数为BooleanLiteralType类型,也就是我传入的Bool类型, 且返回值为实现这个协议的类型本身,在我们的OCBool类型中,其返回值就是OCBool本身。经过这个定义,我们可以直接对OCBool类型直接进行布尔字面量初始化了。
## 支持Bool类型判断
小伙伴们不安分, 肯定想着我怎么用它实现逻辑判断,所以如果你这么写,
### 代码示例如下:
~~~
var isSuccess:OCBool = true
if isSuccess {
println( "老码请你吃火锅!")
}
~~~
你永远吃不到老码的火锅,因为这里编译器会咆哮:
~~~
/Users/tyrion-OldCoder/Documents/Learning/BoolType/BoolType/main.swift:27:4: Type 'OCBool' does not conform to protocol 'LogicValue'
~~~
OCBool现在只能用bool类型初始化,而不能直接返回bool型,小火把们还记得在《老码说编程之白话Swift江湖》中,老码多次提到,妈妈再也不担心我们 if a = 1{}的写法了, 因为等号不支持值返回了, 所以在if判断是后面的条件必须有返回值,OCBool没有,所以编译器哭了。我们解决这个问题。
### 代码示例如下:
~~~
import Foundation
println("Hello, World!")
enum OCBool{
case ocTrue
case ocFalse
}
extension OCBool: BooleanLiteralConvertible{
static func convertFromBooleanLiteral( value: Bool) ->OCBool{
return value ? ocTrue : ocFalse
}
}
extension OCBool: LogicValue{
func getLogicValue() ->Bool {
var boolValue: Bool{
switch self{
case .ocTrue:
return true
case .ocFalse:
return false
}
}
return boolValue
}
}
var isSuccess:OCBool = true
if isSuccess {
println( "老码请你吃火锅!")
}
~~~
### 运行结果如下:
~~~
Hello, World!
老码请你吃火锅!
Program ended with exit code: 0
~~~
### 注意:
* 如果小伙伴们现在用的是Beta版的Xcode,注意苹果官方Blog中,在代码第17行如果在Xcode Beta4下是错误的,这里的协议是,LogicValue而不是BooleanVue,所以记得看错误提示才是好习惯。
* 注意代码第34行,完美支持if判断,且输出结果为“老码请你吃火锅”,老码也是说说而已,请不要当真。
## 支持兼容各们各派的类型
小伙伴们,江湖风险,门派众多,老码有自己的OCBool类型,可能嵩山少林有自己的SSBool类型,甚至连郭美美都可能有自己的MMBool类型,所以OCBool必须能够识别这些类型,这些各门各派的类型,只要支持LogicValue协议,就应该可以被识别,看老码怎么做,
### 代码示例如下:
~~~
extension OCBool{
init( _ v: LogicValue )
{
if v.getLogicValue(){
self = .ocTrue
}
else{
self = .ocFalse
}
}
}
var mmResult: Bool = true
var ocResult:OCBool = OCBool(mmResult)
if ocResult {
println( "老码没钱,郭美美请你吃火锅!")
}
~~~
### 代码运行结果如下:
~~~
Hello, World!
老码没钱,郭美美请你吃火锅!
Program ended with exit code: 0
~~~
漂亮!我们的OCBool类型现在支持了所有的逻辑变量初始化。
### 注意:
* 代码中第2行:“_”下横杠的用法,这是一个功能强大的小强,在此的目的是屏蔽外部参数名,所以小伙伴们可以直接:var ocResult:OCBool = OCBool(mmResult)而不是:var ocResult:OCBool = OCBool(v: mmResult),小伙伴们惊呆了!这个init函数中本来就没有外部参数名啊,还记得老码在书里说过没,Swift的初始化函数会默认使用内部参数名,作为外部参数名。
## 完善OCBool的布尔基因体系:
小伙伴们,bool类型的价值就是在于各种判断,诸如==,!=, &,|,^,!,以及各种组合逻辑运算,我们OCBool也要具备这些功能,否则就会基因缺陷,且看老码如何实现:
~~~
extension OCBool: Equatable{
}
//支持等值判断运算符
func ==( left: OCBool, right: OCBool )->Bool{
switch (left, right){
case (.ocTrue, .ocTrue):
return true
default:
return false
}
}
//支持位与运算符
func &( left:OCBool, right: OCBool)->OCBool{
if left{
return right
}
else{
return false
}
}
//支持位或运算符
func |( left:OCBool, right: OCBool)->OCBool{
if left{
return true
}
else{
return right
}
}
//支持位异或运算符
func ^( left:OCBool, right: OCBool)->OCBool{
return OCBool( left != right )
}
//支持求反运算符
@prefix func !( a:OCBool )-> OCBool{
return a ^ true
}
//支持组合求与运算符
func &= (inout left:OCBool, right:OCBool ){
left = left & right
}
var isHasMoney:OCBool = true
var isHasWife:OCBool = true
var isHasHealty:OCBool = true
var isHasLover:OCBool = true
isHasMoney != isHasHealty
isHasHealty == isHasMoney
isHasWife ^ isHasLover
isHasWife = !isHasLover
if (isHasMoney | isHasHealty) & isHasHealty{
println( "人生赢家,就像老码一样!")
}else
{
println("人生最苦的事事,人死了钱没花了,人生最苦的事是,人活着,钱没了!")
}
~~~
好了,到这里就到这里了,窗外的雷声叫醒了老码,现在应该去吃饭了,以上老码给大家展示了如果制造一个自己的类型,记得老码的示例是在Xcode6 Beta4下测试的,至于Beta5的改变还没有涉及,小伙伴们要好生练习,以后各种自定类型都是基于这个思想。还有这个章节不是老码的原创,老码认真的阅读了苹果的官方博客,且自己的练习总结,如果小伙伴们费了吃奶的劲还是看不懂,请找度娘谷歌,还是看不懂请到老码官方微博:[http://weibo.com/u/5241713117咆哮](http://weibo.com/u/5241713117%E5%92%86%E5%93%AE)。
本文由翻译自Apple Swift Blog :[https://developer.apple.com/swift/blog/?id=8](https://developer.apple.com/swift/blog/?id=8)
Access Control 权限控制的黑与白
最后更新于:2022-04-01 05:05:13
> 翻译:[老码团队翻译组-Arya](http://weibo.com/littlekok/) 校对:[老码团队翻译组-Oberyn](http://weibo.com/u/5241713117)
如果您之前没有接触过权限控制,先来听一个小故事:
> 小明是五道口工业学院的一个大一新生,最近他有点烦恼,因为同屋经常用他的热水壶,好像那是自己家的一样,可是碍于同学情面,又不好意思说。直到有一天,他和学姐小K吐槽。
>
> 学姐听了之后,说:大学集体生活里面,大部分东西都是默认室友可以共用的。如果你不想别人拿,我可以帮你做封印,只要打上private标记,它们就看不到你的东西,更加用不了你的东西了。
>
> 小明说哇靠学姐你还会妖法......
Swift语言从Xcode 6 beta 5版本起,加入了对权限控制(Access Control)的支持。其实权限控制和小明的物品一样,你可以设定水壶是只有自己能用,还是只有宿舍里的人能用,还是全校都可以用。
从此以后,你可以好像神盾局局长一样,完全掌控自己的代码块的”保密级别“,哪些是只能在本文件引用,哪些能用在整个项目里,你还可以发挥大爱精神,把它开源成只要导入你的框架,大家都可以使用的API。
这三种权限分别是:
* ##### private 私有的
在哪里写的,就在哪里用。无论是类、变量、常量还是函数,一旦被标记为私有的,就只能在定义他们的源文件里使用,不能为别的文件所用。
* ##### internal 内部的
标记为internal的代码块,在整个应用(App bundle)或者框架(framework)的范围内都是可以访问的。
* ##### public 公开的
标记为public的代码块一般用来建立API,这是最开放的权限,使得任何人只要导入这个模块,都可以访问使用。
如果要把所有的爱加上一个期限,噢不,是给所有的代码块都标记上权限,不累死才怪。还好swift里面所有代码实体的默认权限,都是最常用的internal。所以当你开发自己的App时,可能完全不用管权限控制的事情。
但当你需要写一个公开API的时候,就必须对里面的代码块进行“隐身对其可见”的public标记,要么其他人是用不到的。
Private(私有级别)的权限最严格,它可以用来隐藏某些功能的细节实现方式。合理构筑你的代码,你就可以安全地使用extension和高级功能,又不把它们暴露给项目内的其他文件。
除了可以给整个声明设权限,Swift还允许大家在需要的时候,把某个属性(property)的取值权限比赋值权限设得更加开放。
##### 举个例子:
~~~
public class ListItem {
// ListItem这个类,有两个公开的属性
public var text: String
public var isComplete: Bool
// 下面的代码表示把变量UUID的赋值权限设为private,对整个app可读,但值只能在本文件里写入
private(set) var UUID: NSUUID
public init(text: String, completed: Bool, UUID: NSUUID) {
self.text = text
self.isComplete = completed
self.UUID = UUID
}
// 这段没有特别标记权限,因此属于默认的internal级别。在框架目标内可用,但对于其他目标不可用
func refreshIdentity() {
self.UUID = NSUUID()
}
public override func isEqual(object: AnyObject?) -> Bool {
if let item = object as? ListItem {
return self.UUID == item.UUID
}
return false
}
}
~~~
当我们使用Objective-C和Swift混合开发时,需要注意:
* 如果你在写的是一个应用,Xcode会生成一个头文件来保证两者的可互访性,而这个生成的头文件会包含public和internal级别的声明。
* 如果你的最终产品是一个Swift框架,头文件里只会出现标记为public级别的声明。(因为框架的头文件,属于公开的Objective-C接口的一部分,只有public部分对Objective-C可用。)
虽然Swift不推荐大家传播和使用第三方的框架,但对于建立和分享源文件形式的框架是支持的。对于需要写框架,方便应用与多个项目的开发者来说,要记得把API标记为public级别。
如果您想了解更多关于权限控制的内容,可以查看苹果官方最新的《The Swift Language》和《Using Swift with Cocoa and Objective-C》指南, 这两本指南在iBooks里面可以下载更新喔。
本文由翻译自Apple Swift Blog :[https://developer.apple.com/swift/blog/?id=5](https://developer.apple.com/swift/blog/?id=5)
苹果官方Blog官方翻译
最后更新于:2022-04-01 05:05:11
语法总结
最后更新于:2022-04-01 05:05:09
> 1.0 翻译:[stanzhai](https://github.com/stanzhai) 校对:[xielingwang](https://github.com/xielingwang)
>
> 2.0 翻译+校对:[miaosiqi](https://github.com/miaosiqi)
本页包含内容:
[TOC=2]
## 语句
### 语句语法
> 语句 → _表达式_ ; 可选
> 语句 → _声明_ ; 可选
> 语句 → _循环语句_ ; 可选
> 语句 → _分支语句_ ; 可选
> 语句 → _标记语句(Labeled Statement)_
> 语句 → _控制转移语句_ ; 可选
> 语句 → _延迟语句_ ; _可选_
> _语句_ → _执行语句_ ; _可选_
> _多条语句(Statements)_ → _语句_ _多条语句(Statements)_ _可选_
### 循环语句语法
> 循环语句 → _for语句_
> 循环语句 → _for-in语句_
> 循环语句 → _while语句_
> 循环语句 → _repeat-while语句_
### For 循环语法
> for语句 → for _for初始条件_ 可选 ; _表达式_ 可选 ; _表达式_ 可选 _代码块_
> for语句 → for ( _for初始条件_ 可选 ; _表达式_ 可选 ; _表达式_ 可选 ) _代码块_
> for初始条件 → _变量声明_ | _表达式集_
### For-In 循环语法
> for-in语句 → for case 可选 _模式_ in _表达式_ _代码块_ _where从句_ _可选_
### While 循环语法
> while语句 → while _条件从句_ _代码块_
> 条件从句 → _表达式_
> _条件从句_ → _表达式_ , _表达式集_
> _条件从句_ → _表达式集_
> _条件从句_ → _可用条件 (availability-condition_) | _表达式集_
> _条件集_ → _条件_ | _条件_ , _条件集_
> _条件_ → _可用条件(availability-condition)_ | _个例条件(case-condition)_ | _可选绑定条件(optional-binding-condition)_
> _个例条件(case-condition)_ → case _模式_ _构造器_ _where从句__可选_
> _可选绑定条件(optional-binding-condition)_ → _可选绑定头(optional-binding-head)_ _可选绑定连续集(optional-binding-continuation-list)_ 可选 _where从句_ _可选_
> _可选绑定头(optional-binding-head)_ → let _模式 构造器_ | var _模式 构造器_
> _可选绑定连续集(optional-binding-contiuation-list)_ → _可选绑定连续(optional-binding-contiuation)_ | _可选绑定连续(optional-binding-contiuation)_ , _可选绑定连续集(optional-binding-contiuation-list)_
> _可选绑定连续(optional-binding-continuation)_ → _模式 构造器_ | _可选绑定头(optional-binding-head)_
> Repeat-While语句语法 repeat-while-statement → repeat _代码块_ while _表达式_
### 分支语句语法
> 分支语句 → _if语句_
> _分支语句_ → _guard语句_
> _分支语句_ → _switch语句_
### If语句语法
> if语句 → if _条件从句_ _代码块_ _else从句(Clause)_ _可选_
> _else从句(Clause)_ → else _代码块_ | else _if语句_
### Guard 语句语法
> guard语句 → guard _条件从句_ else _代码块_
### Switch语句语法
> switch语句 → switch _表达式_ { _SwitchCase_ 可选 }
> SwitchCase集 → _SwitchCase_ _SwitchCase集_ 可选
> SwitchCase → _case标签_ _多条语句(Statements)_ | _default标签_ _多条语句(Statements)_
> SwitchCase → _case标签_ ; | _default标签_ ;
> case标签 → case _case项集_ :
> case项集 → _模式_ _where-clause_ 可选 | _模式_ _where-clause_ 可选 , _case项集_
> default标签 → default :
where从句 → where _where表达式_
where表达式 → _表达式_
### 标记语句语法
> 标记语句(Labeled Statement) → _语句标签_ _循环语句_ | _语句标签_ _if语句_ | _语句标签_ _switch语句_ 语句标签 → _标签名称_ :
> 标签名称 → _标识符_
### 控制传递语句(Control Transfer Statement) 语法
> 控制传递语句 → _break语句_
> 控制传递语句 → _continue语句_
> 控制传递语句 → _fallthrough语句_
> 控制传递语句 → _return语句_ 控制传递语句 → _throw语句_
> Break 语句语法
> break语句 → break _标签名称_ _可选_
> Continue 语句语法
> continue语句 → continue _标签名称_ _可选_
### Fallthrough 语句语法
> fallthrough语句 → fallthrough
### Return 语句语法
> return语句 → return _表达式_ _可选_
### 可用条件(Availability Condition)语法
> _可用条件(availability-condition)_ → #available ( _多可用参数_(availability-arguments) )
> _多可用参数(availability- arguments)_ → _可用参数(availability-argument)_|_可用参数(availability-argument)_ , 多可用参数(availability-arguments)
> _可用参数(availability- argument)_ → _平台名(platform-name)_ _平台版本(platform-version)_
> _可用参数(availability- argument)_ → *
> _平台名_ → iOS | iOSApplicationExtension
> _平台名_ → OSX | OSXApplicationExtension
> _平台名_ → watchOS
> _平台版本_ → _十进制数(decimal-digits)_
> _平台版本_ → _十进制数(decimal-digits)_ . _十进制数(decimal-digits)_
> _平台版本_ → _十进制数(decimal-digits)_ . _十进制数(decimal-digits)_ . _十进制数(decimal-digits)_)
### 抛出语句(Throw Statement)语法
> _抛出语句(throw-statement)_ → throw _表达式(expression)_
### 延迟语句 (defer-statement)语法
> _延迟语句(defer-statement)_ → defer _代码块_
### 执行语句(do-statement)语法
> _执行语句(do-statement)_ → do _代码块_ _catch-clauses_ _可选_
> _catch-clauses_ → _catch-clause_ _catch-clauses_ _可选_
> _catch-clauses_ → catch _模式(pattern)_ 可选 _where-clause_ 可选 _代码块(code-block)_ _可选_
## 泛型参数
### 泛型形参从句(Generic Parameter Clause) 语法
> 泛型参数从句 → _泛型参数集_ _约束从句_ 可选 >
> 泛型参数集 → _泛形参数_ | _泛形参数_ , _泛型参数集_
> 泛形参数 → _类型名称_
> 泛形参数 → _类型名称_ : _类型标识_
> 泛形参数 → _类型名称_ : _协议合成类型_
> 约束从句 → where _约束集_
> 约束集 → _约束_ | _约束_ , _约束集_
> 约束 → _一致性约束_ | _同类型约束_
> 一致性约束 → _类型标识_ : _类型标识_
> 一致性约束 → _类型标识_ : _协议合成类型_
> 同类型约束 → _类型标识_ == _类型_
### 泛型实参从句语法
> (泛型参数从句Generic Argument Clause) → _泛型参数集_ >
> 泛型参数集 → _泛型参数_ | _泛型参数_ , _泛型参数集_
> 泛型参数 → _类型_
## 声明 (Declarations)
### 声明语法
> 声明 → _导入声明_
> 声明 → _常量声明_
> 声明 → _变量声明_
> 声明 → _类型别名声明_
> 声明 → _函数声明_
> 声明 → _枚举声明_
> 声明 → _结构体声明_
> 声明 → _类声明_
> 声明 → _协议声明_
> 声明 → _构造器声明_
> 声明 → _析构器声明_
> 声明 → _扩展声明_
> 声明 → _下标脚本声明_
> 声明 → _运算符声明_
> 声明(Declarations)集 → _声明_ _声明(Declarations)集_ _可选_
### 顶级(Top Level) 声明语法
> 顶级声明 → _多条语句(Statements)_ _可选_
### 代码块语法
> 代码块 → { _多条语句(Statements)_ 可选 }
### 导入(Import)声明语法
> 导入声明 → _属性(Attributes)集_ 可选 import _导入类型_ 可选 _导入路径_
> 导入类型 → typealias | struct | class | enum | protocol | var | func
> 导入路径 → _导入路径标识符_ | _导入路径标识符_ . _导入路径_
> 导入路径标识符 → _标识符_ | _运算符_
### 常数声明语法
> 常量声明 → _属性(Attributes)集_ 可选 _声明修改符(Modifiers)集_ 可选 let _模式构造器集_
> 模式构造器集 → _模式构造器_ | _模式构造器_ , _模式构造器集_
> 模式构造器 → _模式_ _构造器_ 可选
> 构造器 → = _表达式_
### 变量声明语法
> 变量声明 → _变量声明头(Head)_ _模式构造器集_
> 变量声明 → _变量声明头(Head)_ _变量名_ _类型注解_ _代码块_
> 变量声明 → _变量声明头(Head)_ _变量名_ _类型注解_ _getter-setter块_
> 变量声明 → _变量声明头(Head)_ _变量名_ _类型注解_ _getter-setter关键字(Keyword)块_
> 变量声明 → _变量声明头(Head)_ _变量名_ _类型注解_ _构造器_ 可选 _willSet-didSet代码块_
> 变量声明头(Head) → _属性(Attributes)集_ 可选 _声明修改符(Modifers)集_ 可选 var
> 变量名称 → _标识符_
> getter-setter块 → { _getter从句_ _setter从句_ 可选 }
> getter-setter块 → { _setter从句_ _getter从句_ }
> getter从句 → _属性(Attributes)集_ 可选 get _代码块_
> setter从句 → _属性(Attributes)集_ 可选 set _setter名称_ 可选 _代码块_
> setter名称 → ( _标识符_ )
> getter-setter关键字(Keyword)块 → { _getter关键字(Keyword)从句_ _setter关键字(Keyword)从句_ 可选 }
> getter-setter关键字(Keyword)块 → { _setter关键字(Keyword)从句_ _getter关键字(Keyword)从句_ }
> getter关键字(Keyword)从句 → _属性(Attributes)集_ 可选 get
> setter关键字(Keyword)从句 → _属性(Attributes)集_ 可选 set
> willSet-didSet代码块 → { _willSet从句_ _didSet从句_ 可选 }
> willSet-didSet代码块 → { _didSet从句_ _willSet从句_ }
> willSet从句 → _属性(Attributes)集_ 可选 willSet _setter名称_ 可选 _代码块_
> didSet从句 → _属性(Attributes)集_ 可选 didSet _setter名称_ 可选 _代码块_
### 类型别名声明语法
> 类型别名声明 → _类型别名头(Head)_ _类型别名赋值_
> 类型别名头(Head) → _属性_ 可选 _访问级别修改符(access-level-modifier)_ typealias _类型别名名称_
> 类型别名名称 → _标识符_
> 类型别名赋值 → = _类型_
### 函数声明语法
> 函数声明 → _函数头_ _函数名_ _泛型参数从句_ 可选 _函数签名(Signature)_ _函数体_
> 函数头 → _属性(Attributes)集_ 可选 _声明描述符(Specifiers)集_ 可选 func
> 函数名 → _标识符_ | _运算符_
> 函数签名(Signature) → _parameter-clauses_ throws 可选 _函数结果_ _可选_
> _函数签名(Signature)_ → _parameter-clauses_ rethrows _函数结果_ 可选
> 函数结果 → -> _属性(Attributes)集_ 可选 _类型_
> 函数体 → _代码块_
> 参数从句 → _参数从句_ _parameter-clauses_ 可选
> 参数从句 → ( ) | ( _参数集_ ... 可选 )
> 参数集 → _参数_ | _参数_ , _参数集_
> 参数 → inout 可选 let 可选 _外部参数名_ 可选 _本地参数名_ 可选 _类型注解_ _默认参数从句_ 可选
> 参数 → inout 可选 var _外部参数名_ _本地参数名_ 可选 _类型注解_ _默认参数从句_ 可选
> 参数 → _属性(Attributes)集_ 可选 _类型_
> 外部参数名 → _标识符_ | _
> 本地参数名 → _标识符_ | _
> 默认参数从句 → = _表达式_
### 枚举声明语法
> 枚举声明 → _属性(Attributes)集_ 可选 _访问级别修改器(access-level-modifier)_ 可选 _联合式枚举_
> _枚举声明_ → _属性(Attributes)集_ 可选 _访问级别修改器(access-level-modifier)_ 可选 _原始值式枚举(raw-value-style-enum)_
> _联合式枚举_ → enum _枚举名_ _泛型参数从句_ 可选 _类型继承从句(type-inheritance-clause)_ 可选 { _联合样式枚举成员_ 可选 }
> _联合样式枚举成员_ → _union-style-enum-member_ _联合样式枚举成员_ _可选_
> _联合样式枚举成员_ → _声明_ | _联合式(Union Style)的枚举case从句_
> _联合式(Union Style)的枚举case从句_ → _属性(Attributes)集_ 可选 case _联合式(Union Style)的枚举case集_
> 联合式(Union Style)的枚举case集 → _联合式(Union Style)的case_ | _联合式(Union Style)的case_ , _联合式(Union Style)的枚举case集_
> 联合式(Union Style)的枚举case → _枚举的case名_ _元组类型_ 可选
> 枚举名 → _标识符_
> 枚举的case名 → _标识符_
> 原始值式枚举 → enum _枚举名_ _泛型参数从句_ 可选 : _类型标识_ { _原始值式枚举成员集_ 可选 }
> 原始值式枚举成员集 → _原始值式枚举成员_ _原始值式枚举成员集_ 可选
> 原始值式枚举成员 → _声明_ | _原始值式枚举case从句_
> 原始值式枚举case从句 → _属性(Attributes)集_ 可选 case _原始值式枚举case集_
> 原始值式枚举case集 → _原始值式枚举case_ | _原始值式枚举case_ , _原始值式枚举case集_
> 原始值式枚举case → _枚举的case名_ _原始值赋值_ 可选
> 原始值赋值 → = _字面量_ 原始值字面量(raw-value-literal) → _数值字面量_ | _字符串字面量_ | _布尔字面量_
### 结构体声明语法
> 结构体声明 → _属性(Attributes)集_ 可选 _访问级别修改器(access-level-modifier)_ 可选 struct _结构体名称_ _泛型参数从句_ 可选 _类型继承从句_ 可选 _结构体主体_
> 结构体名称 → _标识符_
> 结构体主体 → { _声明(Declarations)集_ 可选 }
### 类声明语法
> 类声明 → _属性(Attributes)集_ 可选 _访问级别修改器(access-level-modifier)_ class _类名_ _泛型参数从句_ 可选 _类型继承从句_ 可选 _类主体_
> 类名 → _标识符_
> 类主体 → { _声明(Declarations)集_ 可选 }
### 协议(Protocol)声明语法
> 协议声明 → _属性(Attributes)集_ 可选__访问级别修改器(access-level-modifier)_ protocol _协议名_ _类型继承从句__可选 _协议主体_
> 协议名 → _标识符_
> 协议主体 → { _协议成员声明(Declarations)集_ 可选 }
> 协议成员声明 → _协议属性声明_
> 协议成员声明 → _协议方法声明_
> 协议成员声明 → _协议构造器声明_
> 协议成员声明 → _协议下标脚本声明_
> 协议成员声明 → _协议关联类型声明_
> 协议成员声明(Declarations)集 → _协议成员声明_ _协议成员声明(Declarations)集_ _可选_
### 协议属性声明语法
> 协议属性声明 → _变量声明头(Head)_ _变量名_ _类型注解_ _getter-setter关键字(Keyword)块_
### 协议方法声明语法
> 协议方法声明 → _函数头_ _函数名_ _泛型参数从句_ 可选 _函数签名(Signature)_
### 协议构造器声明语法
> 协议构造器声明 → _构造器头(Head)_ _泛型参数从句_ 可选 _参数从句_
### 协议下标脚本声明语法
> 协议下标脚本声明 → _下标脚本头(Head)_ _下标脚本结果(Result)_ _getter-setter关键字(Keyword)块_
### 协议关联类型声明语法
> 协议关联类型声明 → _类型别名头(Head)_ _类型继承从句_ 可选 _类型别名赋值_ _可选_
### 构造器声明语法
> 构造器声明 → _构造器头(Head)_ _泛型参数从句_ 可选 _参数从句_ _构造器主体_
> 构造器头(Head) → _属性(Attributes)集_ 可选 _声明修改器集(declaration-modifiers)_ 可选 init
> 构造器头(Head) → _属性(Attributes)集_ 可选 _声明修改器集(declaration-modifiers)_ 可选 init ?
> _构造器头(Head)_ → _属性(Attributes)集_ 可选 _声明修改器集(declaration-modifiers)_ 可选 init !
> _构造器主体_ → _代码块_
### 析构器声明语法
> 析构器声明 → _属性(Attributes)集_ 可选 deinit _代码块_
### 扩展(Extension)声明语法
> 扩展声明 → _访问级别修改器_ 可选 extension _类型标识_ _类型继承从句_ 可选 _extension-body_
> extension-body → { _声明(Declarations)集_ 可选 }
### 下标脚本声明语法
> 下标脚本声明 → _下标脚本头(Head)_ _下标脚本结果(Result)_ _代码块_
> 下标脚本声明 → _下标脚本头(Head)_ _下标脚本结果(Result)_ _getter-setter块_
> 下标脚本声明 → _下标脚本头(Head)_ _下标脚本结果(Result)_ _getter-setter关键字(Keyword)块_
> 下标脚本头(Head) → _属性(Attributes)集_ 可选 _声明修改器(declaration-modifiers)_ 可选 subscript _参数从句_
> 下标脚本结果(Result) → -> _属性(Attributes)集_ 可选 _类型_
### 运算符声明语法
> 运算符声明 → _前置运算符声明_ | _后置运算符声明_ | _中置运算符声明_
> 前置运算符声明 → prefix 运算符 _运算符_ { }
> 后置运算符声明 → postfix 运算符 _运算符_ { }
> 中置运算符声明 → infix 运算符 _运算符_ { _中置运算符属性集_ 可选 }
> 中置运算符属性集 → _优先级从句_ 可选 _结和性从句_ 可选
> 优先级从句 → precedence _优先级水平_
> 优先级水平 → 数值 0 到 255,首末项包括在内 结和性从句 → associativity _结和性_
> 结和性 → left | right | none
### 声明修改器语法
> _声明修改器_ → 类 | 便捷(convenience) | 动态(dynamic) | final | 中置(infix) | lazy | 可变(mutating) | 不可变(nonmutating) | 可选(optional) | 改写(override) | 后置 | 前置 | required |static | unowned | unowned(safe) | unowned(unsafe) | 弱(weak)
> _声明修改器_ → _访问级别声明器(access-level-modifier)_
> _声明修改集_ → _声明修改器_ _声明修改器集_ _可选_
> _访问级别修改器_ → 内部的 | 内部的(set)
> _访问级别修改器_ → 私有的 | 私有的(set)
> _访问级别修改器_ → 公共的 | 公共的(set)
> _访问级别修改器集_ →_访问级别修改器_ _访问级别修改器集_ _可选_
## 模式
### 模式(Patterns) 语法
> 模式 → _通配符模式_ _类型注解_ 可选
> 模式 → _标识符模式_ _类型注解_ 可选
> 模式 → _值绑定模式_
> 模式 → _元组模式_ _类型注解_ _可选_
> _模式_ → _枚举个例模式_
> 模式 → _可选模式_ 模式 → _类型转换模式_
> 模式 → _表达式模式_
### 通配符模式语法
> 通配符模式 → _
### 标识符模式语法
> 标识符模式 → _标识符_
### 值绑定(Value Binding)模式语法
> 值绑定模式 → var _模式_ | let _模式_
### 元组模式语法
> 元组模式 → ( _元组模式元素集_ 可选 )
> 元组模式元素集 → _元组模式元素_ | _元组模式元素_ , _元组模式元素集_
> 元组模式元素 → _模式_
### 枚举用例模式语法
> enum-case-pattern → _类型标识_ 可选 . _枚举的case名_ _元组模式_ _可选_
> 可选模式语法 可选模式 → _识别符模式_ ?
### 类型转换模式语法
> 类型转换模式(type-casting-pattern) → _is模式_ | _as模式_
> is模式 → is _类型_
> as模式 → _模式_ as _类型_
### 表达式模式语法
> 表达式模式 → _表达式_
## 属性
### 属性语法
> 属性 → @ _属性名_ _属性参数从句_ 可选
> 属性名 → _标识符_
> 属性参数从句 → ( _平衡令牌集_ 可选 )
> 属性(Attributes)集 → _属性_ _属性(Attributes)集_ 可选
> 平衡令牌集 → _平衡令牌_ _平衡令牌集_ 可选
> 平衡令牌 → ( _平衡令牌集_ 可选 )
> 平衡令牌 → [ _平衡令牌集_ 可选 ]
> 平衡令牌 → { _平衡令牌集_ 可选 }
> 平衡令牌 → 任意标识符, 关键字, 字面量或运算符
> 平衡令牌 → 任意标点除了(, ), [, ], {, 或 }
## 表达式
### 表达式语法
> 表达式 → _try-operator_ 可选 _前置表达式_ _二元表达式集_ 可选
> 表达式集 → _表达式_ | _表达式_ , _表达式集_
### 前置表达式语法
> 前置表达式 → _前置运算符_ 可选 _后置表达式_
> 前置表达式 → _写入写出(in-out)表达式_
> 写入写出(in-out)表达式 → & _标识符_
> try表达式语法 try-operator → try | try !
### 二元表达式语法
> 二元表达式 → _二元运算符_ _前置表达式_
> 二元表达式 → _赋值运算符_ _try运算符_ 可选 _前置表达式_
> 二元表达式 → _条件运算符_ _try运算符_ 可选 _前置表达式_
> 二元表达式 → _类型转换运算符_
> 二元表达式集 → _二元表达式_ _二元表达式集_ _可选_
### 赋值运算符语法
> 赋值运算符 → =
> 三元条件运算符语法
> 三元条件运算符 → ? _表达式_ :
### 类型转换运算符语法
> 类型转换运算符 → is _类型_
> _类型转换运算符_ → as _类型_
> _类型转换运算符_ → as ? _类型_
> _类型转换运算符_ → as ! _类型_
### 主表达式语法
> 主表达式 → _标识符_ _泛型参数从句_ 可选
> 主表达式 → _字面量表达式_
> 主表达式 → _self表达式_
> 主表达式 → _超类表达式_
> 主表达式 → _闭包表达式_
> 主表达式 → _圆括号表达式_
> 主表达式 → _隐式成员表达式_
> 主表达式 → _通配符表达式_
### 字面量表达式语法
> 字面量表达式 → _字面量_
> 字面量表达式 → _数组字面量_ | _字典字面量_
> 字面量表达式 → FILE | LINE | COLUMN | FUNCTION
> 数组字面量 → [ _数组字面量项集_ 可选 ]
> 数组字面量项集 → _数组字面量项_ , 可选 | _数组字面量项_ , _数组字面量项集_
> 数组字面量项 → _表达式_
> 字典字面量 → [ _字典字面量项集_ ] | [ : ]
> 字典字面量项集 → _字典字面量项_ , 可选 | _字典字面量项_ , _字典字面量项集_
> 字典字面量项 → _表达式_ : _表达式_
### Self 表达式语法
> self表达式 → self
> self表达式 → self . _标识符_
> self表达式 → self [ _表达式_ ]
> self表达式 → self . init
### 超类表达式语法
> 超类表达式 → _超类方法表达式_ | _超类下标表达式_ | _超类构造器表达式_
> 超类方法表达式 → super . _标识符_
> 超类下标表达式 → super [ _表达式_ ]
> 超类构造器表达式 → super . init
### 闭包表达式语法
> 闭包表达式 → { _闭包签名(Signational)_ 可选 _多条语句(Statements)_ }
> 闭包签名(Signational) → _参数从句_ _函数结果_ 可选 in
> 闭包签名(Signational) → _标识符集_ _函数结果_ 可选 in
> 闭包签名(Signational) → _捕获(Capature)集_ _参数从句_ _函数结果_ 可选 in
> 闭包签名(Signational) → _捕获(Capature)集_ _标识符集_ _函数结果_ 可选 in
> 闭包签名(Signational) → _捕获(Capature)集_ in
> 捕获(Capature)集 → [ _捕获(Capature)说明符_ _表达式_ ]
> 捕获(Capature)说明符 → weak | unowned | unowned(safe) | unowned(unsafe)
### 隐式成员表达式语法
> 隐式成员表达式 → . _标识符_
> 圆括号表达式(Parenthesized Expression)语法
> 圆括号表达式 → ( _表达式元素集_ 可选 )
> 表达式元素集 → _表达式元素_ | _表达式元素_ , _表达式元素集_
> 表达式元素 → _表达式_ | _标识符_ : _表达式_
### 通配符表达式语法
> 通配符表达式 → _
> 后置表达式语法
> 后置表达式 → _主表达式_
> 后置表达式 → _后置表达式_ _后置运算符_
> 后置表达式 → _函数调用表达式_
> 后置表达式 → _构造器表达式_
> 后置表达式 → _显示成员表达式_
> 后置表达式 → _后置self表达式_
> 后置表达式 → _动态类型表达式_
> 后置表达式 → _下标表达式_
> 后置表达式 → _强制取值(Forced Value)表达式_
> 后置表达式 → _可选链(Optional Chaining)表达式_
### 函数调用表达式语法
> 函数调用表达式 → _后置表达式_ _圆括号表达式_
> 函数调用表达式 → _后置表达式_ _圆括号表达式_ 可选 _后置闭包(Trailing Closure)_
> 后置闭包(Trailing Closure) → _闭包表达式_
### 构造器表达式语法
> 构造器表达式 → _后置表达式_ . init
### 显式成员表达式语法
> 显示成员表达式 → _后置表达式_ . _十进制数字_
> 显示成员表达式 → _后置表达式_ . _标识符_ _泛型参数从句_ _可选_
### 后置Self 表达式语法
> 后置self表达式 → _后置表达式_ . self
### 动态类型表达式语法
> 动态类型表达式 → _后置表达式_ . dynamicType
### 附属脚本表达式语法
> 附属脚本表达式 → _后置表达式_ [ _表达式集_ ]
### 强制取值(Forced Value)语法
> 强制取值(Forced Value)表达式 → _后置表达式_ !
### 可选链表达式语法
> 可选链表达式 → _后置表达式_ ?
## 词法结构
### 标识符语法
> 标识符 → _标识符头(Head)_ _标识符字符集_ 可选
> 标识符 → _标识符头(Head)_ _标识符字符集_ 可选
> 标识符 → _隐式参数名_
> 标识符集 → _标识符_ | _标识符_ , _标识符集_
> 标识符头(Head) → Upper- or lowercase letter A through Z
> _标识符头(Head)_ → _
> _标识符头(Head)_ → U+00A8, U+00AA, U+00AD, U+00AF, U+00B2–U+00B5, or U+00B7–U+00BA
> 标识符头(Head) → U+00BC–U+00BE, U+00C0–U+00D6, U+00D8–U+00F6, or U+00F8–U+00FF
> 标识符头(Head) → U+0100–U+02FF, U+0370–U+167F, U+1681–U+180D, or U+180F–U+1DBF
> 标识符头(Head) → U+1E00–U+1FFF
> 标识符头(Head) → U+200B–U+200D, U+202A–U+202E, U+203F–U+2040, U+2054, or U+2060–U+206F
> 标识符头(Head) → U+2070–U+20CF, U+2100–U+218F, U+2460–U+24FF, or U+2776–U+2793
> 标识符头(Head) → U+2C00–U+2DFF or U+2E80–U+2FFF
> 标识符头(Head) → U+3004–U+3007, U+3021–U+302F, U+3031–U+303F, or U+3040–U+D7FF
> 标识符头(Head) → U+F900–U+FD3D, U+FD40–U+FDCF, U+FDF0–U+FE1F, or U+FE30–U+FE44
> 标识符头(Head) → U+FE47–U+FFFD
> 标识符头(Head) → U+10000–U+1FFFD, U+20000–U+2FFFD, U+30000–U+3FFFD, or U+40000–U+4FFFD
> 标识符头(Head) → U+50000–U+5FFFD, U+60000–U+6FFFD, U+70000–U+7FFFD, or U+80000–U+8FFFD
> 标识符头(Head) → U+90000–U+9FFFD, U+A0000–U+AFFFD, U+B0000–U+BFFFD, or U+C0000–U+CFFFD
> 标识符头(Head) → U+D0000–U+DFFFD or U+E0000–U+EFFFD
> 标识符字符 → 数值 0 到 9
> 标识符字符 → U+0300–U+036F, U+1DC0–U+1DFF, U+20D0–U+20FF, or U+FE20–U+FE2F
> 标识符字符 → _标识符头(Head)_
> 标识符字符集 → _标识符字符_ _标识符字符集_ 可选
> 隐式参数名 → $ _十进制数字集_
### 字面量语法
> 字面量 → _数值型字面量_ | _字符串字面量_ | _布尔字面量_ | _空字面量_
> _数值型字面量_ → - 可选 _整形字面量_ | - 可选 _浮点型字面量_
> _布尔字面量_ → true | false
> _空字面量_ → nil
### 整型字面量语法
> 整型字面量 → _二进制字面量_
> 整型字面量 → _八进制字面量_
> 整型字面量 → _十进制字面量_
> 整型字面量 → _十六进制字面量_
> 二进制字面量 → 0b _二进制数字_ _二进制字面量字符集_ 可选
> 二进制数字 → 数值 0 到 1
> 二进制字面量字符 → _二进制数字_ | _
> 二进制字面量字符集 → _二进制字面量字符_ _二进制字面量字符集_ 可选
> 八进制字面量 → 0o _八进制数字_ _八进制字符集_ 可选
> 八进字数字 → 数值 0 到 7
> 八进制字符 → _八进制数字_ | _
> 八进制字符集 → _八进制字符_ _八进制字符集_ 可选
> 十进制字面量 → _十进制数字_ _十进制字符集_ 可选
> 十进制数字 → 数值 0 到 9
> 十进制数字集 → _十进制数字_ _十进制数字集_ 可选
> 十进制字面量字符 → _十进制数字_ | _
> 十进制字面量字符集 → _十进制字面量字符_ _十进制字面量字符集_ 可选
> 十六进制字面量 → 0x _十六进制数字_ _十六进制字面量字符集_ 可选
> 十六进制数字 → 数值 0 到 9, a through f, or A through F
> 十六进制字符 → _十六进制数字_ | _
> 十六进制字面量字符集 → _十六进制字符_ _十六进制字面量字符集_ _可选_
### 浮点型字面量语法
> 浮点数字面量 → _十进制字面量_ _十进制分数_ 可选 _十进制指数_ 可选
> 浮点数字面量 → _十六进制字面量_ _十六进制分数_ 可选 _十六进制指数_
> 十进制分数 → . _十进制字面量_
> 十进制指数 → _浮点数e_ _正负号_ 可选 _十进制字面量_
> 十六进制分数 → . _十六进制数_
> _十六进制字面量字符集__可选_
> 十六进制指数 → _浮点数p_ _正负号_ 可选 _十六进制字面量_
> 浮点数e → e | E
> 浮点数p → p | P
> 正负号 → + | -
### 字符串型字面量语法
> 字符串字面量 → " _引用文本_ "
> 引用文本 → _引用文本条目_ _引用文本_ 可选
> 引用文本条目 → _转义字符_
> 引用文本条目 → ( _表达式_ )
> 引用文本条目 → 除了", \, U+000A, or U+000D的所有Unicode的字符
> 转义字符 → /0 | \ | \t | \n | \r | \" | \'
> 转义字符 → \u { _十六进制标量数字集_ }
> unicode标量数字集 → Between one and eight hexadecimal digits
### 运算符语法语法
> 运算符 → _运算符头_ _运算符字符集_ 可选 运算符 → _点运算符头_ _点运算符字符集_ 可选
> 运算符字符 → / | = | - | + | ! | * | % | | > | & | | | ^ | ~ | ?
> 运算符头 → U+00A1–U+00A7
> _运算符头_ → U+00A9 or U+00AB
> _运算符头_ → U+00AC or U+00AE
> _运算符头_ → U+00B0–U+00B1, U+00B6, U+00BB, U+00BF, U+00D7, or U+00F7
> _运算符头_ → U+2016–U+2017 or U+2020–U+2027
> _运算符头_ → U+2030–U+203E
> _运算符头_ → U+2041–U+2053
> _运算符头_ → U+2055–U+205E
> _运算符头_ → U+2190–U+23FF
> _运算符头_ → U+2500–U+2775
> _运算符头_ → U+2794–U+2BFF
> _运算符头_ → U+2E00–U+2E7F
> _运算符头_ → U+3001–U+3003
> _运算符头_ → U+3008–U+3030
> _运算符字符_ → _运算符头_
> _运算符字符_ → U+0300–U+036F
> _运算符字符_ → U+1DC0–U+1DFF
> _运算符字符_ → U+20D0–U+20FF
> _运算符字符_ → U+FE00–U+FE0F
> _运算符字符_ → U+FE20–U+FE2F
> _运算符字符_ → U+E0100–U+E01EF
> _运算符字符集_ → _运算符字符_ _运算符字符集__可选_
> _点运算符头_ → ..
> _点运算符字符_ → . | _运算符字符_
> _点运算符字符集_ → _点运算符字符_ _点运算符字符集_ _可选_
> _二元运算符_ → _运算符_
> 前置运算符 → _运算符_
> 后置运算符 → _运算符_
## 类型
### 类型语法
> 类型 → _数组类型_ | _字典类型_ | _函数类型_ | _类型标识符_ | _元组类型_ | _可选类型_ | _隐式解析可选类型_ | _协议合成类型_ | _元型类型_
### 类型注解语法
> 类型注解 → : _属性(Attributes)集_ 可选 _类型_
### 类型标识语法
> 类型标识 → _类型名称_ _泛型参数从句_ 可选 | _类型名称_ _泛型参数从句_ 可选 . _类型标识符_
> 类型名 → _标识符_
### 元组类型语法
> 元组类型 → ( _元组类型主体_ 可选 )
> 元组类型主体 → _元组类型的元素集_ ... 可选
> 元组类型的元素集 → _元组类型的元素_ | _元组类型的元素_ , _元组类型的元素集_
> 元组类型的元素 → _属性(Attributes)集_ 可选 inout 可选 _类型_ | inout 可选 _元素名_ _类型注解_
> 元素名 → _标识符_
### 函数类型语法
> 函数类型 → _类型_ throws 可选 -> _类型_
> 函数类型 → _类型_ rethrows -> _类型_
### 数组类型语法
> 数组类型 → [ _类型_ ]
### 字典类型语法
> 字典类型 → [ _类型 : 类型_ ]
### 可选类型语法
> 可选类型 → _类型_ ?
> 隐式解析可选类型(Implicitly Unwrapped Optional Type)语法
> 隐式解析可选类型 → _类型_ !
### 协议合成类型语法
> 协议合成类型 → protocol _协议标识符集_ 可选 >
> 协议标识符集 → _协议标识符_ | _协议标识符_ , _协议标识符集_
> 协议标识符 → _类型标识符_
### 元(Metatype)类型语法
> 元类型 → _类型_ . Type | _类型_ . Protocol
### 类型继承从句语法
> _类型继承从句_ → : _类条件(class-requirement))_ , _类型继承集_
> _类型继承从句_ → : _类条件(class-requirement))_
> _类型继承从句_ → : _类型继承集_
> _类型继承集_ → _类型标识符_ | _类型标识符_ , _类型继承集_
> _类条件_ → class
泛型参数
最后更新于:2022-04-01 05:05:06
> 1.0 翻译:[fd5788](https://github.com/fd5788) 校对:[yankuangshi](https://github.com/yankuangshi), [stanzhai](https://github.com/stanzhai)
>
> 2.0 翻译+校对:[wardenNScaiyi](https://github.com/wardenNScaiyi)
本页包含内容:
[TOC]
本节涉及泛型类型、泛型函数以及泛型初始化器(initializer)的参数,包括形参和实参。声明泛型类型、函数或初始化器时,须指定相应的类型参数。类型参数相当于一个占位符,当实例化泛型类型、调用泛型函数或泛型初始化器时,就用具体的类型实参替代之。
关于 Swift 语言的泛型概述,见泛型(第二部分第23章)。
## 泛型形参子句
泛型形参子句指定泛型类型或函数的类型形参,以及这些参数的关联约束和关联类型要求(requirement)。泛型形参子句用尖括号(<>)包住,并且有以下两种形式:
>
>
泛型形参列表中泛型形参用逗号分开,其中每一个采用以下形式:
> `类型形参` : `约束`
泛型形参由两部分组成:类型形参及其后的可选约束。类型形参只是占位符类型(如 T,U,V,Key,Value 等)的名字而已。你可以在泛型类型、函数的其余部分或者初始化器声明,包括函数或初始化器的签名中使用它(与其任何相关类型)。
约束用于指明该类型形参继承自某个类或者遵守某个协议或协议的一部分。例如,在下面的泛型函数中,泛型形参`T: Comparable`表示任何用于替代类型形参`T`的类型实参必须满足`Comparable`协议。
~~~
func simpleMax<T: Comparable>(x: T, _ y: T) -> T {
if x < y {
return y
}
return x
}
~~~
如,`Int`和`Double`均满足`Comparable`协议,该函数接受任何一种类型。与泛型类型相反,调用泛型函数或初始化器时不需要指定泛型实参子句。类型实参由传递给函数或初始化器的实参推断而出。
~~~
simpleMax(17, 42) // T被推断出为Int类型
simpleMax(3.14159, 2.71828) // T被推断出为Double类型
~~~
## Where 子句
要想对类型形参及其关联类型指定额外关联类型要求,可以在泛型形参列表之后添加`where`子句。`where`子句由关键字`where`及其后的用逗号分割的多个关联类型要求组成。
`where`子句中的关联关系用于指明该类型形参继承自某个类或遵守某个协议或协议的一部分。尽管`where`子句提供了语法糖使其有助于表达类型形参上的简单约束(如`T: Comparable`等同于`T where T: Comparable`,等等),但是依然可以用来对类型形参及其关联类型提供更复杂的约束。如,`<T where T: C, T: P>`表示泛型类型`T`继承自类`C`且遵守协议`P`。
如上所述,可以强制约束类型形参的关联类型遵守某个协议。例如`<T: Generator where T.Element: Equatable>`表示`T`遵守`Generator`协议,而且`T`的关联类型`T.Element`遵守`Eauatable`协议(`T`有关联类型`Element`是因为`Generator`声明了`Element`,而`T`遵守`Generator`协议)。
也可以用操作符`==`来指定两个类型等效的关联关系。例如,有这样一个约束:`T`和`U`遵守`Generator`协议,同时要求它们的关联类型等同,可以这样来表达:`<T: Generator, U: Generator where T.Element == U.Element>`。
当然,替代类型形参的类型实参必须满足所有类型形参的约束和关联类型要求。
泛型函数或初始化器可以重载,但在泛型形参子句中的类型形参必须有不同的约束或关联类型要求,抑或二者皆不同。当调用重载的泛型函数或始化器时,编译器会用这些约束来决定调用哪个重载函数或始化器。
> 泛型形参子句语法
> 泛型参数子句 → _泛型参数列表_ _约束子句_ 可选 >
> 泛型参数列表 → _泛形参数_ | _泛形参数_ , _泛型参数列表_
> 泛形参数 → _类型名称_
> 泛形参数 → _类型名称_ : _类型标识_
> 泛形参数 → _类型名称_ : _协议合成类型_
> 约束子句 → where _约束列表_
> 约束列表 → _约束_ | _约束_ , _约束列表_
> 约束 → _一致性约束_ | _同类型约束_
> 一致性约束 → _类型标识_ : _类型标识_
> 一致性约束 → _类型标识_ : _协议合成类型_
> 同类型约束 → _类型标识_ == _类型标识_
## 泛型实参子句
泛型实参子句指定_泛型类型_的类型实参。泛型实参子句用尖括号(<>)包住,形式如下:
>
泛型实参列表中类型实参有逗号分开。类型实参是实际具体类型的名字,用来替代泛型类型的泛型形参子句中的相应的类型形参。从而得到泛型类型的一个特化版本。如,Swift标准库的泛型字典类型定义如下:
~~~
struct Dictionary<KeyTypel: Hashable, ValueType>: Collection, DictionaryLiteralConvertible {
/* .. */
}
~~~
泛型`Dictionary`类型的特化版本,`Dictionary<String, Int>`就是用具体的`String`和`Int`类型替代泛型类型`KeyType: Hashable`和`ValueType`产生的。每一个类型实参必须满足它所替代的泛型形参的所有约束,包括任何`where`子句所指定的额外的关联类型要求。上面的例子中,类型形参`Key`类型要求满足`Hashable`协议,因此`String`也必须满足`Hashable`协议。
可以用本身就是泛型类型的特化版本的类型实参替代类型形参(假设已满足合适的约束和关联类型要求)。例如,为了生成一个元素类型是整型数组的数组,可以用数组的特化版本`Array<Int>`替代泛型类型`Array<T>`的类型形参 `T` 来实现。
~~~
let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
~~~
如泛型形参子句所述,不能用泛型实参子句来指定泛型函数或初始化器的类型实参。
> 泛型实参子句语法
> (泛型参数子句Generic Argument Clause) → _泛型参数列表_ >
> 泛型参数列表 → _泛型参数_ | _泛型参数_ , _泛型参数列表_
> 泛型参数 → _类型_
模式
最后更新于:2022-04-01 05:05:04
> 1.0 翻译:[honghaoz](https://github.com/honghaoz) 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
>
> 2.0 翻译+校对:[ray16897188](https://github.com/ray16897188)
本页内容包括:
[TOC]
模式(pattern)代表了单个值或者复合值的结构。例如,元组`(1, 2)`的结构是逗号分隔的,包含两个元素的列表。因为模式代表一种值的结构,而不是特定的某个值,你可以把模式和各种同类型的值匹配起来。比如,`(x, y)`可以匹配元组`(1, 2)`,以及任何含两个元素的元组。除了将模式与一个值匹配外,你可以从复合值中提取出部分或全部,然后分别把各个部分和一个常量或变量绑定起来。
swift语言中模式有2个基本的分类:一类能成功和任何值的类型相匹配,另一类在运行时(runtime)和某特定值匹配时可能会失败。
第一类模式用于解构简单变量,常量和可选绑定中的值。此类模式包括通配符模式(wildcard patterns),标识符模式(identifier patterns),以及任何包含了它们的值绑定模式(value binding patterns)或者元祖模式(tuple patterns)。你可以为这类模式指定一个类型标注(type annotation)从而限制它们只能匹配某种特定类型的值。
第二类模式用于全模式匹配,这种情况下你用来相比较的值在运行时可能还不存在。此类模式包括枚举用例模式(enumeration case patterns),可选模式(optional patterns),表达式模式(expression patterns)和类型转换模式(type-casting patterns)。你在`switch`语句的case标签中,`do`语句的`catch`从句中,或者在`if, while, guard`和`for-in`语句的case条件句中使用这类模式。
> 模式(Patterns) 语法
> 模式 → _通配符模式_ _类型标注_ 可选
> 模式 → _标识符模式_ _类型标注_ 可选
> 模式 → _值绑定模式_
> 模式 → _元组模式_ _类型标注_ 可选
> 模式 → _枚举用例模式_
> 模式 → _可选模式_
> 模式 → _类型转换模式_
> 模式 → _表达式模式_
## 通配符模式(Wildcard Pattern)
通配符模式由一个下划线(_)构成,且匹配并忽略任何值。当你不在乎被匹配的值时可以使用该模式。例如,下面这段代码在闭区间`1...3`中循环,每次循环时忽略该区间内的当前值:
~~~
for _ in 1...3 {
// Do something three times.
}
~~~
> 通配符模式语法
> _通配符模式_ → _
## 标识符模式(Identifier Pattern)
标识符模式匹配任何值,并将匹配的值和一个变量或常量绑定起来。例如,在下面的常量声明中,`someValue`是一个标识符模式,匹配了类型是`Int`的`42`。
~~~
let someValue = 42
~~~
当匹配成功时,`42`被绑定(赋值)给常量`someValue`。
如果一个变量或常量声明的左边的模式是一个标识符模式,那么这个标识符模式是一个隐式的值绑定模式(value-binding pattern)。
> 标识符模式语法
> 标识符模式 → _标识符_
## 值绑定模式(Value-Binding Pattern)
值绑定模式把匹配到的值绑定给一个变量或常量名。把绑定匹配到的值绑定给常量时,用关键字`let`,绑定给变量时,用关键字`var`。
在值绑定模式中的标识符模式会把新命名的变量或常量与匹配值做绑定。例如,你可以拆开一个元组的元素,然后把每个元素绑定到其相应一个的标识符模式中。
~~~
let point = (3, 2)
switch point {
// Bind x and y to the elements of point.
case let (x, y):
print("The point is at (\(x), \(y)).")
}
// prints "The point is at (3, 2).”
~~~
在上面这个例子中,`let`将元组模式`(x, y)`分配到各个标识符模式。正是由于这么做,`switch`语句中`case let (x, y):`和`case (let x, let y):`匹配到的值是一样的。
> 值绑定(Value Binding)模式语法
> 值绑定模式 → var _模式_ | let _模式_
## 元组模式(Tuple Pattern)
元组模式是逗号分隔的,有零个或多个模式的列表,并被一对圆括号括起来。元组模式匹配相应元组类型的值。
你可以使用类型标注去限制一个元组模式能匹配哪些种元组类型。例如,在常量声明`let (x, y): (Int, Int) = (1, 2)`中的元组模式`(x, y): (Int, Int)`只匹配两个元素都是`Int`这种类型的元组。如果仅需要限制一个元组模式中的某几个元素,只需要直接对这几个元素提供类型标注即可。例如,在`let (x: String, y)`中的元组模式可以和任何有两个元素,且第一个元素类型是`String`的元组类型匹配。
当元组模式被用在`for-in`语句或者变量或常量声明时,它仅可以包含通配符模式,标识符模式,可选模式或者其他包含这些模式的元祖模式。比如下面这段代码就不正确,因为`(x, 0)`中的元素`0`是一个表达式模式:
~~~
let points = [(0, 0), (1, 0), (1, 1), (2, 0), (2, 1)]
// This code isn't valid.
for (x, 0) in points {
/* ... */
}
~~~
对于只包含一个元素的元组,括号是不起作用的。模式只匹配这个单个元素的类型。举例来说,下面3条语句是等效的:
~~~
let a = 2 // a: Int = 2
let (a) = 2 // a: Int = 2
let (a): Int = 2 // a: Int = 2
~~~
> 元组模式语法
> 元组模式 → ( _元组模式元素列表_ 可选 )
> 元组模式元素列表 → _元组模式元素_ | _元组模式元素_ , _元组模式元素列表_
> 元组模式元素 → _模式_
## 枚举用例模式(Enumeration Case Pattern)
一个枚举用例模式匹配现有的某个枚举类型的某个用例(case)。枚举用例模式出现在`switch`语句中的case标签中,以及`if`,`while`,`guard`和`for-in`语句的case条件中。
如果你准备匹配的枚举用例有任何关联的值,则相应的枚举用例模式必须指定一个包含每个关联值元素的元组模式。关于使用`switch`语句来匹配包含关联值枚举用例的例子,请参阅`Associated Values`.
> 枚举用例模式语法
> enum-case-pattern → _类型标识_ 可选 . _枚举的case名_ _元组模式_ _可选_
## 可选模式(Optional Pattern)
可选模式与封装在一个`Optional(T)`或者一个`ExplicitlyUnwrappedOptional(T)`枚举中的`Some(T)`用例相匹配。可选模式由一个标识符模式和紧随其后的一个问号组成,在某些情况下表现为枚举用例模式。
由于可选模式是`optional`和`ImplicitlyUnwrappedOptional`枚举用例模式的语法糖(syntactic sugar),下面的2种写法是一样的:
~~~
let someOptional: Int? = 42
// Match using an enumeration case pattern
if case .Some(let x) = someOptional {
print(x)
}
// Match using an optional pattern
if case let x? = someOptional {
print(x)
}
~~~
如果一个数组的元素是可选类型,可选模式为`for-in`语句提供了一种在该数组中迭代的简便方式,只为数组中的非空`non-nil`元素执行循环体。
~~~
let arrayOfOptionalInts: [Int?] = [nil, 2, 3, nil, 5]
// Match only non-nil values
for case let number? in arrayOfOptinalInts {
print("Found a \(number)")
}
//Found a 2
//Found a 3
//Found a 5
~~~
> 可选模式语法
> 可选模式 → _标识符模式_ ?
## 类型转换模式(Type-Casting Patterns)
有两种类型转换模式,`is`模式和`as`模式。这两种模式只出现在`switch`语句中的case标签中。`is`模式和`as`模式有以下形式:
> is `type`
> `pattern` as `type`
`is`模式仅当一个值的类型在运行时(runtime)和`is`模式右边的指定类型一致 - 或者是该类型的子类 - 的情况下,才会匹配这个值。`is`模式和`is`操作符有相似表现,它们都进行类型转换,却舍弃返回的类型。
`as`模式仅当一个值的类型在运行时(runtime)和`as`模式右边的指定类型一致 - 或者是该类型的子类 - 的情况下,才会匹配这个值。如果匹配成功,被匹配的值的类型被转换成`as`模式左边指定的模式。
关于使用`switch`语句来匹配`is`模式和`as`模式值的例子,请参阅`Type Casting for Any and AnyObject`。
> 类型转换模式语法
> type-casting-pattern → _is模式_ | _as模式_
> is模式 → is _类型_
> as模式 → _模式_ as _类型_
## 表达式模式(Expression Pattern)
一个表达式模式代表了一个表达式的值。表达式模式只出现在`switch`语句中的`case`标签中。
由表达式模式所代表的表达式与使用了Swift标准库中`~=`操作符的输入表达式的值进行比较。如果`~=`操作符返回`true`,则匹配成功。默认情况下,`~=`操作符使用`==`操作符来比较两个相同类型的值。它也可以将一个整型数值与一个`Range`对象中的一段整数区间做匹配,正如下面这个例子所示:
~~~
let point = (1, 2)
switch point {
case (0, 0):
print("(0, 0) is at the origin.")
case (-2...2, -2...2):
print("(\(point.0), \(point.1)) is near the origin.")
default:
print("The point is at (\(point.0), \(point.1)).")
}
// prints "(1, 2) is near the origin.”
~~~
你可以重载`~=`操作符来提供自定义的表达式匹配行为。比如你可以重写上面的例子,拿`point`表达式去比较字符串形式的点。
~~~
// Overload the ~= operator to match a string with an integer
func ~=(pattern: String, value: Int) -> Bool {
return pattern == "\(value)"
}
switch point {
case ("0", "0"):
print("(0, 0) is at the origin.")
default:
print("The point is at (\(point.0), \(point.1)).")
}
// prints "(1, 2) is near the origin.”
~~~
> 表达式模式语法
> 表达式模式 → _表达式_
特性
最后更新于:2022-04-01 05:05:02
> 1.0 翻译:[Hawstein](https://github.com/Hawstein) 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
>
> 2.0 翻译+校对:[KYawn](https://github.com/KYawn)
本页内容包括:
[TOC=2]
特性提供了关于声明和类型的更多信息。在Swift中有两类特性,用于修饰声明的以及用于修饰类型的。
通过以下方式指定一个特性:符号`@`后面跟特性名,如果包含参数,则把参数带上:
> @`attribute name`
> @`attribute name`(`attribute arguments`)
有些声明特性通过接收参数来指定特性的更多信息以及它是如何修饰一个特定的声明的。这些特性的参数写在小括号内,它们的格式由它们所属的特性来定义。
## 声明特性
声明特性只能应用于声明。然而,你也可以将`noreturn`特性应用于函数或方法类型。
`autoclosure`
这个特性通过把表达式自动封装成无参数的闭包来延迟表达式的计算。它可以声明返回表达式自身类型的没有参数的方法类型,也可以用于函数参数的声明。含有`autoclosure`特性的声明同时也具有`noescape`的特性,除非传递可选参数`escaping`.关于怎样使用`autoclosure`特性的例子,参见[函数类型](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#function_type).
`available`
将`available`特性用于声明时,意味着该声明的生命周期会依赖于特定的平台和操作系统版本。
`available`特性经常与参数列表一同出现,该参数列表至少有两个参数,参数之间由逗号分隔。这些参数由以下这些平台名字中的一个起头:
* `iOS`
* `iOSApplicationExtension`
* `OSX`
* `OSXApplicationExtension`
* `watchOS`
当然,你也可以用一个星号(*)来表示,该声明在上面提到的所有平台上都是有效的。
剩下的参数,可以以任何顺序出现,并且可以添加关于声明生命周期的附加信息,包括重要的里程碑。
* `unavailable`参数表示:该声明在特定的平台上是无效的
* `introduced`参数表示:该声明第一次被引入时所在平台的版本。格式如下:
`introduced=version number`
* `deprecated`参数表示:该声明第一次被建议弃用时所在平台的版本。格式如下:
`deprecated=version number`
* `obsoleted`参数表示:该声明第一次被弃用时所在平台的版本。当一个声明被弃用时,它就从此平台中被移除,不能再被使用。格式如下:
`obsoleted=version number`
* `message`参数用来提供文本信息。当使用建议弃用或者被弃用的声明时,编译器会抛出错误或警告信息。格式如下:
`message=message`
* `renamed`参数用来提供文本信息,用以表示被重命名的声明的新名字。当使用这个重命名的声明遇到错误时,编译器会显示出该新名字。格式如下:
`renamed=new name`
你可以将`renamed`参数和`unavailable`参数以及类型别名声明组合使用,以向用户表示:在你的代码中,一个声明已经被重命名。当一个声明的名字在一个框架或者库的不同发布版本间发生变化时,这会相当有用。
~~~
// First release
protocol MyProtocol {
// protocol definition
}
// Subsequent release renames MyProtocol
protocol MyRenamedProtocol {
// protocol definition
}
@available(*, unavailable, renamed="MyRenamedProtocol")
typealias MyProtocol = MyRenamedProtocol
~~~
你可以在一个单独的声明上使用多个`available`特性,以详细说明该声明在不同平台上的有效性。编译器只有在当前的目标平台和`available`特性中指定的平台匹配时,才会使用`available`特性
如果`available`特性除了平台名称参数外,只指定了一个`introduced`参数,那么可以使用以下简写语法代替:
@available(`platform name` `version number`, *)
`available`特性的简写语法可以简明地表达出多个平台的可用性。尽管这两种形式在功能上是相同的,但请尽可能地使用简明语法形式。
~~~
@available(iOS 8.0, OSX 10.10, *)
class MyClass {
// class definition
}
~~~
`objc`
该特性用于修饰任何可以在Objective-C中表示的声明。比如,非嵌套类、协议、非泛型枚举(仅限整型值类型)、类和协议的属性和方法(包括`getter`和`setter`)、构造器、析构器以及下标。`objc`特性告诉编译器这个声明可以在Objective-C代码中使用。
标有`objc`特性的类必须继承自Objective-C中定义的类。如果你将`objc`特性应用于一个类或协议,它也会隐式地应用于那个类的成员或协议。对于标记了`objc`特性的类,编译器会隐式地为它的子类添加`objc`特性。标记了`objc`特性的协议不能继承没有标记`objc`的协议。
如果你将`objc`特性应用于枚举,每一个枚举的`case`都会以枚举名称和`case`名称组合的方式暴露在Objective-C代码中。例如:一个名为`Venus`的`case`在`Planet`枚举中,这个`case`暴露在Objective-C代码中时叫做`PlanetVenus`。
`objc`特性有一个可选的参数,由标记符组成。当你想把`objc`所修饰的实体以一个不同的名字暴露给Objective-C时,你就可以使用这个特性参数。你可以使用这个参数来命名类,协议,方法,getters,setters,以及构造器。下面的例子把`ExampleClass`中`enabled`属性的getter暴露给Objective-C,名字是`isEnabled`,而不是它原来的属性名。
~~~
@objc
class ExampleClass {
var enabled: Bool {
@objc(isEnabled) get {
// Return the appropriate value
}
}
}
~~~
`noescape`
在函数或者方法声明上使用该特性,它表示参数将不会被存储用作后续的计算,其用来确保不会超出函数调用的生命周期。对于其属性或方法来说,使用`noescape`声明属性的函数类型不需要显式的使用`self.`。
`nonobjc`
该特性用于方法、属性、下标、或构造器的声明,这些声明本是可以在Objective-C代码中表示的。使用`nonobjc`特性告诉编译器这个声明不能在Objective-C代码中使用。
可以使用`nonobjc`特性解决标有`objc`的类中桥接方法的循环问题,该特性还允许标有`objc`的类的构造器和方法进行重载(overload)。
标有`nonobjc`特性的方法不能重写(override)一个标有`objc`特性的方法。然而,标有`objc`特性的方法可以重写标有`nonobjc`特性的方法。同样,标有`nonobjc`特性的方法不能满足一个需要标有`@objc`特性的方法的协议。
`noreturn`
该特性用于修饰函数或方法声明,表明该函数或方法的对应类型,`T`,是`@noreturn T`。你可以用这个特性修饰函数或方法的类型,这样一来,函数或方法就不会返回到它的调用者中去。
对于一个没有用`noreturn`特性标记的函数或方法,你可以将它重写为用该特性标记的。相反,对于一个已经用`noreturn`特性标记的函数或方法,你则不可以将它重写为没使用该特性标记的。当你在一个comforming类型中实现一个协议方法时,该规则同样适用。
`NSApplicationMain`
在类上使用该特性表示该类是应用程序委托类,使用该特性与调用`NSApplicationMain(_:_:)`函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
如果你不想使用这个特性,可以提供一个`main.swift`文件,并且提供一个`main`函数去调用`NSApplicationMain(_:_:)`函数。比如,如果你的应用程序使用一个派生于`NSApplication`的自定义子类作为主要类,你可以调用`NSApplicationMain`函数而不是使用该特性。
`NSCopying`
该特性用于修饰一个类的存储型变量属性。该特性将使属性的setter与属性值的一个副本合成,这个值由`copyWithZone(_:)`方法返回,而不是属性本身的值。该属性的类型必需遵循`NSCopying`协议。
`NSCopying`特性的原理与Objective-C中的`copy`特性相似。
`NSManaged`
该特性用于修饰`NSManagedObject`子类中的存储型变量属性,表明属性的存储和实现由Core Data在运行时基于相关实体描述动态提供。
`testable`
该特性用于`import`声明可以测试的编译模块,它能访问任何标有`internal`权限标识符的实体,这和将它声明为`public`权限标识符有同样的效果。
`UIApplicationMain`
在类上使用该特性表示该类是应用程序委托类,使用该特性与调用`UIApplicationMain(_:_:)`函数并且把该类的名字作为委托类的名字传递给函数的效果相同。
如果你不想使用这个特性,可以提供一个`main.swift`文件,并且提供一个`main`函数去调用`UIApplicationMain(_:_:)`函数。比如,如果你的应用程序使用一个派生于`UIApplication`的自定义子类作为主要类,你可以调用`UIApplicationMain`函数而不是使用该特性。
`warn_unused_result`
该特性应用于方法或函数声明,当方法或函数被调用,但其结果未被使用时,该特性会让编译器会产生警告。
你可以使用这个特性提供一个警告信息,这个警告信息是关于不正确地使用未变异的方法的,这个方法也有一个对应的变异方法。
`warn_unused_result`有下面两个可选的参数。
* `message`参数用来提供警告信息,并在因当方法或函数被调用,但其结果未被使用时,显示警告信息。格式如下:
`message=message`
* `mutable_variant`参数用于提供变异方法的名称,如果未变异方法以一个可变的值被调用而且其结果并未被使用时,应该使用此变异方法。格式如下(方法名有字符串构成):
`mutable_variant=method name`
比如,Swift标准库提供了变异方法`sortInPlace()`和未变异方法`sort()`集合,它们的元素生成器符合`Comparable`协议。如果你调用了`sort()`方法,而没有使用它的结果,很有可能,你打算使用变异方法`sortInPlace()`替代。
### Interface Builder使用的声明特性
Interface Builder特性是Interface Builder用来与Xcode同步的声明特性。Swift提供了以下的Interface Builder特性:`IBAction`,`IBDesignable`,`IBInspectable`,以及`IBOutlet`。这些特性与Objective-C中对应的特性在概念上是相同的。
`IBOutlet`和`IBInspectable`用于修饰一个类的属性声明;`IBAction`特性用于修饰一个类的方法声明;`IBDesignable`用于修饰类的声明。
## 类型特性
类型特性只能用于修饰类型。然而,你也可以用`noreturn`特性去修饰函数或方法声明。
`convention`
该特性用于函数的类型,它指出函数调用的约定。
`convention`特性有下面几个可选的参数。
* `swift`参数用于表明一个Swift函数引用。这是Swift中标准的函数值调用约定。
* `block`参数用于表明一个Objective-C兼容的块引用。函数值表示为一个块对象的引用,这是一个`id-`兼容的Objective-C对象,对象中嵌入了调用函数。调用函数使用C的调用约定。
* `c`参数用于表明一个C函数引用。函数值没有上下文,这个函数也使用C的调用约定。
使用C函数调用约定的函数也可用作使用Objective-C块调用约定的函数,同时使用Objective-C块调用约定的函数也可用作使用Swift函数调用约定的函数。然而,只有非泛型的全局函数和本地函数或者不使用任何本地变量的闭包可以被用作使用C函数调用约定的函数。
`noreturn`
该特性用于修饰函数或方法的类型,表明该函数或方法不会返回到它的调用者中去。你也可以用它标记函数或方法的声明,表示函数或方法的相应类型,`T`,是`@noreturn T`。
> 特性语法 特性 → @ _特性名_ _特性参数子句_ 可选
> 特性名 → _标识符_
> 特性参数子句 → ( _平衡令牌列表_ 可选 )
> 特性(Attributes)列表 → _特色_ _特性(Attributes)列表_ 可选
> 平衡令牌列表 → _平衡令牌_ _平衡令牌列表_ 可选
> 平衡令牌 → ( _平衡令牌列表_ 可选 )
> 平衡令牌 → [ _平衡令牌列表_ 可选 ]
> 平衡令牌 → { _平衡令牌列表_ 可选 }
> 平衡令牌 → 任意标识符, 关键字, 字面量或运算符
> 平衡令牌 → 任意标点除了(, ), [, ], {, 或 }
声明
最后更新于:2022-04-01 05:04:59
> 1.0 翻译:[marsprince](https://github.com/marsprince) [Lenhoon](https://github.com/marsprince)[(微博)](http://www.weibo.com/lenhoon) 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
>
> 2.0 翻译+校对:[Lenhoon](https://github.com/Lenhoon)
本页包含内容:
[TOC=2]
一条_声明(declaration)_可以在程序里引入新的名字或者构造。举例来说,可以使用声明来引入函数和方法,变量和常量,或者来定义新的命名好的枚举,结构,类和协议类型。可以使用一条声明来延长一个已经存在的命名好的类型的行为。或者在程序里引入在其它地方声明的符号。
在Swift中,大多数声明在某种意义上讲也是执行或同时声明它们的初始化定义。这意味着,因为协议和它们的成员不匹配,大多数协议成员需要单独的声明。为了方便起见,也因为这些区别在Swift里不是很重要,_声明语句(declaration)_同时包含了声明和定义。
> 声明语法
> 声明 → _导入声明_
> 声明 → _常量声明_
> 声明 → _变量声明_
> 声明 → _类型别名声明_
> 声明 → _函数声明_
> 声明 → _枚举声明_
> 声明 → _结构体声明_
> 声明 → _类声明_
> 声明 → _协议声明_
> 声明 → _构造器声明_
> 声明 → _析构器声明_
> 声明 → _扩展声明_
> 声明 → _附属脚本声明_
> 声明 → _运算符声明_
> 声明(Declarations)列表 → _声明_ _声明(Declarations)列表_ _可选_
## 顶级代码
Swift的源文件中的顶级代码由零个或多个语句,声明和表达式组成。默认情况下,在一个源文件的顶层声明的变量,常量和其他命名的声明语句可以被同一模块部分里的每一个源文件中的代码访问。可以通过使用一个访问级别修饰符来标记这个声明,从而重写这个默认行为,[访问控制级别(Access Control Levels)](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#access_control_levels)中有所介绍。
> 顶级(Top Level) 声明语法
> _顶级声明_ → [_多条语句(Statements)_](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO) _可选_
## 代码块
_代码块_用来将一些声明和控制结构的语句组织在一起。它有如下的形式:
> {
> `statements`
> }
代码块中的_语句(statements)_包括声明,表达式和各种其他类型的语句,它们按照在源码中的出现顺序被依次执行。
> 代码块语法
> _代码块_ → { _多条语句(Statements)_ _可选_ }
## 引入声明
可以使用在其他文件中声明的内容_引入声明(import declaration)_。引入语句的基本形式是引入整个代码模块;它由`import`关键字开始,后面 紧跟一个模块名:
> import `module`
可以提供更多的细节来限制引入的符号,如声明一个特殊的子模块或者在一个模块或子模块中做特殊的声明。(待改进) 当使用了这些细节后,在当前的程序汇总只有引入的符号是可用的(并不是声明的整个模块)。
> import `import kind` `module`.`symbol name`
> import `module`.`submodule`
> 导入(Import)声明语法
> 导入声明 → _特性(attributes)列表_ 可选 import _导入类型_ 可选 _导入路径_
> 导入类型 → typealias | struct | class | enum | protocol | var | func
> 导入路径 → _导入路径标识符_ | _导入路径标识符_ . _导入路径_
> 导入路径标识符 → _标识符_ | _运算符_
## 常量声明
_常量声明(constant declaration)_可以在程序里命名一个常量。常量以关键词`let`来声明,遵循如下的格式:
> let `constant name`: `type` = `expression`
当常量的值被给定后,常量就将_常量名称(constant name)_和_表达式(expression)_初始值不变的结合在了一起,而且不能更改。
这意味着如果常量以类的形式被初始化,类本身的内容是可以改变的,但是常量和类之间的结合关系是不能改变的。
当一个常量被声明为全局变量,它必须被给定一个初始值。当一个常量在类或者结构体中被声明时,它被认为是一个_常量属性(constant property)_。常量并不是可计算的属性,因此不包含getters和setters。
如果_常量名(constant name)_是一个元组形式,元组中的每一项初始化_表达式(expression)_中都要有对应的值。
~~~
let (firstNumber, secondNumber) = (10, 42)
~~~
在上例中,`firstNumber`是一个值为`10`的常量,`secnodeName`是一个值为`42`的常量。所有常量都可以独立的使用:
~~~
println("The first number is /(firstNumber).")
// prints "The first number is 10."
println("The second number is /(secondNumber).")
// prints "The second number is 42."
~~~
当_常量名称(constant name)_的类型可以被推断出时,类型标注_(:type)_在常量声明中是一个可选项,它可以用来描述在类型推断(Type Inference)中找到的类型。
声明一个常量类型属性要使用关键字`static`声明修饰符。类型属性在类型属性(Type Properties)中有介绍。
如果还想获得更多关于常量的信息或者想在使用中获得帮助,请查看常量和变量(Constants and Variables)和存储属性(Stored Properties)等节。
> 常数声明语法
> 常量声明 → _特性(Attributes)列表_ 可选 _声明修饰符(Specifiers)列表_ 可选 let _模式构造器列表_
> 模式构造器列表 → _模式构造器_ | _模式构造器_ , _模式构造器列表_
> 模式构造器 → _模式_ _构造器_ 可选
> 构造器 → = _表达式_
## 变量声明
_变量声明(variable declaration)_可以在程序里声明一个变量,它以关键字`var`来声明。
变量声明有几种不同的形式声明不同种类的命名值和计算型值,如存储和计算变量和属性,存储变量和属性监视,和静态变量属性。所使用的声明形式取决于变量所声明的范围和打算声明的变量类型。
> 注意:
> 也可以在协议声明的上下文声明属性,详情参见协议属性声明(Protocal Property Declaration)。
可以重载一个子类中的属性,通过使用'override'声明修饰符来标记子类的属性声明,重写(Overriding)中有所介绍。
### 存储型变量和存储型属性
下面的形式声明了一个存储型变量或存储型变量属性
> var `variable name`: `type` = `expression`
可以在全局,函数内,或者在类和结构体的声明(context)中使用这种形式来声明一个变量。当变量以这种形式 在全局或者一个函数内被声明时,它代表一个 _存储型变量(stored variable)_ 。当它在类或者结构体中被声明时,它代表一个 _存储型变量属性(stored variable property)_ 。
初始化的 _表达式(expression)_ 不可以在协议的声明中出现,在其他情况下,初始化 _表达式(expression)_ 是可选的(optional),如果没有初始化 _表达式(expression)_ ,那么变量定义时必须显示包括类型标注 ( _:type_ )
对于常量的定义,如果 _变量名字(variable name)_ 是一个元组(tuple),元组中每一项的名称都要和初始化 _表达式(expression)_ 中的相应值一致。
正如名字一样,存储型变量的值或存储型变量属性存储在内存中。
### 计算型变量和计算型属性
如下形式声明一个一个存储型变量或存储型属性:
> var `variable name`: `type` {
> get {
> `statements`
> }
> set(`setter name`) {
> `statements`
> }
> }
可以在全局,函数体内或者类,结构体,枚举,扩展声明的上下文中使用这种形式的声明。当变量以这种形式在全局或者一个函数内被声明时,它代表一个_计算型变量(computed variable)_。当它在类,结构体,枚举,扩展声明的上下文中中被声明时,它代表一个 _计算型变量(computed variable)_ 。
getter用来读取变量值,setter用来写入变量值。setter子句是可选择的,只有getter是必需的,可以将这些语句 都省略,只是简单的直接返回请求值,正如在只读计算属性(Read-Only Computed Properties)中描述的那样。但是如果提供了一个setter语句,也必需提供一个getter语句。
setter的名字和圆括号内的语句是可选的。如果写了一个setter名,它就会作为setter的参数被使用。如果不写setter名,setter的初始名为'newValue',正如在setter声明速记(Shorthand Setter Declaration)中提到的那样。
不像存储型变量和存储型属性那样,计算型属性和计算型变量的值不存储在内存中。
获得更多信息,查看更多关于计算型属性的例子,请查看计算型属性(Computed Properties)一节。
### 存储型变量监视器和属性监视器
可以用`willset`和`didset`监视器来声明一个存储型变量或属性。一个包含监视器的存储型变量或属性按如下的形式声明:
> var `variable name`: `type` = expression {
> willSet(setter name) {
> `statements`
> }
> didSet(`setter name`) {
> `statements`
> }
> }
可以在全局,函数体内或者类,结构体,枚举,扩展声明的上下文中使用这种形式的声明。当变量以这种形式在全局或者一个函数内被声明时,监视器代表一个_存储型变量监视器(stored variable observers)_;当它在类,结构体,枚举,扩展声明的上下文中被声明时,监视器代表_属性监视器(property observers)_。
可以为适合的监视器添加任何存储型属性。也可以通过重写子类属性的方式为适合的监视器添加任何继承的属性 (无论是存储型还是计算型的),参见重写属性监视器(Overriding Property Observers)。
初始化_表达式(expression)_在一个类中或者结构体的声明中是可选的,但是在其他地方是必需的。当类型可以从初始化_表达式(expression)_中推断而来,那么这个_类型(type)_标注是可选的。
当变量或属性的值被改变时,`willset`和`didset`监视器提供了一个监视方法(适当的回应)。 监视器不会在变量或属性第一次初始化时运行,它们只有在值被外部初始化语句改变时才会被运行。
`willset`监视器只有在变量或属性值被改变之前运行。新的值作为一个常量经过过`willset`监视器,因此不可以在`willset`语句中改变它。`didset`监视器在变量或属性值被改变后立即运行。和`willset`监视器相反,为了以防止仍然需要获得旧的数据,旧变量值或者属性会经过`didset`监视器。这意味着,如果在变量或属性自身的`didiset`监视器语句中设置了一个值,设置的新值会取代刚刚在`willset`监视器中经过的那个值。
在`willset`和`didset`语句中,_setter名(setter name)_和圆括号的语句是可选的。如果写了一个setter名,它就会作为`willset`和`didset`的参数被使用。如果不写setter名, `willset`监视器初始名为`newvalue`,`didset`监视器初始名为`oldvalue`。
当提供一个`willset`语句时,`didset`语句是可选的。同样的,在提供了一个`didset`语句时,`willset`语句是可选的。
获得更多信息,查看如何使用属性监视器的例子,请查看属性监视器(Property Observers)一节。 声明修饰符
### 类型变量属性
声明一个类型变量属性,要用`static`声明修饰符标记该声明。类可能需要`class`声明修饰符去标记类的类型计算型属性从而允许子类可以重写超类的实现。类型属性在类型属性(Type Properties)章节讨论。
> > 注意
> >
> > 在一个类声明中,关键字`static`与用声明修饰符`class`和`final`去标记一个声明的效果相同
> 变量声明语法
> 变量声明 → _变量声明头(Head)_ _模式构造器列表_
> 变量声明 → _变量声明头(Head)_ _变量名_ _类型标注_ _代码块_
> 变量声明 → _变量声明头(Head)_ _变量名_ _类型标注_ _getter-setter块_
> 变量声明 → _变量声明头(Head)_ _变量名_ _类型标注_ _getter-setter关键字(Keyword)块_
> 变量声明 → _变量声明头(Head)_ _变量名_ _构造器_ _willSet-didSet代码块_
> 变量声明 → _变量声明头(Head)_ _变量名_ _类型标注_ _构造器_ 可选 _willSet-didSet代码块_
> 变量声明头(Head) → _特性(Attributes)列表_ 可选 _声明修饰符(Specifiers)列表_ 可选 var
> 变量名称 → _标识符_
> getter-setter块 → { _getter子句_ _setter子句_ 可选 }
> getter-setter块 → { _setter子句_ _getter子句_ }
> getter子句 → _特性(Attributes)列表_ 可选 get _代码块_
> setter子句 → _特性(Attributes)列表_ 可选 set _setter名称_ 可选 _代码块_
> setter名称 → ( _标识符_ )
> getter-setter关键字(Keyword)块 → { _getter关键字(Keyword)子句_ _setter关键字(Keyword)子句_ 可选 }
> getter-setter关键字(Keyword)块 → { _setter关键字(Keyword)子句_ _getter关键字(Keyword)子句_ }
> getter关键字(Keyword)子句 → _特性(Attributes)列表_ 可选 get
> setter关键字(Keyword)子句 → _特性(Attributes)列表_ 可选 set
> willSet-didSet代码块 → { _willSet子句_ _didSet子句_ 可选 }
> willSet-didSet代码块 → { _didSet子句_ _willSet子句_ }
> willSet子句 → _特性(Attributes)列表_ 可选 willSet _setter名称_ 可选 _代码块_
> didSet子句 → _特性(Attributes)列表_ 可选 didSet _setter名称_ 可选 _代码块_
## 类型的别名声明
_类型别名声明(type alias declaration)_可以在程序里为一个已存在的类型声明一个别名。类型的别名声明语句使用关键字`typealias`声明,遵循如下的形式:
> `typealias name` = `existing type`
当声明一个类型的别名后,可以在程序的任何地方使用别_名(name)_来代替_已存在的类型(existing type)_。已存在的类型可以是已经被命名的类型或者是混合类型。类型的别名不产生新的类型,它只是简单的和已存在的类型做名称替换。
查看更多[协议关联类型声明(Protocol Associated Type Declaration)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO).
> 类型别名声明语法
> 类型别名声明 → _类型别名头(Head)_ _类型别名赋值_
> 类型别名头(Head) → _属性列表_ 可选 _访问级别修饰符_
> 类型别名名称 → _标识符_
> 类型别名赋值 → = _类型_
## 函数声明
使用_函数声明(function declaration)_在程序里引入新的函数或者方法。一个函数被声明在类的上下文,结构体,枚举,或者协议中,从而作为_方法(method)_被引用。 函数声明使用关键字`func`,遵循如下的形式:
> func `function name`(`parameters`) -> `return type` {
> `statements`
> }
如果函数返回`Void`类型,返回类型可以被忽略,如下所示:
> func `function name`(`parameters`) {
> `statements`
> }
每个参数的类型都要标明,它们不能被推断出来。虽然函数的参数默认是常量,也可以使用参数名前使用`let`来强调这一行为。在这些参数前面添加`var`使它们成为变量,作用域内任何对变量的改变只在函数体内有效,或者用`inout`使的这些改变可以在调用域内生效。更多关于in-out参数的讨论,参见In-Out参数(In-Out Parameters)
函数可以使用元组类型作为返回值来返回多个变量。
函数定义可以出现在另一个函数声明内。这种函数被称作nested函数。更多关于_嵌套函数(Nested Functions)_的讨论,参见嵌套函数(Nested Functions)。
### 参数名
函数的参数是一个以逗号分隔的列表 。函数调用是的变量顺序必须和函数声明时的参数顺序一致。 最简单的参数列表有着如下的形式:
> `parameter name`: `parameter type`
一个参数有一个内部名称,这个内部名称可以在函数体内被使用。同样也可以作为外部名称,当调用方法时这个外部名称被作为实参的标签来使用。默认情况下,第一个参数的外部名称省略不写,第二个和其之后的参数使用它们的内部名称作为它们的外部名称。
~~~
func f(x: Int, y: Int) -> Int{ return x + y}
f(1, y: 2) // y是有标记的,x没有
~~~
可以按如下的一种形式,重写参数名被使用的默认过程:
> `external parameter name` `local parameter name`: `parameter type`
> _ `local parameter name`: `parameter type`
在内部参数名前的名称赋予这个参数一个外部名称,这个名称可以和内部参数的名称不同。外部参数名在函数被调用时必须被使用。对应的参数在方法或函数被调用时必须有外部名 。
内部参数名前的强调字符下划线(_)使参数在函数被调用时没有名称。在函数或方法调用时,与其对应的语句必须没有名字。
~~~
func f(x x: Int, withY y: Int, _z: Int) -> Int{
return x + y + z }
f(x: 1, withY: 2, 3) // x和y是有标记的,z没有
~~~
### 特殊类型的参数
参数可以被忽略,参数的值的数量可变,并且还可以提供默认值,使用形式如下:
> _ : `parameter type`.
> `parameter name`: `parameter type`...
> `parameter name`: `parameter type` = `default argument value`
以下划线(_)命名的参数是明确忽略的,在函数体内不能被访问。
一个以基础类型名的参数,如果紧跟着三个点(`...`),被理解为是可变参数。一个函数至多可以拥有一个可变参数,且必须是最后一个参数。可变参数被作为该基本类型名的数组来看待。举例来讲,可变参数`Int...`被看做是`[Int]`。查看可变参数的使用例子,详见可变参数(Variadic Parameters)一节。
在参数的类型后面有一个以等号(`=`)连接的表达式,这样的参数被看做有着给定表达式的初始值。当函数被调用时,给定的表达式被求值。如果参数在函数调用时被省略了,就会使用初始值。
~~~
func f(x: Int = 42) -> Int { return x}
f() // 有效的,使用默认值
f(7) // 有效的,提供了值,没有提供值的名称
f(x: 7) //无效的,值和值的名称都提供了
~~~
### 特殊方法
枚举或结构体的方法来修改`self`属性,必须以`mutating`声明修饰符标记。
子类方法重写超类中的方法必须以`override`声明修饰符标记。重写一个方法不使用`override`修饰符,或者使用了`override`修饰符却并没有重写超类方法都会产生一个编译时错误。
枚举或者结构体中的类型方法而不是实例方法,要以`static`声明修饰符标记,而对于类中的类型方法,要使用`class`声明修饰符标记。
### 柯里化函数(Curried Functions)
可以重写一个带有多个参数的函数使它等同于一个只有一个参数并且返回一个函数的函数,这个返回函数携带下一个参数并且返回另外一个函数,一直持续到再没有剩余的参数,此时要返回的函数返回原来的多参函数要返回的原始值。这个重写的函数被称为_柯里化函数(curried function)_。例如,可以为`addTwoInts(a:b:)`重写一个等价的`addTwoIntsCurried(a:)(b:)`的函数。
~~~
func addTwoInts(a: Int, b: Int) -> Int {
return a + b
}
func addTwoIntsCurried(a: Int) -> (Int -> Int) {
func addTheOtherInt(b: Int) -> Int {
return a + b
}
return addTheOtherInt
}
~~~
这个`addTwoInts(a:b:)`函数带有两个整型值并且返回他们的和。`addTwoIntsCurried(a:)(b:)`函数带有一个整型值,并且返回另外一个带有第二个整型值的函数并使其和第一个整型值相加(这个内嵌的函数从包含它的函数中捕获第一个整型参数的值)。
在Swift中,可以通过以下语法非常简明的写一个柯里化函数:
> func `function name`(`parameter`)(`parameter`) -> `return type` {
> `statements`
> }
举例来说,下面的两个声明是等价的:
~~~
func addTwoIntsCurried(a a: Int)(b: Int) -> Int {
return a + b
}
func addTwoIntsCurried(a a: Int) -> (Int -> Int)
{
func addTheOtherInt(b: Int) -> Int {
return a + b
}
return addTheOtherInt
}
~~~
为了像使用非柯里化函数一样的方式使用`addTwoIntsCurried(a:)(b:)`函数,必须用第一个整型参数调用`addTwoIntsCurried(a:)(b:)`,紧接着用第二个整型参数调用其返回的函数:
~~~
addTwoInts(a: 4, b: 5)
//返回值为9
addTwoIntsCurried(a: 4)(b: 5)
//返回值为9
~~~
虽然在每次调用一个非柯里化函数时必须提供所有的参数,可以使用函数的柯里化形式把参数分配在多次函数调用中,称之为“_偏函数应用(partial function application)_”,例如可以为`addTwoIntsCurried(a:)(b:)`函数使用参数`1`然后把返回的结果赋值给常量`plusOne`:
~~~
let plusOne = addTwoIntsCurried(a: 1)
// plusOne 是类型为 Int -> Int的函数
~~~
因为`plusOne`是函数`addTwoIntsCurried(a:)(b:)`绑定参数为`1`时结果,所以可以调用`plusOne`并且传入一个整型使其和`1`相加。
~~~
plusOne(10)
// 返回值为11
~~~
### 抛出异常函数和抛出异常方法(Throwing Functions and Methods)
可以抛出一个错误的函数或方法必需使用`throws`关键字标记。这些函数和方法被称为_抛出异常函数(throwing functions)_和_抛出异常方法(throwing methods)_。它们有着下面的形式:
> func `function name`(`parameters`) throws -> `return type` { `statements` }
调用一个抛出异常函数或抛出异常方法必需用一个`try`或者`try!`表达式来封装(也就是说,在一个范围内使用一个`try`或者`try!`运算符)。
`throws`关键字是函数的类型的一部分,不抛出异常的函数是抛出异常函数的一个子类型。所以,可以在使用抛出异常函数的地方使用不抛出异常函数。对于柯里化函数,`throws`关键字仅运用于最内层的函数。
不能重写一个仅基于是否能抛出错误的函数。也就是说,可以重载一个基于函数_参数(parameter)_能否抛出一个错误的函数。
一个抛出异常的方法不能重写一个不能抛出异常的方法,而且一个异常抛出方法不能满足一个协议对于不抛出异常方法的需求。也就是说,一个不抛出异常的方法可以重写一个抛出异常的方法,而且一个不抛出异常的方法可以满足一个协议对于抛出异常的需求。
### 重抛出异常函数和重抛出异常方法(Rethrowing Functions and Methods)
一个函数或方法可以使用`rethrows`关键字来声明,从而表明仅当这个函数或方法的一个函数参数抛出错误时这个函数或方法才抛出错误。这些函数和方法被称为_重抛出异常函数(rethrowing functions)_和_重抛出异常方法(rethrowing methods)_。重抛出异常函数或方法必需有至少一个抛出异常函数参数。
~~~
func functionWithCallback(callback: () throws -> Int) rethrows {
try callback()
}
~~~
一个抛出异常函数方法不能重写一个重抛出异常函数方法,一个抛出异常方法不能满足一个协议对于重抛出异常方法的需求。也就是说,一个重抛出异常方法可以重写一个抛出异常方法,而且一个重抛出异常方法可以满足一个协议对于抛出异常方法的需求。
> 函数声明语法
> 函数声明 → _函数头_ _函数名_ _泛型参数子句_ 可选 _函数签名(Signature)_ _函数体_
> 函数头 → _特性(Attributes)列表_ 可选 _声明修饰符(Specifiers)列表_ 可选 func
> 函数名 → _标识符_ | _运算符_
> 函数签名(signature) → _parameter-clauses_ throws _函数结果_ 可选
> 函数签名(signature) → _parameter-clauses_ rethrows _函数结果_ 可选
> 函数结果 → -> _特性(Attributes)列表_ 可选 _类型_
> 函数体 → _代码块_
> parameter-clauses → _参数子句_ _parameter-clauses_ 可选
> 参数子句 → ( ) | ( _参数列表_ ... 可选 )
> 参数列表 → _参数_ | _参数_ , _参数列表_
> 参数 → inout 可选 let 可选 _外部参数名__可选_ _内部参数名_ _类型标注_ _默认参数子句_ 可选
> 参数 → inout 可选 var _外部参数名_ _内部参数名_ _类型标注_ _默认参数子句_ 可选
> 参数 → _特性(Attributes)列表_ 可选 _类型_
> 参数名 → _标识符_ | _
> 内部参数名 → _标识符_ | _
> 默认参数子句 → = _表达式_
## 枚举声明
在程序里使用_枚举声明(enumeration)_来引入一个枚举类型。
枚举声明有两种基本的形式,使用关键字`enum`来声明。枚举声明体使用从零开始的变量——叫做_枚举事件(enumeration cases)_,和任意数量的声明,包括计算型属性,实例方法,类型方法,构造器,类型别名,甚至其他枚举,结构体,和类。枚举声明不能包含析构器或者协议声明。
枚举类型可以采用任何数量的协议,但是这些协议不能从类,结构体和其他的枚举继承。
不像类或者结构体。枚举类型并不提供隐式的初始构造器,所有构造器必须显式的声明。构造器可以委托枚举中的其他构造器,但是构造过程仅当构造器将一个枚事件指定给`self`才全部完成。
和结构体类似但是和类不同,枚举是值类型:枚举实例在赋予变量或常量时,或者被函数调用时被复制。 更多关于值类型的信息,参见结构体和枚举都是值类型(Structures and Enumerations Are Value Types)一节。
可以扩展枚举类型,正如在扩展声明(Extension Declaration)中讨论的一样。
### 任意事件类型的枚举
如下的形式声明了一个包含任意类型枚举时间的枚举变量
> enum `enumeration name`: `adopted protocols`{
> case `enumeration case 1`
> case `enumeration case 2`(`associated value types`)
> }
这种形式的枚举声明在其他语言中有时被叫做_可识别联合(discrinminated)_。
这种形式中,每一个事件块由关键字`case`开始,后面紧接着一个或多个以逗号分隔的枚举事件。每一个事件名必须是独一无二的。每一个事件也可以指定它所存储的指定类型的值,这些类型在_关联值类型(associated values types)_的元组里被指定,立即书写在事件 名后。获得更多关于关联值类型的信息和例子,请查看关联值(Associated Values)一节。
枚举有一个递归结构,就是说,枚举有着枚举类型自身实例的关联值的事件。然而,枚举类型的实例有值语义,意味着它们在内存中有着固定的位置。为了支持递归,编译器必需插入一个间接层。
为间接使用特殊的枚举事件,使用`indirect`声明修饰符标记。
> enum Tree { case Empty indirect case Node(value: T, left: Tree, right:Tree) }
为了间接的使用一个枚举的所有事件,使用`indirect`修饰符标记整个枚举-当枚举有许多事件且每个事件都需要使用`indirect`修饰符标记的时候这将非常便利。
一个被`indirect`修饰符标记的枚举事件必需有一个关联值。一个使用`indirect`修饰符标记的枚举包含有着关联值的事件和没有关联值的事件的混合。就是说,它不能包含任何也使用`indirect`修饰符标记的事件。
### 使用原始值类型事件的枚举(Enumerations with Cases of a Raw-Value Type)
以下的形式声明了一个包含相同基础类型的枚举事件的枚举:
> enum `enumeration name`: `raw value type`, `adopted protocols`{
> case `enumeration case 1` = `raw value 1`
> case `enumeration case 2` = `raw value 2`
> }
在这种形式中,每一个事件块由`case`关键字开始,后面紧接着一个或多个以逗号分隔的枚举事件。和第一种形式的枚举事件不同,这种形式的枚举事件包含一个同类型的基础值,叫做_原始值(raw value)_。这些值的类型在_原始值类型(raw-value type)_中被指定,必须表示一个整数,浮点数,字符串,或者一个字符。特别是_原始值类型(raw-value type)_必需遵守`Equatable`类型的协议和下列形式中的一种字面量构造协议(literal-convertible protocols):整型字面量有`IntergerLiteralConvertible`,浮点行字面量有`FloatingPointLiteralConvertible`,包含任意数量字符的字符串型字面量有`StringLiteralConvertible`,仅包含一个单一字符的字符串型字面量有`ExtendedGraphemeClusterLiteralConvertible`。每一个事件必须有唯一的名字,必须有一个唯一的初始值。
如果初始值类型被指定为`Int`,则不必为事件显式的指定值,它们会隐式的被标为值`0,1,2`等。每一个没有被赋值的`Int`类型时间会隐式的赋予一个初始值,它们是自动递增的。
~~~
num ExampleEnum: Int {
case A, B, C = 5, D
}
~~~
在上面的例子中,`ExampleEnum.A`的值是`0`,`ExampleEnum.B`的值是`1`。因为`ExampleEnum.C`的值被显式的设定为`5`,因此`ExampleEnum.D`的值会自动增长为`6`。
如果原始值类型被指定为`String`类型,你不用明确的为事件指定值,每一个没有指定的事件会隐式地用与事件名字相同的字符串指定。
> enum WeekendDay: String { case Saturday, Sunday }
在上面这个例子中,`WeekendDay.Saturday`的原始值是`"Saturday"`,`WeekendDay.Sunday`的原始值是`"Sunday"`。
拥有多种事件的原始值类型的枚举含蓄地遵循定义在Swift标准库中的`RawRepresentable`协议。所以,它们拥有一个原始值(`rawValue`)属性和一个有着`init?(rawValue: RawValue)`签名的可失败构造器(a failable initializer)。可以使用原始值属性去取的枚举事件的原始值,就像在`ExampleEnum.B.rawValue`中一样。如果有一个事件符合,也可以使用原始值去找到一个符合的事件,通过调用枚举的可失败构造器,如`ExampleEnum(rawValue: 5)`,这个可失败构造器返回一个可选的事件。想得到更多的信息和关于原始值类型查看更多信息和获取初始值类型事件的信息,参阅初始值原始值(Raw Values)。
### 获得枚举事件
使用点(.)来引用枚举类型的事件,如`EnumerationType.EnumerationCase`。当枚举类型可以上下文推断出时,可以省略它(.仍然需要),参照枚举语法(Enumeration Syntax)和显式成员表达(Implicit Member Expression)。
使用`switch`语句来检验枚举事件的值,正如使用switch语句匹配枚举值(Matching Enumeration Values with a Switch Statement)一节描述的那样。枚举类型是模式匹配(pattern-matched)的,和其相反的是`switch`语句case块中枚举事件匹配,在枚举事件类型(Enumeration Case Pattern)中有描述。
> 枚举声明语法 枚举声明 → _特性(Attributes)列表_ 可选 _访问级别修饰符_ 可选 _联合式枚举_ 枚举声明 → _特性(Attributes)列表_ 可选 _访问级别修饰符_ 可选 _原始值式枚举_ 联合式枚举 → indirect 可选 enum _枚举名_ _泛型参数子句_ 可选 类型继承子句_可选_ { _union-style-enum-members_ 可选 }
> union-style-enum-members → _union-style-enum-member_ _union-style-enum-members_ 可选
> union-style-enum-member → _声明_ | _联合式(Union Style)的枚举case子句_
> 联合式(Union Style)的枚举case子句 → _特性(Attributes)列表_ 可选 indirect 可选 case _联合式(Union Style)的枚举case列表_
> 联合式(Union Style)的枚举case列表 → _联合式(Union Style)的case_ | _联合式(Union Style)的case_ , _联合式(Union Style)的枚举case列表_
> 联合式(Union Style)的case → _枚举的case名_ _元组类型_ 可选
> 枚举名 → _标识符_
> 枚举的case名 → _标识符_
> 原始值式枚举 → enum _枚举名_ _泛型参数子句_ 可选 _类型继承子句_ { _原始值式枚举成员列表_ }
> 原始值式枚举成员列表 → _原始值式枚举成员_ _原始值式枚举成员列表_ 可选
> 原始值式枚举成员 → _声明_ | _原始值式枚举case子句_
> 原始值式枚举case子句 → _特性(Attributes)列表_ 可选 case _原始值式枚举case列表_
> 原始值式枚举case列表 → _原始值式枚举case_ | _原始值式枚举case_ , _原始值式枚举case列表_
> 原始值式枚举case → _枚举的case名_ _原始值赋值_ 可选
> 原始值赋值 → = _原始值字面量_
> 原始值字面量 → 数字型字面量|字符串型字面量|布尔型字面量
## 结构体声明
使用_结构体声明(strucre declaration)_可以在程序里引入一个结构体类型。结构体声明使用`struct`关键字,遵循如下的形式:
> struct `structure name`: `adopted protocols` {
> `declarations`
> }
结构体内包含零或多个声明_声明(declarations)_。这些_声明(declarations)_可以包括存储型和计算型属性,类型属性,实例方法,类型方法,构造器,下标脚本,类型别名,甚至其他结构体,类,和枚举声明。结构体声明不能包含析构器或者协议声明。详细讨论和包含多种结构体声明的实例,参见[类和结构体(Classes and Structures)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)一节。
结构体可以包含任意数量的协议,但是不能继承自类,枚举或者其他结构体。
有三种方法可以创建一个声明过的结构体实例:
* 调用结构体内声明的构造器,参照[构造器(Initializers)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)一节。
* 如果没有声明构造器,调用结构体的逐个构造器,详情参见[Memberwise Initializers for Structure Types](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)。
* 如果没有声明析构器,结构体的所有属性都有初始值,调用结构体的默认构造器,详情参见[默认构造器(Default Initializers)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)。
结构体的构造过程参见[构造过程(Initiaization)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)一节。
结构体实例属性可以用点(.)来获得,详情参见[获得属性(Accessing Properties)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)一节。
结构体是值类型;结构体的实例在被赋予变量或常量,被函数调用时被复制。获得关于值类型更多信息,参见 [结构体和枚举是值类型(Structures and Enumerations Are Value Types)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)一节。
可以使用扩展声明来扩展结构体类型的行为,参见[扩展声明(Extension Declaration)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)。
> 结构体声明语法
> 结构体声明 → _特性(Attributes)列表_ 可选 访问级别修饰符_可选_ struct _结构体名称_ _泛型参数子句_ 可选 _类型继承子句_ 可选 _结构体主体_
> 结构体名称 → _标识符_
> 结构体主体 → { _声明(Declarations)列表_ 可选 }
## 类声明
可以在程序中使用_类声明(class declaration)_来引入一个类。类声明使用关键字`class`,遵循如下的形式:
> class `class name`: `superclass`, `adopted protocols` {
> `declarations`
> }
一个类内包含零或多个_声明(declarations)_。这些_声明(declarations)_可以包括存储型和计算型属性,实例方法,类型方法,构造器,单独的析构器,下标脚本,类型别名,甚至其他结构体,类,和枚举声明。类声明不能包含协议声明。详细讨论和包含多种类声明的实例,参见类和结构体(Classes and Structures)一节。
一个类只能继承一个父类,_超类(superclass)_,但是可以包含任意数量的协议。_超类(superclass)_第一次出现在_类名(class name)_和冒号后面,其后跟着_采用的协议(adopted protocols)_。泛型类可以继承其它类型类和非泛型类,但是非泛型类只能继承其它的非泛型类。当在冒号后面写泛型超类的名称时,必须写那个泛型类的全名,包括它的泛型参数子句。
正如在初始化声明(Initializer Declaration)谈及的那样,类可以有指定构造器和方便构造器。类的指定构造器必须初始化类所有的已声明的属性,它必须在超类构造器调用前被执行。
类可以重写属性,方法,下表脚本和它的超类构造器。重写的属性,方法,下标脚本,和指定构造器必须以`override`声明修饰符标记。
为了要求子类去实现超类的构造器,使用`required`声明修饰符去标记超类的构造器。在子类实现父类构造器时也必须使用`required`声明修饰符去标记。
虽然_超类(superclass)_的属性和方法声明可以被当前类继承,但是_超类(superclass)_声明的指定构造器却不能。这意味着,如果当前类重写了超类的所有指定构造器,它就继承了超类的方便构造器。Swift的类并不是继承自一个全局基础类。
有两种方法来创建已声明的类的实例:
* 调用类的一个构造器,参见[构造器(Initializers)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)。
* 如果没有声明构造器,而且类的所有属性都被赋予了初始值,调用类的默认构造器,参见[默认构造器(Default Initializers)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)。
类实例属性可以用点(.)来获得,详情参见[获得属性(Accessing Properties)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)一节。
类是引用类型;当被赋予常量或变量,函数调用时,类的实例是被引用,而不是复制。获得更多关于引用类型的信息,[结构体和枚举都是值类型(Structures and Enumerations Are Value Types)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)一节。
可以使用扩展声明来扩展类的行为,参见[扩展声明(Extension Declaration)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)。
> 类声明语法
> 类声明 → _特性(Attributes)列表_ 可选 访问级别修饰符class _类名_ _泛型参数子句_ 可选 _类型继承子句_ 可选 _类主体_
> 类名 → _标识符_
> 类主体 → { _声明(Declarations)列表_ 可选 }
## 协议声明(translated by 小一)
一个_协议声明(protocol declaration)_为程序引入一个命名了的协议类型。协议声明在一个全局访问的区域使用`protocol` 关键词来进行声明并有下面这样的形式:
> protocol `protocol name`: `inherited protocols` {
> `protocol member declarations`
> }
协议的主体包含零或多个_协议成员声明(protocol member declarations)_,这些成员描述了任何采用该协议必须满足的一致性要求。特别的,一个协议可以声明必须实现某些属性、方法、初始化程序及下标脚本的一致性类型。协议也可以声明专用种类的类型别名,叫做_关联类型(associated types)_,它可以指定协议的不同声明之间的关系。协议声明不包括类,结构体,枚举或者其它协议的声明。协议成员声明会在下面的详情里进行讨论。
协议类型可以从很多其它协议那继承。当一个协议类型从其它协议那继承的时候,来自其它协议的所有要求就集合了,而且从当前协议继承的任何类型必须符合所有的这些要求。对于如何使用协议继承的例子,查看协议继承(Protocol Inheritance)
> 注意:
> 也可以使用协议合成类型集合多个协议的一致性要求,详情参见协议合成类型(Protocol Composition Type)和协议合成(Protocol Composition)
可以通过采用在类型的扩展声明中的协议来为之前声明的类型添加协议一致性。在扩展中必须实现所有采用协议的要求。如果该类型已经实现了所有的要求,可以让这个扩展声明的主题留空。
默认地,符合某一个协议的类型必须实现所有声明在协议中的属性、方法和下标脚本。也就是说,可以用`optional`声明修饰符标注这些协议成员声明以指定它们的一致性类型实现是可选的。`optional`修饰符仅仅可以用于使用`objc`属性标记过的协议。这样的结果就是仅仅类类型可以采用并符合包含可选成员要求的协议。更多关于如何使用`optional`属性的信息及如何访问可选协议成员的指导——比如当不能肯定是否一致性的类型实现了它们——参见可选协议要求(Optional Protocol Requirements)
为了限制协议的采用仅仅针对类类型,需要强制使用`class`来标记协议,通过将`class`关键在写在冒号后面的_继承协议列表(inherited protocols)_的第一个位置。例如,下面的协议形式只能被类类型采用:
~~~
protocol SomeProtocol:class{
/* Protocol member go here */
}
~~~
任意继承自需要标记有`class`协议的协议都可以智能地仅能被类类型采用。
> 注意:
> 如果协议已经用`object`属性标记了,`class`条件就隐性地应用于该协议;没有必要再明确地使用`class`条件来标记该协议了。
协议是命名的类型,因此它们可以以另一个命名类型出现在代码的所有地方,就像[协议类型(Protocol as Types)](http://wiki.jikexueyuan.com/project/swift/chapter2/21_Protocols.html#protocols_as_types)里讨论的那样。然而不能构造一个协议的实例,因为协议实际上不提供它们指定的要求的实现。
可以使用协议来声明一个类的代理的方法或者应该实现的结构,就像[委托(代理)模式(Delegation)](http://wiki.jikexueyuan.com/project/swift/chapter2/21_Protocols.html#delegation)描述的那样。
> 协议(Protocol)声明语法
> 协议声明 → _特性(Attributes)列表_ 可选 _访问级别修饰符_ 可选 protocol _协议名_ _类型继承子句_ 可选 _协议主体_
> 协议名 → _标识符_
> 协议主体 → { _协议成员声明(Declarations)列表_ 可选 }
> 协议成员声明 → _协议属性声明_
> 协议成员声明 → _协议方法声明_
> 协议成员声明 → _协议构造器声明_
> 协议成员声明 → _协议附属脚本声明_
> 协议成员声明 → _协议关联类型声明_
> 协议成员声明(Declarations)列表 → _协议成员声明_ _协议成员声明(Declarations)列表_ _可选_
### 协议属性声明
协议声明了一致性类型必须在协议声明的主体里通过引入一个_协议属性声明(protocol property declaraion)_来实现一个属性。协议属性声明有一种特殊的类型声明形式:
> var `property name`: `type` { get set }
同其它协议成员声明一样,这些属性声明仅仅针对符合该协议的类型声明了`getter`和`setter`要求。结果就是不需要在协议里它被声明的地方实现`getter`和`setter`。
`getter`和`setter`要求可以通过一致性类型以各种方式满足。如果属性声明包含`get`和`set`关键词,一致性类型就可以用可读写(实现了`getter`和`setter`)的存储型变量属性或计算型属性,但是属性不能以常量属性或只读计算型属性实现。如果属性声明仅仅包含`get`关键词的话,它可以作为任意类型的属性被实现。比如说实现了协议的属性要求的一致性类型,参见属性要求(Property Requirements)
更多参见[变量声明(Variabel Declaration)](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#variable_declaration)
> 协议属性声明语法
> _协议属性声明_ → _变量声明头(Head)_ _变量名_ _类型标注_ _getter-setter关键字(Keyword)块_
### 协议方法声明
协议声明了一致性类型必须在协议声明的主体里通过引入一个协议方法声明来实现一个方法。协议方法声明和函数方法声明有着相同的形式,包含如下两条规则:它们不包括函数体,不能在类的声明内为它们的参数提供初始值.举例来说,符合的类型执行协议必需的方法。参见必需方法(Method Requirements)一节。
使用`static`声明修饰符可以在协议声明中声明一个类或必需的静态方法。执行这些方法的类用修饰符`class`声明。相反的,执行这些方法的结构体必须以`static`声明修饰符声明。如果想使用扩展方法,在扩展类时使用`class`修饰符,在扩展结构体时使用`static`修饰符。
更多请参阅函数声明(Function Declaration)。
> 协议方法声明语法
> _协议方法声明_ → _函数头_ _函数名_ _泛型参数子句_ _可选_ _函数签名(Signature)_
### 协议构造器声明
协议声明了一致性类型必须在协议声明的主体里通过引入一个协议构造器声明来实现一个构造器。协议构造器声明 除了不包含构造器体外,和构造器声明有着相同的形式。
一个一致性类型可以通过实现一个非可失败构造器或者`init!`可失败构造器去满足一个非可失败协议构造器的需求。一个一致性类型通过实现任意类型的构造器可以满足一个可失败协议构造器的需求。
当一个类去实现一个构造器去满足一个协议的构造器的需求,如果这个类还没有用`final`声明修饰符标记,这个构造器必需使用`required`声明修饰符去标记。
更多请参阅构造器声明(Initializer Declaration)。
> 协议构造器声明语法
> _协议构造器声明_ → _构造器头(Head)_ _泛型参数子句_ _可选_ _参数子句_
### 协议下标脚本声明
协议声明了一致性类型必须在协议声明的主体里通过引入一个协议下标脚本声明来实现一个下标脚本。协议下标脚本声明对下标脚本声明有一个特殊的形式:
> subscript (`parameters`) -> `return type` { get set }
下标脚本声明只为和协议一致的类型声明了必需的最小数量的的getter和setter。如果下标脚本申明包含get和set关键字,一致的类型也必须有一个getter和setter语句。如果下标脚本声明值包含get关键字,一致的类型必须_至少(at least)_包含一个getter语句,可以选择是否包含setter语句。
更多参阅[下标脚本声明(Subscript Declaration)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)。
> 协议附属脚本声明语法
> _协议附属脚本声明_ → _附属脚本头(Head)_ _附属脚本结果(Result)_ _getter-setter关键字(Keyword)块_
### 协议相关类型声明
协议声明相关类型使用关键字`typealias`。相关类型为作为协议声明的一部分的类型提供了一个别名。相关类型和参数语句中的类型参数很相似,但是它们在声明的协议中包含`self`关键字。在这些语句中,`self`指代和协议一致的可能的类型。获得更多信息和例子,查看关联类型(Associated Types)一节或类型别名声明(Type Alias Declaration)一节。
> 协议关联类型声明语法
> _协议关联类型声明_ → _类型别名头(Head)_ _类型继承子句_ _可选_ _类型别名赋值_ _可选_
## 构造器声明
_构造器(initializer)_声明会为程序内的类,结构体或枚举引入构造器。构造器使用关键字`init`来声明,遵循两条基本形式。
结构体,枚举,类可以有任意数量的构造器,但是类的构造器的规则和行为是不一样的。不像结构体和枚举那样,类有两种结构体,designed initializers 和convenience initializers,参见[构造器(Initialization)](51353)一节。
如下的形式声明了结构体,枚举和类的指定构造器:
> init(`parameters`) {
> `statements`
> }
类的指定构造器将类的所有属性直接初始化。如果类有超类,它不能调用该类的其他构造器,它只能调用超类的一个指定构造器。如果该类从它的超类处继承了任何属性,这些属性在当前类内被赋值或修饰时,必须调用一个超类的指定构造器。
指定构造器可以在类声明的上下文中声明,因此它不能用扩展声明的方法加入一个类中。
结构体和枚举的构造器可以调用其他的已声明的构造器,委托其中一个或所有的构造器进行初始化过程。
以`convenience`声明修饰符来标记构造器声明来声明一个类的便利构造器:
> convenience init(`parameters`) {
> `statements`
> }
便利构造器可以将初始化过程委托给另一个便利构造器或类的一个指定构造器。这意味着,类的初始化过程必须 以一个将所有类属性完全初始化的指定构造器的调用作为结束。便利构造器不能调用超类的构造器。
可以使用required声明修饰符,将便利构造器和指定构造器标记为每个子类的构造器都必须实现的。一个子类的关于这个构造器的实现也必须使用`required`声明修饰符标记。
默认情况下,声明在超类的构造器没有被子类继承。也就是说,如果一个子类使用默认的值去构造它所有的存储属性,而且没有定义任何自己的构造器,它将继承超类的构造器。如果子类重写所有超类的指定构造器,子类继承超类的便利构造器。
和方法,属性和下表脚本一样,需要使用`override`声明修饰符标记重写了的制定构造器。
> 注意 如果使用`required`声明修饰符去标记一个构造器,当在子类中重写必要构造器时,也不要用`override`修饰符去标记构造器。
查看更多关于不同声明方法的构造器的例子,参阅[构造过程(Initialization)](51353)一节。
### 可失败构造器(Failable Initializers)
_可失败构造器_是一种可以生成可选实例或者是一类构造器声明的隐式解析可选实例(an implicitly unwrapped optional instance)类型。所以,构造区通过返回`nil`来指明构造过程失败。
声明可以生成可选实例的可失败构造器,在构造器声明的`init`关键字后加追加一个问号(`init?`)。声明可生成隐式解析可选实例的可失败构造器,在构造器声明后追加一个叹号(`init!`)。使用`init?`可失败构造器生成结构体的一个可选实例的例子如下。
~~~
struct SomeStruct {
let string: String
//生成一个'SomeStruct'的可选实例
init?(input: String) {
if input.isEmpty {
// 弃用'self' 返回 'nil'
}
string = input
}
}
~~~
除非必需处理结果的可选性,可以使用调用非可失败构造器的方式调用`init?`可失败构造器。
~~~
if let actualInstance = SomeStruct(input: "Hello") {
//'SomeStruct'实例相关
} else {
//'SomeStruct'实例构造过程失败,构造器返回'nil'
}
~~~
在实现构造体的任何时间,结构体或者枚举的可失败构造器可以返回`nil`。然而,类的可失败构造器,仅在类的所有存储属性被构造之后且`self.init`或`super.init`被调用之后才返回`nil`(就是说,构造器的委托被执行)。
可失败构造器可以委托任何种类的构造器。非可失败可以委托其它非可失败构造器或者`init!`可失败构造器。
构造过程的失败由构造器的委托产生。特别的,如果可失败构造器代理一个构造器失败且返回`nil`,那么之后被委托的构造器也会失败且隐式的返回`nil`。如果非可失败构造器代理`init!`可失败构造器失败了且返回`nil`,那么后出现一个运行时错误(如同使用`!`操作符去解析一个有着`nil`值的可选项)。
可失败指定构造器可以在子类中任何一种指定构造器重写。非可失败指定构造器在子类中仅能通过非可失败构造器被重写。
得到更多的信息并且了解更多关于可失败构造器的例子,请参阅可失败构造器(Failable Initializer)
> 构造器声明语法
> _构造器声明_ → _构造器头(Head)_ _泛型参数子句_ _可选_ _参数子句_ _构造器主体_
> _构造器头(Head)_ → _特性(Attributes)列表_ _可选_ _声明修饰符列表(modifiers)_ _可选_ init
> _构造器头(Head)_ → _特性(Attributes)列表_ _可选_ _声明修饰符列表(modifiers)_ _可选_ init ?
> _构造器头(Head)_ → _特性(Attributes)列表_ _可选_ _声明修饰符列表(modifiers)_ _可选_ init ! _构造器主体_ → _代码块_
## 析构声明
_析构声明(deinitializer declaration)_为类声明了一个析构器。析构器没有参数,遵循如下的格式:
> deinit {
> `statements`
> }
当类没有任何语句时将要被释放时,析构器会自动的被调用。析构器在类的声明体内只能被声明一次——但是不能在 类的扩展声明内,每个类最多只能有一个。
子类继承了它的超类的析构器,在子类将要被释放时隐式的调用。子类在所有析构器被执行完毕前不会被释放。
析构器不会被直接调用。
查看例子和如何在类的声明中使用析构器,参见[析构过程Deinitialization](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)一节。
> 析构器声明语法
> _析构器声明_ → [ _特性(Attributes)列表_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/06_Attributes.html#attributes) _可选_ deinit [ _代码块_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#code_block)
## 扩展声明
_扩展声明(extension declaration)_用于扩展一个现存的类,结构体,枚举的行为。扩展声明使用关键字`extension`,遵循如下的规则:
> extension `type name`: `adopted protocols` {
> `declarations`
> }
一个扩展声明体包括零个或多个_声明语句(declarations)_。这些_声明语句(declarations)_可以包括计算型属性,计算型类型属性,实例方法,类型方法,构造器,下标脚本声明,甚至其他结构体,类,和枚举声明。扩展声明不能包含析构器,协议声明,存储型属性,属性监测器或其他的扩展属性。详细讨论和查看包含多种扩展声明的实例,参见[扩展(Extensions)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)一节。
扩展声明可以向现存的类,结构体,枚举内添加_一致的协议(adopted protocols)_。扩展声明不能向一个类中添加继承的类,因此在_类型名称_的冒号后面仅能指定一个协议列表。
属性,方法,现存类型的构造器不能被它们类型的扩展所重写。
扩展声明可以包含构造器声明,这意味着,如果扩展的类型在其他模块中定义,构造器声明必须委托另一个在 那个模块里声明的构造器来恰当的初始化。
> 扩展(Extension)声明语法
> _扩展声明_ → [访问级别修饰符](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO) _可选_ extension [_类型标识_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_identifier) [_类型继承子句_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_inheritance_clause) _可选_ [_extension-body_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#extension_body)
> _extension-body_ → { [_声明(Declarations)列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#declarations) _可选_ }
## 下标脚本声明
_下标脚本(subscript)_声明用于向特定类型添加附属脚本支持,通常为访问集合,列表和序列的元素时提供语法便利。附属脚本声明使用关键字`subscript`,声明形式如下:
> subscript (`parameter`) -> (return type){
> get{
> `statements`
> }
> set(`setter name`){
> `statements`
> }
> }
附属脚本声明只能在类,结构体,枚举,扩展和协议声明的上下文进行声明。
_参数列表(parameters)_指定一个或多个用于在相关类型的下标脚本中访问元素的索引(例如,表达式`object[i]`中的`i`)。尽管用于元素访问的索引可以是任意类型的,但是每个变量必须包含一个用于指定每种索引类型的类型标注。_返回类型(return type)_指定被访问的元素的类型。
和计算性属性一样,下标脚本声明支持对访问元素的读写操作。getter用于读取值,setter用于写入值。setter子句是可选的,当仅需要一个getter子句时,可以将二者都忽略且直接返回请求的值即可。也就是说,如果使用了setter子句,就必须使用getter子句。
_setter名称(setter name)_和封闭的括号是可选的。如果使用了setter名称,它会被当做传给setter的变量的名称。如果不使用setter名称,那么传给setter的变量的名称默认是`value`。_setter名称(setter name)_的类型必须与_返回类型(return type)_的类型相同。
可以在下标脚本声明的类型中,可以重载下标脚本,只要_参数列表(parameters)_或_返回类型(return type)_与先前的不同即可。也可以重写继承自超类的下标脚本声明。此时,必须使用`override`声明修饰符声明那个被重写的下标脚本。(待定)
同样可以在协议声明的上下文中声明下标脚本,[协议下标脚本声明(Protocol Subscript Declaration)](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-XID_619)中有所描述。
更多关于下标脚本和下标脚本声明的例子,请参考[下标脚本(Subscripts)](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Subscripts.html#//apple_ref/doc/uid/TP40014097-CH16-XID_393)。
> 附属脚本声明语法
> _附属脚本声明_ → [_附属脚本头(Head)_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#subscript_head) [_附属脚本结果(Result)_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#subscript_result) [ _代码块_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#code_block)
> _附属脚本声明_ → [ _附属脚本头(Head)_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#subscript_head) [ _附属脚本结果(Result)_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#subscript_result) [ _getter-setter块_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#getter_setter_block)
> _附属脚本声明_ → [ _附属脚本头(Head)_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#subscript_head) [ _附属脚本结果(Result)_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#subscript_result) [ _getter-setter关键字(Keyword)块_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#getter_setter_keyword_block)
> _附属脚本头(Head)_ → [ _特性(Attributes)列表_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/06_Attributes.html#attributes) _可选_ [ _声明修饰符列表(declaration-modifiers)_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO) _可选_ subscript [ _参数子句_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#parameter_clause)
> _附属脚本结果(Result)_ → -> [ _特性(Attributes)列表_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/06_Attributes.html#attributes) _可选_ [ _类型_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)
## 运算符声明(translated by 林)
_运算符声明(operator declaration)_会向程序中引入中缀、前缀或后缀运算符,它使用关键字`operator`声明。
可以声明三种不同的缀性:中缀、前缀和后缀。操作符的_缀性(fixity)_描述了操作符与它的操作数的相对位置。
运算符声明有三种基本形式,每种缀性各一种。运算符的缀性通过在`operator`关键字之前添加声明修饰符`infix`,`prefix`或`postfix`来指定。每种形式中,运算符的名字只能包含[运算符(Operators)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)中定义的运算符字符。
下面的这种形式声明了一个新的中缀运算符:
> infix operator `operator name` {
> precedence `precedence level`
> associativity `associativity`
> }
_中缀运算符(infix operator)_是二元运算符,它可以被置于两个操作数之间,比如表达式`1 + 2` 中的加法运算符(`+`)。
中缀运算符可以可选地指定优先级,结合性,或两者同时指定。
运算符的_优先级(precedence)_可以指定在没有括号包围的情况下,运算符与它的操作数如何紧密绑定的。可以使用上下文关键字`precedence`并_优先级(precedence level)_一起来指定一个运算符的优先级。_优先级(precedence level)_可以是0到255之间的任何一个数字(十进制整数);与十进制整数字面量不同的是,它不可以包含任何下划线字符。尽管优先级是一个特定的数字,但它仅用作与另一个运算符比较(大小)。也就是说,一个操作数可以同时被两个运算符使用时,例如`2 + 3 * 5`,优先级更高的运算符将优先与操作数绑定。
运算符的_结合性(associativit)_可以指定在没有括号包围的情况下,优先级相同的运算符以何种顺序被分组的。可以使用上下文关键字`associativity`并_结合性(associativity)_一起来指定一个运算符的结合性,其中_结合性_可以说是上下文关键字`left`,`right`或`none`中的任何一个。左结合运算符以从左到右的形式分组。例如,减法运算符(`-`)具有左结合性,因此`4 - 5 - 6`被以`(4 - 5) - 6`的形式分组,其结果为`-7`。 右结合运算符以从右到左的形式分组,对于设置为`none`的非结合运算符,它们不以任何形式分组。具有相同优先级的非结合运算符,不可以互相邻接。例如,表达式`1 < 2 < 3`非法的。
声明时不指定任何优先级或结合性的中缀运算符,它们的优先级会被初始化为100,结合性被初始化为`none`。
下面的这种形式声明了一个新的前缀运算符:
> prefix operator `operator name`{}
紧跟在操作数前边的_前缀运算符(prefix operator)_是一元运算符,例如表达式`++i`中的前缀递增运算符(`++`)。
前缀运算符的声明中不指定优先级。前缀运算符是非结合的。
下面的这种形式声明了一个新的后缀运算符:
> postfix operator `operator name`{}
紧跟在操作数后边的_后缀运算符(postfix operator)_是一元运算符,例如表达式`i++`中的前缀递增运算符(`++`)。
和前缀运算符一样,后缀运算符的声明中不指定优先级。后缀运算符是非结合的。
声明了一个新的运算符以后,需要声明一个跟这个运算符同名的函数来实现这个运算符。如果在实现一个前缀或者后缀操作符,也必须使用相符的`prefix`或者`postfix`声明修饰符标记函数声明。如果实现中缀操作符,不需要使用`infix`声明修饰符标记函数声明。如何实现一个新的运算符,请参考[Custom Operators](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)。
> 运算符声明语法
> _运算符声明_ → [ _前置运算符声明_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#prefix_operator_declaration) | [ _后置运算符声明_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#postfix_operator_declaration) | [ _中置运算符声明_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#infix_operator_declaration)
> _前置运算符声明_ → prefix 运算符 [ _运算符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/LexicalStructure.html#operator) { }
> _后置运算符声明_ → postfix 运算符 [ _运算符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/LexicalStructure.html#operator) { }
> _中置运算符声明_ → infix 运算符 [ _运算符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/LexicalStructure.html#operator) { [ _中置运算符属性_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#infix_operator_attributes) _可选_ }
> _中置运算符属性_ → [ _优先级子句_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#precedence_clause) _可选_ [ _结和性子句_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#associativity_clause) _可选_
> _优先级子句_ → precedence [ _优先级水平_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#precedence_level)
> _优先级水平_ → 十进制整数 0 到 255
> _结和性子句_ → associativity [ _结和性_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#associativity)
> _结和性_ → left | right | none
## 声明修饰符
_声明修饰符(Declaration modifiers)_是关键字或者说是上下文相关的关键字,它可以修改一个声明的行为或者含义。可以在一个声明的特性和引进该声明的关键字之间,指定一个声明修饰符,并写下它的关键字或上下文相关的关键字。
`dynamic` 可以将该修饰符用于任何可以出现在Objective-C中的类成员上。当将`dynamic`修饰符用于一个成员声明上时,对该成员的访问总是由Objective-C的实时系统动态地安排,而永远不会由编译器内联或去虚拟化。 因为当一个声明被标识`dynamic`修饰符时,会由Objective-C的实时系统动态地安排,所以他们是被隐式的标识了`objc`特性的。
`final`
该修饰符用于修饰一个类或类中的属性,方法,以及下标成员。如果用它修饰一个类,那么这个类则不能被继承。如果用它修饰类中的属性,方法或下标,则表示在子类中,它们不能被重写。
`lazy`
该修饰符用于修饰类或结构体中的存储型变量属性,表示该属性的初始值最多只被计算和存储一次,且发生在第一次访问它时。如何使用`lazy`特性的一个例子,请见:[惰性存储型属性(Lazy Stored Properties)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)。
`optional`
该修饰符用于修饰一个类或类中的属性,方法,以及下标成员,表示遵循类型没有被要求实现这些成员。 只能将`optional`修饰符用于被`objc`标识的协议。这样一来,只有类类型可以适配或遵循拥有可选成员需求的协议。关于如何使用`optional`修饰符,以及如何访问可选协议成员的指导(比如,不确定遵循类型是否已经实现了这些可选成员),可以参见[可选成员需求(Optional Protocol Requirements)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)一章
`required`
该修饰符用于修饰一个类的特定构造器或便捷构造器,表示该类所有的子类都需要实现该构造器。在子类实现该构造器时,同样必须使用`required`修饰符修饰该构造器。
`weak`
`weak`修饰符用于修饰一个变量或一个存储型变量属性,表示该变量或属性通过一个弱引用指向存储其值的对象。该变量或属性的类型必须是一个可选类类型。通过`weak`修饰符可避免强引用循环。关于`weak`修饰符的例子和更多信息,可以参见[弱引用(Weak References)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)一章
### 访问控制级别
Swift提供了三个级别的权限控制:`public`, `internal`, 和 `private`。可以给声明标识以下访问级别修饰符中的一个以指定声明的权限级别。访问控制在[访问控制(Access Control)](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)一章有详细说明。
`public`
修饰符用于修饰声明时,表示该声明可被同一个模块中的代码访问。被`public`权限级别修饰符修饰的声明,还可被其他模块的代码访问,只要该模块注入了该声明所在的模块。
`internal`
修饰符用于修饰声明时,表示该声明只能被同一模块中的代码访问。默认的,绝大多数声明会被隐式的标识上`internal`权限级别修饰符
`private`
修饰符用于修饰声明时,表示该声明只能被同一源文件中的代码访问。
以上的任意一个权限级别修饰符都可以有选择的带上一个参数,该参数由关键字`set`和一对括号组成(比如,`private(set)`)。当想要指明一个变量或下标脚注的setter的访问级别要低于或等于该变量或下标脚注的实际访问级别时,使用这种格式的权限级别修饰符,就像[Getters and Setters](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)一章中讨论的一样。
> 声明修饰符的语法
>
> 声明修饰符 → class | convenience| dynamic | final | infix | lazy | mutating | nonmutating |optional | override | postfix| prefix | required | static | unowned | unowned(safe) |unowned(unsafe) | weak
>
> 声明修饰符 → 权限级别修饰符
>
> 访问级别修饰符 → internal | internal(set)
>
> 访问级别修饰符 → private | private(set)
>
> 访问级别修饰符 → public | public(set)
>
> 访问级别修饰符 → 访问级别修饰符(access-level-modeifier) 访问级别修饰符列表(access-level-modeifiers) _可选_
语句
最后更新于:2022-04-01 05:04:57
> 1.0 翻译:[coverxit](https://github.com/coverxit) 校对:[numbbbbb](https://github.com/numbbbbb), [coverxit](https://github.com/coverxit), [stanzhai](https://github.com/stanzhai),
>
> 2.0 翻译+校对:[littledogboy](https://github.com/littledogboy)
本页包含内容:
[TOC=2]
在 Swift 中,有两种类型的语句:简单语句和控制流语句。简单语句是最常见的,用于构造表达式或者声明。控制流语句则用于控制程序执行的流程,Swift 中有三种类型的控制流语句:循环语句、分支语句和控制传递语句。
循环语句用于重复执行代码块;分支语句用于执行满足特定条件的代码块;控制传递语句则用于修改代码的执行顺序。在稍后的叙述中,将会详细地介绍每一种类型的控制流语句。
是否将分号(`;`)添加到语句的结尾处是可选的。但若要在同一行内写多条独立语句,请务必使用分号。
> 语句语法
> _语句_ → [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression) ; _可选_
> _语句_ → [_声明_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#declaration) ; _可选_
> _语句_ → [_循环语句_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#loop_statement) ; _可选_
> _语句_ → [_分支语句_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#branch_statement) ; _可选_
> _语句_ → [_标记语句(Labeled Statement)_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#labeled_statement)
> _语句_ → [_控制转移语句_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#control_transfer_statement) ; _可选_
> _语句_ → [_XXX语句_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#control_transfer_statement) ; _可选_
> _多条语句(Statements)_ → [_语句_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#statement) [_多条语句(Statements)_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#statements) _可选_
## 循环语句
取决于特定的循环条件,循环语句允许重复执行代码块。Swift 提供四种类型的循环语句:`for`语句、`for-in`语句、`while`语句和`do-while`语句。
通过`break`语句和`continue`语句可以改变循环语句的控制流。有关这两条语句,详情参见 [Break 语句](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#break_statement)和 [Continue 语句](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#continue_statement)。
> 循环语句语法
> _循环语句_ → [_for语句_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#for_statement)
> _循环语句_ → [_for-in语句_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#for_in_statement)
> _循环语句_ → [_while语句_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#wheetatype类型ile_statement)
> _循环语句_ → [_do-while语句_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#do_while_statement)
### For 语句
`for`语句只有在循环条件为真时重复执行代码块,此时计数器递增。
`for`语句的形式如下:
> for `initialzation`; `condition`; `increment` {
> `statements`
> }
_initialzation_、_condition_ 和 _increment_ 之间的分号,以及包围循环体 _statements_ 的大括号都是不可省略的。
`for`语句的执行流程如下:
1. _initialzation_ _循环变量_ 只会被执行一次,通常用于声明和初始化在接下来的循环中需要使用的变量。
2. 判断 _condition_ 循环条件: 如果为`true`,_statements_ _循环体_ 将会被执行,然后转到第3步。如果为`false`,_statements_ 和 _increment_ _循环增量_ 都不会被执行,`for`至此执行完毕。
3. 计算 _increment_ 表达式,然后转到第2步。
在 _initialzation_ 中定义的变量仅在`for`循环的作用域内有效。_condition_ 表达式的值的类型必须遵循`BooleanType`协议。
> For 循环语法
> _for语句_ → for [_for初始条件_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#for_init) _可选_ ; [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression) _可选_ ; [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression) _可选_ [_代码块_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#code_block)
> _for语句_ → for ( [_for初始条件_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#for_init) _可选_ ; [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression) _可选_ ; [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression) _可选_ ) [_代码块_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#code_block)
> _for初始条件_ → [_变量声明_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#variable_declaration) | [_表达式列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression_list)
### For-In 语句
`for-in`语句允许在重复执行代码块的同时,迭代集合(或遵循`Sequence`协议的任意类型)中的每一项。
`for-in`语句的形式如下:
> for `item` in `collection` {
> `statements`
> }
`for-in`语句在循环开始前会调用 _collection_ 表达式的`generate`方法来获取一个生成器类型(这是一个遵循`Generator`协议的类型)的值。接下来循环开始,调用 _collection_ 表达式的`next`方法。如果其返回值不是`None`,它将会被赋给_item_,然后执行 _statements_,执行完毕后回到循环开始处;否则,将不会赋值给 _item_ 也不会执行 _statements_,`for-in`至此执行完毕。
> For-In 循环语法
> _for-in语句_ → for [_模式_](http://wiki.jikexueyuan.com/project/swift/chapter3/07_Patterns.html#pattern) in [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression) [_代码块_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#code_block)
### While 语句
`while`语句当循环条件为真时,允许重复执行代码块。
`while`语句的形式如下:
> while `condition` {
> `statements`
> }
`while`语句的执行流程如下:
1. 计算 _condition_ 表达式: 如果为真`true`,转到第2步。如果为`false`,`while`至此执行完毕。
2. 执行 _statements_ ,然后转到第1步。
由于 _condition_ 的值在 _statements_ 执行前就已计算出,因此`while`语句中的 _statements_ 可能会被执行若干次,也可能不会被执行。
_condition_ 表达式的值的类型必须遵循`BooleanType`协议。同时,_condition_ 表达式也可以使用可选绑定,详情参见[可选绑定](http://wiki.jikexueyuan.com/project/swift/chapter2/01_The_Basics.html#optional_binding)。
> While 循环语法
> _while语句_ → while [_while条件_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#while_condition) [_代码块_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#code_block)
> _条件_ → [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression) | [_声明_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#declaration)
> _条件_ → [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression)
> _条件_ → [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression) | [_条件列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)
> _条件_ → [_可用条件_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statement.html#availability) [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression)
> _条件列表_ → [_条件条件_](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO) [_条件列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)
> _条件_ → [_可用条件_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statement.html#availability) [可选绑定条件](http://wiki.jikexueyuan.com/project/swift/chapter2/01_The_Basics.html#optional_binding)
> _case条件_ → case [_模式_](http://wiki.jikexueyuan.com/project/swift/chapter3/07_Patterns.html#pattern) [构造器](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO) [where](http://wiki.jikexueyuan.com/project/swift/chapter3/DOTO)
> _可选绑定条件_ → [可选绑定头](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO) [持续可选绑定](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO) [持续可选绑定列表](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)
> _可选绑定头_ → let [_模式_](http://wiki.jikexueyuan.com/project/swift/chapter3/07_Patterns.html#pattern) [构造器](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO) var [_模式_](http://wiki.jikexueyuan.com/project/swift/chapter3/07_Patterns.html#pattern) [构造器](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)
> _可持续绑定列表_ → [_模式_](http://wiki.jikexueyuan.com/project/swift/chapter3/07_Patterns.html#pattern) | [构造器](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO) [可选绑定头](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)
### Repeat-While 语句
`repeat-while`语句允许代码块被执行一次或多次。
`repeat-while`语句的形式如下:
> repeat {
> `statements`
> } while `condition`
`repeat-while`语句的执行流程如下:
1. 执行 _statements_,然后转到第2步。
2. 计算 _condition_ 表达式: 如果为`true`,转到第1步。如果为`false`,`repeat-while`至此执行完毕。
由于 _condition_ 表达式的值是在 _statements_ 执行后才计算出,因此`repeat-while`语句中的 _statements_ 至少会被执行一次。
_condition_ 表达式的值的类型必须遵循`BooleanType`协议。同时,_condition_ 表达式也可以使用可选绑定,详情参见[可选绑定](http://wiki.jikexueyuan.com/project/swift/chapter2/01_The_Basics.html#optional_binding)。
> Repeat-While 循环语法
>
> * repeat-while语句_ → repeat [_代码块_](../chapter3/05_Declarations.html#code_block) while[_while条件*](../chapter3/10_Statements.html#while_condition)
## 分支语句
取决于一个或者多个条件的值,分支语句允许程序执行指定部分的代码。显然,分支语句中条件的值将会决定如何分支以及执行哪一块代码。Swift 提供两种类型的分支语句:`if`语句和`switch`语句。
`switch`语句中的控制流可以用`break`语句修改,详情请见[Break 语句](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#break_statement)。
> 分支语句语法
> _分支语句_ → [_if语句_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#if_statement)
> _分支语句_ → [_switch语句_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#switch_statement)
### If 语句
取决于一个或多个条件的值,`if`语句将决定执行哪一块代码。
`if`语句有两种标准形式,在这两种形式里都必须有大括号。
第一种形式是当且仅当条件为真时执行代码,像下面这样:
> if `condition` {
> `statements`
> }
第二种形式是在第一种形式的基础上添加 _else 语句_,当只有一个 else 语句时,像下面这样:
> if `condition` {
> `statements to execute if condition is true`
> } else {
> `statements to execute if condition is false`
> }
同时,else 语句也可包含`if`语句,从而形成一条链来测试更多的条件,像下面这样:
> if `condition 1` {
> `statements to execute if condition 1 is true`
> } else if `condition 2` {
> `statements to execute if condition 2 is true`
> }
> else {
> `statements to execute if both conditions are false`
> }
`if`语句中条件的值的类型必须遵循`LogicValue`协议。同时,条件也可以使用可选绑定,详情参见[可选绑定](http://wiki.jikexueyuan.com/project/swift/chapter2/01_The_Basics.html#optional_binding)。
> If语句语法
> _if语句_ → if [_if条件_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#if_condition) [_代码块_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#code_block) [_else(Clause)_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#else_clause) _可选_
> _if条件_ → [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression) | [_声明_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#declaration)
> _else(Clause)_ → else [_代码块_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#code_block) | else [_if语句_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#if_statement)
### Guard 语句
`guard` 语句用来转移程序控制出其作用域,如果一个或者多个条件不成立。 `guard` 语句的格式如下:
> guard `condition` else {
> `statements`
> }
`guard`语句中条件值的类型必须遵循`LogicValue`协议。且条件可以使用可选绑定,详情参见[可选绑定](http://wiki.jikexueyuan.com/project/swift/chapter2/01_The_Basics.html#optional_binding)。
在`guard`语句中声明的常量或者变量,可用范围从声明开始到作用域结束,常量和变量的值从可选绑定声明中分配。
`guard`语句需要有`else`子句,并且必须调用被`noreturn`属性标记的函数,或者使用下面的语句把程序执行转移到guard语句的作用域外。
* `return`
* `break`
* `continue`
* `throw`
执行转移语句详情参见[控制传递语句](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)
### Switch 语句
取决于`switch`语句的_控制表达式(control expression)_,`switch`语句将决定执行哪一块代码。
`switch`语句的形式如下:
> switch `control expression` {
> case `pattern 1`:
> `statements`
> case `pattern 2` where `condition`:
> `statements`
> case `pattern 3` where `condition`,
> `pattern 4` where `condition`:
> `statements`
> default:
> `statements`
> }
`switch`语句的_控制表达式(control expression)_会首先被计算,然后与每一个 case 的模式(pattern)进行匹配。如果匹配成功,程序将会执行对应的 case 分支里的 _statements_。另外,每一个 case 分支都不能为空,也就是说在每一个 case 分支中至少有一条语句。如果你不想在匹配到的 case 分支中执行代码,只需在该分支里写一条`break`语句即可。
可以用作控制表达式的值是十分灵活的,除了标量类型(scalar types,如`Int`、`Character`)外,你可以使用任何类型的值,包括浮点数、字符串、元组、自定义类的实例和可选(optional)类型,甚至是枚举类型中的成员值和指定的范围(range)等。关于在`switch`语句中使用这些类型,详情参见[控制流](http://wiki.jikexueyuan.com/project/swift/chapter2/05_Control_Flow.html)一章的 [Switch](http://wiki.jikexueyuan.com/project/swift/chapter2/05_Control_Flow.html#switch)。
你可以在模式后面添加一个起保护作用的表达式(guard expression)。_起保护作用的表达式_是这样构成的:关键字`where`后面跟着一个作为额外测试条件的表达式。因此,当且仅当_控制表达式_匹配一个_case_的某个模式且起保护作用的表达式为真时,对应 case 分支中的 _statements_ 才会被执行。在下面的例子中,_控制表达式_只会匹配含两个相等元素的元组,如`(1, 1)`:
~~~
case let (x, y) where x == y:
~~~
正如上面这个例子,也可以在模式中使用`let`(或`var`)语句来绑定常量(或变量)。这些常量(或变量)可以在其对应的起保护作用的表达式和其对应的_case_块里的代码中引用。但是,如果 case 中有多个模式匹配控制表达式,那么这些模式都不能绑定常量(或变量)。
`switch`语句也可以包含默认(`default`)分支,只有其它 case 分支都无法匹配控制表达式时,默认分支中的代码才会被执行。一个`switch`语句只能有一个默认分支,而且必须在`switch`语句的最后面。
尽管模式匹配操作实际的执行顺序,特别是模式的计算顺序是不可知的,但是 Swift 规定`switch`语句中的模式匹配的顺序和书写源代码的顺序保持一致。因此,当多个模式含有相同的值且能够匹配控制表达式时,程序只会执行源代码中第一个匹配的 case 分支中的代码。
#### Switch 语句必须是完备的
在 Swift 中,`switch`语句中控制表达式的每一个可能的值都必须至少有一个 case 分支与之对应。在某些情况下(例如,表达式的类型是`Int`),你可以使用默认块满足该要求。
#### 不存在隐式的贯穿(fall through)
当匹配的 case 分支中的代码执行完毕后,程序会终止`switch`语句,而不会继续执行下一个 case 分支。这就意味着,如果你想执行下一个 case 分支,需要显式地在你需要的 case 分支里使用`fallthrough`语句。关于`fallthrough`语句的更多信息,详情参见 [Fallthrough 语句](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#fallthrough_statement)。
> Switch语句语法
> _switch语句_ → switch [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression) { [_SwitchCase列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#switch_cases) _可选_ }
> _SwitchCase列表_ → [_SwitchCase_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#switch_case) [_SwitchCase列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#switch_cases) _可选_
> _SwitchCase_ → [_case标签_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#case_label) [_多条语句(Statements)_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#statements) | [_default标签_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#default_label) [_多条语句(Statements)_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#statements)
> _SwitchCase_ → [_case标签_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#case_label) ; | [_default标签_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#default_label) ;
> _case标签_ → case [_case项列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#case_item_list) :
> _case项列表_ → [_模式_](http://wiki.jikexueyuan.com/project/swift/chapter3/07_Patterns.html#pattern) [_guard-clause_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#guard_clause) _可选_ | [_模式_](http://wiki.jikexueyuan.com/project/swift/chapter3/07_Patterns.html#pattern) [_guard-clause_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#guard_clause) _可选_ , [_case项列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#case_item_list)
> _default标签_ → default :
> _where-clause_ → where [_guard-expression_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#guard)
> _where-expression_ → [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression)
## 带标签的语句
你可以在循环语句或`switch`语句前面加上_标签_,它由标签名和紧随其后的冒号(:)组成。在`break`和`continue`后面跟上标签名可以显式地在循环语句或`switch`语句中更改控制流,把控制权传递给指定标签标记的语句。关于这两条语句用法,详情参见 [Break 语句](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#break_statement)和 [Continue 语句](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#continue_statement)。
标签的作用域是该标签所标记的语句之后的所有语句。你可以不使用带标签的语句,但只要使用它,标签名就必唯一。
关于使用带标签的语句的例子,详情参见[控制流](http://wiki.jikexueyuan.com/project/swift/chapter2/05_Control_Flow.html)一章的[带标签的语句](http://wiki.jikexueyuan.com/project/swift/chapter2/05_Control_Flow.html#labeled_statements)。
> 标记语句语法
> _标记语句(Labeled Statement)_ → [_语句标签_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#statement_label) [_循环语句_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#loop_statement) | [_语句标签_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#statement_label) [_switch语句_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#switch_statement)
> _语句标签_ → [_标签名称_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#label_name) :
> _标签名称_ → [_标识符_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#identifier)
## 控制传递语句
通过无条件地把控制权从一片代码传递到另一片代码,控制传递语句能够改变代码执行的顺序。Swift 提供四种类型的控制传递语句:`break`语句、`continue`语句、`fallthrough`语句和`return`语句。
> 控制传递语句(Control Transfer Statement) 语法
> _控制传递语句_ → [_break语句_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#break_statement)
> _控制传递语句_ → [_continue语句_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#continue_statement)
> _控制传递语句_ → [_fallthrough语句_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#fallthrough_statement)
> _控制传递语句_ → [_return语句_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#return_statement)
> _控制传递语句_ → [_throw语句_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#throw_statement)
### Break 语句
`break`语句用于终止循环或`switch`语句的执行。使用`break`语句时,可以只写`break`这个关键词,也可以在`break`后面跟上标签名(label name),像下面这样:
> break
> break `label name`
当`break`语句后面带标签名时,可用于终止由这个标签标记的循环或`switch`语句的执行。
而当只写`break`时,则会终止`switch`语句或上下文中包含`break`语句的最内层循环的执行。
在这两种情况下,控制权都会被传递给循环或`switch`语句外面的第一行语句。
关于使用`break`语句的例子,详情参见[控制流](http://wiki.jikexueyuan.com/project/swift/chapter2/05_Control_Flow.html)一章的 [Break](http://wiki.jikexueyuan.com/project/swift/chapter2/05_Control_Flow.html#break) 和[带标签的语句](http://wiki.jikexueyuan.com/project/swift/chapter2/05_Control_Flow.html#labeled_statements)。
> Break 语句语法
> _break语句_ → break [_标签名称_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#label_name) _可选_
### Continue 语句
`continue`语句用于终止循环中当前迭代的执行,但不会终止该循环的执行。使用`continue`语句时,可以只写`continue`这个关键词,也可以在`continue`后面跟上标签名(label name),像下面这样:
> continue
> continue `label name`
当`continue`语句后面带标签名时,可用于终止由这个标签标记的循环中当前迭代的执行。
而当只写`break`时,可用于终止上下文中包含`continue`语句的最内层循环中当前迭代的执行。
在这两种情况下,控制权都会被传递给循环外面的第一行语句。
在`for`语句中,`continue`语句执行后,_increment_ 表达式还是会被计算,这是因为每次循环体执行完毕后 _increment_表达式都会被计算。
关于使用`continue`语句的例子,详情参见[控制流](http://wiki.jikexueyuan.com/project/swift/chapter2/05_Control_Flow.html)一章的 [Continue](http://wiki.jikexueyuan.com/project/swift/chapter2/05_Control_Flow.html#continue) 和[带标签的语句](http://wiki.jikexueyuan.com/project/swift/chapter2/05_Control_Flow.html#labeled_statements)。
> Continue 语句语法
> _continue语句_ → continue [_标签名称_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#label_name) _可选_
### Fallthrough 语句
`fallthrough`语句用于在`switch`语句中传递控制权。`fallthrough`语句会把控制权从`switch`语句中的一个 case 传递给下一个 case 。这种传递是无条件的,即使下一个 case 的模式与`switch`语句的控制表达式的值不匹配。
`fallthrough`语句可出现在`switch`语句中的任意 case 里,但不能出现在最后一个 case 分支中。同时,`fallthrough`语句也不能把控制权传递给使用了可选绑定的 case 分支。
关于在`switch`语句中使用`fallthrough`语句的例子,详情参见[控制流](http://wiki.jikexueyuan.com/project/swift/chapter2/05_Control_Flow.html)一章的[控制传递语句](http://wiki.jikexueyuan.com/project/swift/chapter2/05_Control_Flow.html#control_transfer_statements)。
> Fallthrough 语句语法
> _fallthrough语句_ → fallthrough
### Return 语句
`return`语句用于在函数或方法的实现中将控制权传递给调用者,接着程序将会从调用者的位置继续向下执行。
使用`return`语句时,可以只写`return`这个关键词,也可以在`return`后面跟上表达式,像下面这样:
> return
> return `expression`
当`return`语句后面带表达式时,表达式的值将会返回给调用者。如果表达式值的类型与调用者期望的类型不匹配,Swift 则会在返回表达式的值之前将表达式值的类型转换为调用者期望的类型。
而当只写`return`时,仅仅是将控制权从该函数或方法传递给调用者,而不返回一个值。(这就是说,该函数或方法的返回类型为`Void`或`()`)
> Return 语句语法
> _return语句_ → return [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression) _可选_
### Availability 语句
可用性条件,被当做`if` ,`while` 语句的条件,并且 `guard` 语句在运行时会基于特定的语法格式查询接口的可用性。
avaliability 语句的形式如下:
> if #available(`platform name version`,`...`, *) {
> `statements to execute if the APIs are available`
> } else {
> `fallback statements to execute if the APIs are unavailable`
> }
可用性条件执行一个代码块时,取决于在运行时想要使用的接口是否可用。 当编译器检查到代码块中的接口是可用的,则从可用性条件中获取相应信息。
可用性条件使用逗号分隔平台名称和版本列表。使用`iOS`,`OSX`,以及`watchOS`为平台名称,包括相应的版本号。*参数是必需的。在任何平台上代码块主体都被可用性条件保护起来,由满足最低部署条件的目标设备运行。
与布尔类型条件不同,不能用逻辑运算符 && 和 || 合并可用性条件。
> 可用性条件语法
> _可用性条件_ → #available ( [availability-arguments](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO) )
> _可用性条件_ → [availability-argument](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO) | [availability-argument](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO) , [availability-arguments](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)
> _可用性条件_ → [平台名称](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO) [版本号](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)
> _可用性条件_ → *
> _平台名称_ → iOS | iOSApplicationExtension
> _平台名称_ → OSX | OSXApplicationExtension
> _平台名称_ → watchOS
> _版本号_ → [十进制数字](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)
> _版本号_ → [十进制数字](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO) . [十进制数字](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)
> _版本号_ → [十进制数字](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO) . [十进制数字](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO) . [十进制数字](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)
### Throw 语句
`throw`语句出现在抛出函数或者抛出方法体内,或者类型被`throws`关键字标记的表达式体内。
`throw`语句使程序结束执行当前的作用域,并在封闭作用域中传播错误。抛出的错误会一直传播,直到被`do`语句的`catch`子句处理掉。
`throw`语句由`throw`关键字 跟一个表达式组成 ,如下所示。
> throw `expression`
表达式值的类型必须遵循 `LogicValue`协议
关于如何使用`throw`语句的例子,详情参见[错误处理](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)一章的[抛出错误](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)。
> throw 语句语法
> _抛出语句_ → throw _[表达式](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)_
### Defer 语句
`defer` 语句用于转移程序控制出延迟语句作用域之前执行代码。
在 `defer` 语句中的语句无论程序控制如何转移都会执行。这意味着 `defer` 语句可以被使用在以下这些情况,像手动得执行资源管理,关闭文件描述,或者即使抛出了错误也需要去实现执行一些动作。
如果多个 `defer` 语句出现在同一范围内,那么它们执行的顺序与出现的顺序相反。给定作用域中的第一个`defer` 语句,会在最后执行,这意味着最后执行的延迟语句中的语句涉及的资源可以被其他 `defer`语句清理掉。
> 1 func f( ) {
> 2 defer { print("First") }
> 3 defer { print("Second") }
> 4 defer { print("Third") }
> 5 }
> 6 f()
> 7 // prints "Third"
> 8 // prints "Second"
> 9 // prints "First"
`defer` 语句中的语句无法转移程序控制出延迟语句。
> defer 语句语法
> _延迟语句_ → defer _[代码块](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)_
### Do 语句
`do` 语句用于引入一个新的作用域,该作用域中可以含有一个或多个`catch`子句,catch子句中定义了一些匹配错误情况的模式。`do` 语句作用域内定义的常量和变量,只能在do语句作用域内访问。
swift 中的 do 语句与C 中限定代码块界限的大括号 ({})很相似,并且在程序运行的时候并不会造成系统开销。
> do {
> try `expression`
> `statements`
> } catch `pattern 1` {
> `statements`
> } catch `pattern 2` where condition {
> `statements`
> }
如同`switch`语句,编译器会判断`catch`子句是否被遗漏。如果catch没有被遗漏,则认为错误被处理。否则,错误会自动传播出包含作用域,被一个封闭的`catch`语句或抛出函数处理掉,包含函数必须以`throws`关键字声明。
为了确保错误已经被处理,使用一个匹配所有错误的`catch`子句,如通配符模式(_)。如果一个`catch`子句不指定一种模式,`catch`子句会匹配和约束任何局部变量命名的`error`。有关在`catch`子句中使用模式的更多信息,详见[模式](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)。
关于在一些`catch`子句中如何使用`do`语句的例子,详情参见[错误处理](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)一章的[抛出错误](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)。
> do 语句语法 → do [ _代码块_ ](../chapter3/05_Declarations.html#code_block) [catch](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)
> catch → _[catch子句](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO) [catch子句](http://wiki.jikexueyuan.com/project/swift/chapter3/TODO)_
> catch → catch _[_模式*](../chapter3/07_Patterns.html#pattern)*可选的_ [_where_]() _可选的_ [ _代码块*](../chapter3/05_Declarations.html#code_block)
表达式
最后更新于:2022-04-01 05:04:55
> 1.0 翻译:[sg552](https://github.com/sg552) 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
>
> 2.0 翻译+校对:[EudeMorgen](https://github.com/EudeMorgen)
本页包含内容:
[TOC=2]
Swift 中存在四种表达式: 前缀(prefix)表达式,二元(binary)表达式,主要(primary)表达式和后缀(postfix)表达式。表达式可以返回一个值,以及运行某些逻辑(causes a side effect)。
前缀表达式和二元表达式就是对某些表达式使用各种运算符(operators)。 主要表达式是最短小的表达式,它提供了获取(变量的)值的一种途径。 后缀表达式则允许你建立复杂的表达式,例如配合函数调用和成员访问。 每种表达式都在下面有详细论述。
> 表达式语法
> _表达式_ → [ _试算子(try operator)_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#*) _可选_ | [ _前置表达式_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#prefix_expression) | [ _二元表达式列表_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#binary_expressions) _可选_
> _表达式列表_ → [ _表达式_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression) | [ _表达式_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression) , [ _表达式列表_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression_list)
## 前缀表达式(Prefix Expressions)
前缀表达式由可选的前缀符号和表达式组成。(这个前缀符号只能接收一个参数)
对于这些操作符的使用,请参见: Basic Operators and Advanced Operators
作为对上面标准库运算符的补充,你也可以对 某个函数的参数使用 '&'运算符。 更多信息,请参见: "In-Out parameters".
> 前置表达式语法
> _前置表达式_ → [ _前置运算符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/LexicalStructure.html#prefix_operator) _可选_ [ _后置表达式_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#postfix_expression)
> _前置表达式_ → [ _写入写出(in-out)表达式_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#in_out_expression)
> _写入写出(in-out)表达式_ → & [ _标识符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/LexicalStructure.html#identifier)
## try 操作符(try operator)
try表达式由紧跟在可能会出错的表达式后面的`try`操作符组成,形式如下: `try expression` 强制的try表示由紧跟在可能会出错的表达式后面的`try!`操作符组成,出错时会产生一个运行时错误,形式如下: `try! expression` 关于`try`更多的例子和信息请参见:Catching and Handling Errors.
> try表达式语法
> _try 操作符_ → [ _try_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/LexicalStructure.html#try_operator) | _try!_
## 二元表达式(Binary Expressions)
二元表达式由 "左边参数" + "二元运算符" + "右边参数" 组成, 它有如下的形式:
> `left-hand argument` `operator` `right-hand argument`
关于这些运算符(operators)的更多信息,请参见:Basic Operators and Advanced Operators.
> 注意
> 在解析时, 一个二元表达式表示为一个一级数组(a flat list), 这个数组(List)根据运算符的先后顺序,被转换成了一个tree. 例如: 2 + 3 _5 首先被认为是: 2, + , `3`, _, 5\. 随后它被转换成 tree (2 + (3 * 5))
>
> 二元表达式语法
> _二元表达式_ → [ _二元运算符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/LexicalStructure.html#binary_operator) [ _前置表达式_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#prefix_expression)
> _二元表达式_ → [ _赋值运算符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#assignment_operator) [ _前置表达式_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#prefix_expression)
> _二元表达式_ → [ _条件运算符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#conditional_operator) [_前置表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#prefix_expression)
> _二元表达式_ → [ _类型转换运算符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#type_casting_operator)
> _二元表达式列表_ → [ _二元表达式_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#binary_expression) [ _二元表达式列表_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#binary_expressions) _可选_
> _赋值操作符_
## 赋值表达式(Assignment Operator)
赋值表达式会对某个给定的表达式赋值。 它有如下的形式;
> `expression` = `value`
就是把右边的 _value_ 赋值给左边的 _expression_. 如果左边的_expression_ 需要接收多个参数(是一个tuple ),那么右边必须也是一个具有同样数量参数的tuple. (允许嵌套的tuple)
~~~
(a, _, (b, c)) = ("test", 9.45, (12, 3))
// a is "test", b is 12, c is 3, and 9.45 is ignored
~~~
赋值运算符不返回任何值。
> 赋值运算符语法
> _赋值运算符_ → =
## 三元条件运算符(Ternary Conditional Operator)
三元条件运算符 是根据条件来获取值。 形式如下:
> `condition` ? `expression used if true` : `expression used if false`
如果 `condition` 是true, 那么返回 第一个表达式的值(此时不会调用第二个表达式), 否则返回第二个表达式的值(此时不会调用第一个表达式)。
想看三元条件运算符的例子,请参见: Ternary Conditional Operator.
> 三元条件运算符语法
> _三元条件运算符_ → ? [ _表达式_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression) :
## 类型转换运算符(Type-Casting Operators)
有4种类型转换运算符: `is`,`as`,`?`和`!`. 它们有如下的形式:
> `expression` is `type`
> `expression` as `type`
> `expression` is? `type` `expression` as! `type`
`is`运算符在程序运行时检查表达式能否向下转化为指定的类型,如果可以在返回`ture`,如果不行,则返回`false`。
`as`运算符在程序编译时执行类型转化,且总是成功,比如进行向上转换(upcast)和桥接(bridging)。向上转换指把表达式转换成类型的超类的一个是实例而不使用中间的变量。以下表达式是等价的:
~~~
func f(any: Any) { print("Function for Any") }
func f(int: Int) { print("Function for Int") }
let x = 10
f(x)
// prints "Function for Int"
let y: Any = x
f(y)
// prints "Function for Any"
f(x as Any)
// prints "Function for Any"
~~~
桥接运算可以让你把一个Swift标准库中的类型的表达式作为一个与之相关的基础类(比如NSString)来使用,而不需要新建一个实例。关于桥接的更多实例参见Using Swift with Cocoa and Objective-C中的Cocoa Data Types。
`as?`操作符为带条件的类型转换。`as?`操作符返回可选的转换类型。在运行时,如果转换成功,表达式的值会被覆盖掉再返回,如果转换不成功的话,则返回`nil`。如果条件转换中的条件的真值一开始就已经确定真伪了,则在编译时会报错。
`a!`操作符表示强制转换,其返回指定的类型,而不是可选的类型。如果转换失败,则会出现运行时错误。表达式`x as T`效果等同于`(x as? T)!`。
关于类型转换的更多内容和例子,请参见: Type Casting.
> 类型转换运算符(type-casting-operator)语法
> _类型转换运算符_ → is [ _类型_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type) _类型转换运算符_ → as [ _类型_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type) _类型转换运算符_ → is ? [ _类型_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type) _类型转换运算符_ → as ! [ _类型_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)
## 主表达式(Primary Expressions)
`主表达式`是最基本的表达式。 它们可以跟 前缀表达式,二元表达式,后缀表达式以及其他主要表达式组合使用。
> 主表达式语法
> _主表达式_ → [_标识符_](http://wiki.jikexueyuan.com/project/swift/chapter3/LexicalStructure.html#identifier) [_泛型参数子句_](http://wiki.jikexueyuan.com/project/swift/chapter3/GenericParametersAndArguments.html#generic_argument_clause) _可选_
> _主表达式_ → [_字符型表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#literal_expression)
> _主表达式_ → [_self表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#self_expression)
> _主表达式_ → [_超类表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#superclass_expression)
> _主表达式_ → [_闭包表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#closure_expression)
> _主表达式_ → [_圆括号表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#parenthesized_expression)
> _主表达式_ → [_隐式成员表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#implicit_member_expression)
> _主表达式_ → [_通配符表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#wildcard_expression)
### 字符型表达式(Literal Expression)
由这些内容组成:普通的字符(string, number) , 一个字符的字典或者数组,或者下面列表中的特殊字符。
| 字符(Literal) | 类型(Type) | 值(Value) |
| --- | --- | --- |
| /FILE | String | 所在的文件名 |
| /LINE | Int | 所在的行数 |
| /COLUMN | Int | 所在的列数 |
| /FUNCTION | String | 所在的function 的名字 |
在某个函数(function)中,`__FUNCTION__` 会返回当前函数的名字。 在某个方法(method)中,它会返回当前方法的名字。 在某个property 的getter/setter中会返回这个属性的名字。 在特殊的成员如init/subscript中 会返回这个关键字的名字,在某个文件的顶端(the top level of a file),它返回的是当前module的名字。
当作为函数或者方法时,字符型表达式的值在被调用时初始化。
~~~
func logFunctionName(string: String = __FUNCTION__) {
print(string)
}
func myFunction() {
logFunctionName() // Prints "myFunction()".
}
myFunction()
namedArgs(1, withJay: 2)
~~~
一个`array literal`,是一个有序的值的集合。 它的形式是:
> [`value 1`, `value 2`, `...`]
数组中的最后一个表达式可以紧跟一个逗号(','). []表示空数组 。 array literal的type是 T[], 这个T就是数组中元素的type. 如果该数组中有多种type, T则是跟这些type的公共`supertype`最接近的type.空的`array literal`由一组方括号定义,可用来创建特定类型的空数组。
~~~
var emptyArray: [Double] = []
~~~
一个`dictionary literal` 是一个包含无序的键值对(key-value pairs)的集合,它的形式是:
> [`key 1`: `value 1`, `key 2`: `value 2`, `...`]
dictionary 的最后一个表达式可以是一个逗号(','). [:] 表示一个空的dictionary. 它的type是 Dictionary (这里KeyType表示 key的type, ValueType表示 value的type) 如果这个dictionary 中包含多种 types, 那么KeyType, Value 则对应着它们的公共supertype最接近的type( closest common supertype).一个空的dictionary literal由方括号中加一个冒号组成,以此来与空array literal区分开,可以使用空的dictionary literal来创建特定类型的键值对。
~~~
var emptyDictionary: [String: Double]=[:]
~~~
> 字面量表达式语法
> _字面量表达式_ → [_字面量_](http://wiki.jikexueyuan.com/project/swift/chapter3/LexicalStructure.html#literal)
> _字面量表达式_ → [_数组字面量_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#array_literal) | [_字典字面量_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#dictionary_literal)
> _字面量表达式_ → __FILE__ | __LINE__ | __COLUMN__ | __FUNCTION__
> _数组字面量_ → [ [_数组字面量项列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#array_literal_items) _可选_ ]
> _数组字面量项列表_ → [_数组字面量项_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#array_literal_item) , _可选_ | [_数组字面量项_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#array_literal_item) , [_数组字面量项列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#array_literal_items)
> _数组字面量项_ → [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression)
> _字典字面量_ → [ [_字典字面量项列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#dictionary_literal_items) ] | [ : ]
> _字典字面量项列表_ → [_字典字面量项_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#dictionary_literal_item) , _可选_ | [_字典字面量项_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#dictionary_literal_item) , [_字典字面量项列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#dictionary_literal_items)
> _字典字面量项_ → [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression) : [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression)
### self表达式(Self Expression)
self表达式是对 当前type 或者当前instance的引用。它的形式如下:
> self
> self.`member name`
> self[`subscript index`]
> self(`initializer arguments`) self.init(`initializer arguments`)
如果在 initializer, subscript, instance method中,self等同于当前type的instance. 在一个静态方法(static method), 类方法(class method)中, self等同于当前的type.
当访问 member(成员变量时), self 用来区分重名变量(例如函数的参数). 例如, (下面的 self.greeting 指的是 var greeting: String, 而不是 init(greeting: String) )
~~~
class SomeClass {
var greeting: String
init(greeting: String) {
self.greeting = greeting
}
}
~~~
在mutating 方法中, 你可以使用self 对 该instance进行赋值。
~~~
struct Point {
var x = 0.0, y = 0.0
mutating func moveByX(deltaX: Double, y deltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY)
}
}
~~~
> Self 表达式语法
> _self表达式_ → self
> _self表达式_ → self . [_标识符_](http://wiki.jikexueyuan.com/project/swift/chapter3/LexicalStructure.html#identifier)
> _self表达式_ → self [ [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression) ]
> _self表达式_ → self . init
### 超类表达式(Superclass Expression)
超类表达式可以使我们在某个class中访问它的超类. 它有如下形式:
> super.`member name`
> super[`subscript index`]
> super.init(`initializer arguments`)
形式1 用来访问超类的某个成员(member). 形式2 用来访问该超类的 subscript 实现。 形式3 用来访问该超类的 initializer.
子类(subclass)可以通过超类(superclass)表达式在它们的 member, subscripting 和 initializers 中来利用它们超类中的某些实现(既有的方法或者逻辑)。
> 超类(superclass)表达式语法
> _超类表达式_ → [_超类方法表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#superclass_method_expression) | [_超类下标表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#超类下标表达式) | [_超类构造器表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#superclass_initializer_expression)
> _超类方法表达式_ → super . [_标识符_](http://wiki.jikexueyuan.com/project/swift/chapter3/LexicalStructure.html#identifier)
> _超类下标表达式_ → super [ [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression) ]
> _超类构造器表达式_ → super . init
### 闭包表达式(Closure Expression)
闭包(closure) 表达式可以建立一个闭包(在其他语言中也叫 lambda, 或者 匿名函数(anonymous function)). 跟函数(function)的声明一样, 闭包(closure)包含了可执行的代码(跟方法主体(statement)类似) 以及接收(capture)的参数。 它的形式如下:
~~~
{ (parameters) -> return type in
statements
}
~~~
闭包的参数声明形式跟方法中的声明一样, 请参见:Function Declaration.
闭包还有几种特殊的形式, 让使用更加简洁:
* 闭包可以省略 它的参数的type 和返回值的type. 如果省略了参数和参数类型,就也要省略 'in'关键字。 如果被省略的type 无法被编译器获知(inferred) ,那么就会抛出编译错误。
* 闭包可以省略参数,转而在方法体(statement)中使用 $0, $1, $2 来引用出现的第一个,第二个,第三个参数。
* 如果闭包中只包含了一个表达式,那么该表达式就会自动成为该闭包的返回值。 在执行 'type inference '时,该表达式也会返回。
下面几个 闭包表达式是 等价的:
~~~
myFunction {
(x: Int, y: Int) -> Int in
return x + y
}
myFunction {
(x, y) in
return x + y
}
myFunction { return $0 + $1 }
myFunction { $0 + $1 }
~~~
关于 向闭包中传递参数的内容,参见: Function Call Expression.
闭包表达式可以通过一个参数列表(capture list) 来显式指定它需要的参数。 参数列表 由中括号 [] 括起来,里面的参数由逗号','分隔。一旦使用了参数列表,就必须使用'in'关键字(在任何情况下都得这样做,包括忽略参数的名字,type, 返回值时等等)。
在闭包的参数列表( capture list)中, 参数可以声明为 'weak' 或者 'unowned' .
~~~
myFunction { print(self.title) } // strong capture
myFunction { [weak self] in print(self!.title) } // weak capture
myFunction { [unowned self] in print(self.title) } // unowned capture
~~~
在参数列表中,也可以使用任意表达式来赋值. 该表达式会在 闭包被执行时赋值,然后按照不同的力度来获取(这句话请慎重理解)。(captured with the specified strength. ) 例如:
~~~
// Weak capture of "self.parent" as "parent"
myFunction { [weak parent = self.parent] in print(parent!.title) }
~~~
关于闭包表达式的更多信息和例子,请参见: Closure Expressions.
> 闭包表达式语法
> _闭包表达式_ → { [_闭包签名(Signational)_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#closure_signature) _可选_ [_多条语句(Statements)_](http://wiki.jikexueyuan.com/project/swift/chapter3/10_Statements.html#statements) }
> _闭包签名(Signational)_ → [_参数子句_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#parameter_clause) [_函数结果_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#function_result) _可选_ in
> _闭包签名(Signational)_ → [_标识符列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/LexicalStructure.html#identifier_list) [_函数结果_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#function_result) _可选_ in
> _闭包签名(Signational)_ → [_捕获(Capature)列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#capture_list) [_参数子句_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#parameter_clause) [_函数结果_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#function_result) _可选_ in
> _闭包签名(Signational)_ → [_捕获(Capature)列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#capture_list) [_标识符列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/LexicalStructure.html#identifier_list) [_函数结果_](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#function_result) _可选_ in
> _闭包签名(Signational)_ → [_捕获(Capature)列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#capture_list) in
> _捕获(Capature)列表_ → [ [_捕获(Capature)说明符_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#capture_specifier) [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression) ]
> _捕获(Capature)说明符_ → weak | unowned | unowned(safe) | unowned(unsafe)
### 隐式成员表达式(Implicit Member Expression)
在可以判断出类型(type)的上下文(context)中,隐式成员表达式是访问某个type的member( 例如 class method, enumeration case) 的简洁方法。 它的形式是:
> .`member name`
例子:
~~~
var x = MyEnumeration.SomeValue
x = .AnotherValue
~~~
> 隐式成员表达式语法
> _隐式成员表达式_ → . [_标识符_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#identifier)
### 圆括号表达式(Parenthesized Expression)
圆括号表达式由多个子表达式和逗号','组成。 每个子表达式前面可以有 identifier x: 这样的可选前缀。形式如下:
> (`identifier 1`: `expression 1`, `identifier 2`: `expression 2`, `...`)
圆括号表达式用来建立tuples , 然后把它做为参数传递给 function. 如果某个圆括号表达式中只有一个 子表达式,那么它的type就是 子表达式的type。例如: (1)的 type是Int, 而不是(Int)
> 圆括号表达式(Parenthesized Expression)语法
> _圆括号表达式_ → ( [_表达式元素列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression_element_list) _可选_ )
> _表达式元素列表_ → [_表达式元素_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression_element) | [_表达式元素_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression_element) , [_表达式元素列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression_element_list)
> _表达式元素_ → [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression) | [_标识符_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#identifier) : [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression)
### 通配符表达式(Wildcard Expression)
通配符表达式用来忽略传递进来的某个参数。例如:下面的代码中,10被传递给x, 20被忽略(译注:好奇葩的语法。。。)
~~~
(x, _) = (10, 20)
// x is 10, 20 is ignored
~~~
> 通配符表达式语法
> _通配符表达式_ → _
## 后缀表达式(Postfix Expressions)
后缀表达式就是在某个表达式的后面加上 操作符。 严格的讲,每个主要表达式(primary expression)都是一个后缀表达式
Swift 标准库提供了下列后缀表达式:
* ++ Increment
* -- Decrement
对于这些操作符的使用,请参见: Basic Operators and Advanced Operators
> 后置表达式语法
> _后置表达式_ → [_主表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#primary_expression)
> _后置表达式_ → [_后置表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#postfix_expression) [_后置运算符_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#postfix_operator)
> _后置表达式_ → [_函数调用表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#function_call_expression)
> _后置表达式_ → [_构造器表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#initializer_expression)
> _后置表达式_ → [_显示成员表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#explicit_member_expression)
> _后置表达式_ → [_后置self表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#postfix_self_expression)
> _后置表达式_ → [_动态类型表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#dynamic_type_expression)
> _后置表达式_ → [_下标表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#subscript_expression)
> _后置表达式_ → [_强制取值(Forced Value)表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#forced_value_expression)
> _后置表达式_ → [_可选链(Optional Chaining)表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#optional_chaining_expression)
### 函数调用表达式(Function Call Expression)
函数调用表达式由函数名和参数列表组成。它的形式如下:
> `function name`(`argument value 1`, `argument value 2`)
如果该function 的声明中指定了参数的名字,那么在调用的时候也必须得写出来. 例如:
> `function name`(`argument name 1`: `argument value 1`, `argument name 2`: `argument value 2`)
可以在 函数调用表达式的尾部(最后一个参数之后)加上 一个闭包(closure) , 该闭包会被目标函数理解并执行。它具有如下两种写法:
~~~
// someFunction takes an integer and a closure as its arguments
someFunction(x, {$0 == 13}+
someFunction(x) {$0 == 13}
~~~
如果闭包是该函数的唯一参数,那么圆括号可以省略。
~~~
// someFunction takes a closure as its only argument
myData.someMethod() {$0 == 13}
myData.someMethod {$0 == 13}
~~~
> 函数调用表达式语法
> _函数调用表达式_ → [_后置表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#postfix_expression) [_圆括号表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#parenthesized_expression)
> _函数调用表达式_ → [_后置表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#postfix_expression) [_圆括号表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#parenthesized_expression) _可选_ [_后置闭包(Trailing Closure)_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#trailing_closure)
> _后置闭包(Trailing Closure)_ → [_闭包表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#closure_expression)
### 初始化函数表达式(Initializer Expression)
Initializer表达式用来给某个Type初始化。 它的形式如下:
> `expression`.init(`initializer arguments`)
初始化函数表达式在调用函数时用来初始某个Type。 也可以使用初始化函数表达式来委托调用(delegate to )到superclass的initializers.
~~~
class SomeSubClass: SomeSuperClass {
init() {
// subclass initialization goes here
super.init()
}
}
~~~
和函数类似, 初始化表达式可以用作数值。 举例来说:
~~~
// Type annotation is required because String has multiple initializers.
let initializer: Int -> String = String.init
let oneTwoThree = [1, 2, 3].map(initializer).reduce("", combine: +)
print(oneTwoThree)
// prints "123"
~~~
如果要用名字来指定某个type, 可以不用初始化函数表达式直接使用type的initializer。在其他情况下, 你必须使用初始化函数表达式。
~~~
let s1 = SomeType.init(data: 3) // Valid
let s2 = SomeType(data: 1) // Also valid
let s4 = someValue.dynamicType(data: 5) // Error
let s3 = someValue.dynamicType.init(data: 7) // Valid
~~~
> 构造器表达式语法
> _构造器表达式_ → [_后置表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#postfix_expression) . init
### 显式成员表达式(Explicit Member Expression)
显示成员表达式允许我们访问type, tuple, module的成员变量。它的形式如下:
> `expression`.`member name`
该member 就是某个type在声明时候所定义(declaration or extension) 的变量, 例如:
~~~
class SomeClass {
var someProperty = 42
}
let c = SomeClass()
let y = c.someProperty // Member access
~~~
对于tuple, 要根据它们出现的顺序(0, 1, 2...)来使用:
~~~
var t = (10, 20, 30)
t.0 = t.1
// Now t is (20, 20, 30)
~~~
对于某个module的member的调用,只能调用在top-level声明中的member.
> 显式成员表达式语法
> _显示成员表达式_ → [_后置表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#postfix_expression) . [_十进制数字_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#decimal_digit)
> _显示成员表达式_ → [_后置表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#postfix_expression) . [_标识符_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#identifier) [_泛型参数子句_](http://wiki.jikexueyuan.com/project/swift/chapter3/GenericParametersAndArguments.html#generic_argument_clause) _可选_
### 后缀self表达式(Postfix Self Expression)
后缀表达式由 某个表达式 + '.self' 组成. 形式如下:
> `expression`.self
> `type`.self
形式1 表示会返回 expression 的值。例如: x.self 返回 x
形式2:返回对应的type。我们可以用它来动态的获取某个instance的type。
> 后置Self 表达式语法
> _后置self表达式_ → [_后置表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#postfix_expression) . self
### dynamic表达式(Dynamic Type Expression)
(因为dynamicType是一个独有的方法,所以这里保留了英文单词,未作翻译, --- 类似与self expression)
dynamicType 表达式由 某个表达式 + '.dynamicType' 组成。
> `expression`.dynamicType
上面的形式中, expression 不能是某type的名字(当然了,如果我都知道它的名字了还需要动态来获取它吗)。动态类型表达式会返回"运行时"某个instance的type, 具体请看下面的列子:
~~~
class SomeBaseClass {
class func printClassName() {
println("SomeBaseClass")
}
}
class SomeSubClass: SomeBaseClass {
override class func printClassName() {
println("SomeSubClass")
}
}
let someInstance: SomeBaseClass = SomeSubClass()
// someInstance is of type SomeBaseClass at compile time, but
// someInstance is of type SomeSubClass at runtime
someInstance.dynamicType.printClassName()
// prints "SomeSubClass"
~~~
> 动态类型表达式语法
> _动态类型表达式_ → [_后置表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#postfix_expression) . dynamicType
### 下标脚本表达式(Subscript Expression)
下标脚本表达式提供了通过下标脚本访问getter/setter 的方法。它的形式是:
> `expression`[`index expressions`]
可以通过下标脚本表达式通过getter获取某个值,或者通过setter赋予某个值.
关于subscript的声明,请参见: Protocol Subscript Declaration.
> 附属脚本表达式语法
> _附属脚本表达式_ → [_后置表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#postfix_expression) [ [_表达式列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#expression_list) ]
### 强制取值表达式(Forced-Value Expression)
强制取值表达式用来获取某个目标表达式的值(该目标表达式的值必须不是nil )。它的形式如下:
> `expression`!
如果该表达式的值不是nil, 则返回对应的值。 否则,抛出运行时错误(runtime error)。 返回的值可能会被需改,可以是被赋值了,也可以是出现异常造成的。比如:
~~~
var x: Int? = 0
x!++
// x is now 1
var someDictionary = ["a": [1, 2, 3], "b": [10, 20]]
someDictionary["a"]![0] = 100
// someDictionary is now [b: [10, 20], a: [100, 2, 3]]
~~~
> 强制取值(Forced Value)语法
> _强制取值(Forced Value)表达式_ → [_后置表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#postfix_expression) !
### 可选链表达式(Optional-Chaining Expression)
可选链表达式由目标表达式 + '?' 组成,形式如下:
> `expression`?
后缀'?' 返回目标表达式的值,把它做为可选的参数传递给后续的表达式
如果某个后缀表达式包含了可选链表达式,那么它的执行过程就比较特殊: 首先先判断该可选链表达式的值,如果是 nil, 整个后缀表达式都返回 nil, 如果该可选链的值不是nil, 则正常返回该后缀表达式的值(依次执行它的各个子表达式)。在这两种情况下,该后缀表达式仍然是一个optional type(In either case, the value of the postfix expression is still of an optional type)
如果某个"后缀表达式"的"子表达式"中包含了"可选链表达式",那么只有最外层的表达式返回的才是一个optional type. 例如,在下面的例子中, 如果c 不是nil, 那么 c?.property.performAction() 这句代码在执行时,就会先获得c 的property方法,然后调用 performAction()方法。 然后对于 "c?.property.performAction()" 这个整体,它的返回值是一个optional type.
~~~
var c: SomeClass?
var result: Bool? = c?.property.performAction()
~~~
如果不使用可选链表达式,那么 上面例子的代码跟下面例子等价:
~~~
if let unwrappedC = c {
result = unwrappedC.property.performAction()
}
~~~
后缀'?' 返回目标表达式的值可能会被修改,可能是由于出现了赋值,也有可能是出现异常而产生的修改。如果可选链表达式为`nil`,则表达式右边的复制操作不会被执行。比如:
~~~
func someFunctionWithSideEffects() -> Int {
return 42 // No actual side effects.
}
var someDictionary = ["a": [1, 2, 3], "b": [10, 20]]
someDictionary["not here"]?[0] = someFunctionWithSideEffects()
// someFunctionWithSideEffects is not evaluated
// someDictionary is still [b: [10, 20], a: [1, 2, 3]]
someDictionary["a"]?[0] = someFunctionWithSideEffects()
// someFunctionWithSideEffects is evaluated and returns 42
// someDictionary is now [b: [10, 20], a: [42, 2, 3]]
~~~
> 可选链表达式语法
> _可选链表达式_ → [_后置表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html#postfix_expression) ?
类型
最后更新于:2022-04-01 05:04:52
> 1.0 翻译:[lyuka](https://github.com/lyuka) 校对:[numbbbbb](https://github.com/numbbbbb), [stanzhai](https://github.com/stanzhai)
>
> 2.0 翻译+校对:[EudeMorgen](https://github.com/EudeMorgen)
本页包含内容:
[TOC]
Swift 语言存在两种类型:命名型类型和复合型类型。命名型类型是指定义时可以给定名字的类型。命名型类型包括类、结构体、枚举和协议。比如,一个用户定义的类MyClass的实例拥有类型MyClass。除了用户定义的命名型类型,Swift 标准库也定义了很多常用的命名型类型,包括那些表示数组、字典和可选值的类型。
那些通常被其它语言认为是基本或初级的数据型类型(Data types)——比如表示数字、字符和字符串的类型——实际上就是命名型类型,这些类型在Swift 标准库中是使用结构体来定义和实现的。因为它们是命名型类型,因此你可以按照“扩展和扩展声明”章节里讨论的那样,声明一个扩展来增加它们的行为以迎合你程序的需求。
_复合型类型_是没有名字的类型,它由 Swift 本身定义。Swift 存在两种复合型类型:函数类型和元组类型。一个复合型类型可以包含命名型类型和其它复合型类型。例如,元组类型(Int, (Int, Int))包含两个元素:第一个是命名型类型Int,第二个是另一个复合型类型(Int, Int).
本节讨论 Swift 语言本身定义的类型,并描述 Swift 中的类型推断行为。
> 类型语法
> _类型_ → [ _数组类型_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#array_type) | [ _字典类型_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#dictionary_type) | [ _函数类型_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#function_type) | [ _类型标识_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_identifier) | [ _元组类型_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#tuple_type) | [ _可选类型_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#optional_type) | [ _隐式解析可选类型_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#implicitly_unwrapped_optional_type) | [_协议合成类型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#protocol_composition_type)| [_元型类型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#metatype_type)
## 类型注解
类型注解显式地指定一个变量或表达式的值。类型注解始于冒号`:`终于类型,比如下面两个例子:
~~~
let someTuple: (Double, Double) = (3.14159, 2.71828)
func someFunction(a: Int){ /* ... */ }
~~~
在第一个例子中,表达式`someTuple`的类型被指定为`(Double, Double)`。在第二个例子中,函数`someFunction`的参数`a`的类型被指定为`Int`。
类型注解可以在类型之前包含一个类型特性(type attributes)的可选列表。
> 类型注解语法
> _类型注解_ → : [_特性(Attributes)列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/06_Attributes.html#attributes) _可选_ [_类型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)
## 类型标识符
类型标识符引用命名型类型或者是命名型/复合型类型的别名。
大多数情况下,类型标识符引用的是与之同名的命名型类型。例如类型标识符`Int`引用命名型类型`Int`,同样,类型标识符`Dictionary<String, Int>`引用命名型类型`Dictionary<String, Int>`。
在两种情况下类型标识符不引用同名的类型。情况一,类型标识符引用的是命名型/复合型类型的类型别名。比如,在下面的例子中,类型标识符使用`Point`来引用元组`(Int, Int)`:
~~~
typealias Point = (Int, Int)
let origin: Point = (0, 0)
~~~
情况二,类型标识符使用dot(`.`)语法来表示在其它模块(modules)或其它类型嵌套内声明的命名型类型。例如,下面例子中的类型标识符引用在`ExampleModule`模块中声明的命名型类型`MyType`:
~~~
var someValue: ExampleModule.MyType
~~~
> 类型标识语法
> _类型标识_ → [_类型名称_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_name) [_泛型参数子句_](http://wiki.jikexueyuan.com/project/swift/chapter3/GenericParametersAndArguments.html#generic_argument_clause) _可选_ | [_类型名称_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_name) [_泛型参数子句_](http://wiki.jikexueyuan.com/project/swift/chapter3/GenericParametersAndArguments.html#generic_argument_clause) _可选_ . [_类型标识_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_identifier)
> _类名_ → [_标识符_](http://wiki.jikexueyuan.com/project/swift/chapter3/LexicalStructure.html#identifier)
## 元组类型
元组类型使用逗号隔开并使用括号括起来的0个或多个类型组成的列表。
你可以使用元组类型作为一个函数的返回类型,这样就可以使函数返回多个值。你也可以命名元组类型中的元素,然后用这些名字来引用每个元素的值。元素的名字由一个标识符紧跟一个冒号`(:)`组成。“函数和多返回值”章节里有一个展示上述特性的例子。
`void`是空元组类型`()`的别名。如果括号内只有一个元素,那么该类型就是括号内元素的类型。比如,`(Int)`的类型是`Int`而不是`(Int)`。所以,只有当元组类型包含的元素个数在两个及以上时才可以命名元组元素。
> 元组类型语法
> _元组类型_ → ( [_元组类型主体_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#tuple_type_body) _可选_ )
> _元组类型主体_ → [_元组类型的元素列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#tuple_type_element_list) ... _可选_
> _元组类型的元素列表_ → [_元组类型的元素_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#tuple_type_element) | [_元组类型的元素_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#tuple_type_element) , [_元组类型的元素列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#tuple_type_element_list)
> _元组类型的元素_ → [_特性(Attributes)列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/06_Attributes.html#attributes) _可选_ inout _可选_ [_类型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type) | inout _可选_ [_元素名_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#element_name) [_类型注解_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_annotation)
> _元素名_ → [_标识符_](http://wiki.jikexueyuan.com/project/swift/chapter3/LexicalStructure.html#identifier)
## 函数类型
函数类型表示一个函数、方法或闭包的类型,它由一个参数类型和返回值类型组成,中间用箭头`->`隔开:
~~~
`parameter type` -> `return type`
~~~
由于 _参数类型_ 和 _返回值类型_ 可以是元组类型,所以函数类型支持多参数与多返回值的函数与方法。。
对于参数类型是空元组类型`()`以及返回值类型为表达式类型的函数类型,你可以对其参数声明使用`autoclosure`(见声明属性章节)。一个自动闭包函数捕获特定表达式上的隐式闭包而非表达式本身。下面的例子使用`autoclosure`属性来定义一个很简单的assert函数:
~~~
func simpleAssert(@autoclosure condition: Void -> Bool, _ message: String) {
if !condition() {
print(message)
}
}
let testNumber = 5
simpleAssert(testNumber % 2 == 0, "testNumber isn't an even number.")
// prints "testNumber isn't an even number."
~~~
函数类型可以拥有一个可变长参数作为参数类型中的最后一个参数。从语法角度上讲,可变长参数由一个基础类型名字紧随三个点`(...)`组成,如`Int...`。可变长参数被认为是一个包含了基础类型元素的数组。即`Int...`就是`[Int]`。关于使用可变长参数的例子,见章节Variadic Parameters。
为了指定一个`in-out`参数,可以在参数类型前加`inout`前缀。但是你不可以对可变长参数或返回值类型使用`inout`。关于In-Out参数的讨论见章节In-Out参数部分。
柯里化函数(Curried fuction)的函数类型从右向左递归地组成一组。例如,函数类型`Int -> Int -> Int`可以被理解为`Int -> (Int -> Int)`——也就是说,一个函数的参数为`Int`类型,其返回类型是一个参数类型为`Int`返回类型为`Int`的函数类型。关于柯里化函数的讨论见章节Curried Fuctions。
函数类型若要抛出错误就必须使用`throws`关键字来标记,若要重抛错误则必须使用`rethrows`关键字来标记。`throws`关键字是函数类型的一部分,不抛出函数(nonthrowing function)是抛出函数(throwing function)函数的一个子类型。因此,在使用抛出函数的地方也可以使用不抛出函数。对于柯里化函数,`throws`关键字只应用于最里层的函数。抛出和重抛函数(rethrowing function)的相关描述见章节抛出函数与方法和重抛函数与方法。
> 函数类型语法
> _函数类型_ → [_类型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type) _抛出_ _可选_ -> [_类型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)
> _函数类型_ → [_类型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)_重抛_ -> [_类型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)
## 数组类型
Swift语言中使用[`type`]来简化标准库中定义`Array<T>`类型的操作。 换句话说,下面两个声明是等价的:
~~~
let someArray: [String] = ["Alex", "Brian", "Dave"]
let someArray: Array<String> = ["Alex", "Brian", "Dave"]
~~~
上面两种情况下,常量`someArray`都被声明为字符串数组。数组的元素也可以通过`[]`获取访问:`someArray[0]`是指第0个元素`“Alex”`。
你也可以嵌套多对方括号来创建多维数组,最里面的方括号中指明数组元素的基本类型。比如,下面例子中使用三对方括号创建三维整数数组。
~~~
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`类型的细节讨论,见章节Arrays。
> 数组类型语法
> _数组类型_ → [_类型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type)
## 字典类型
Swift语言中使用[`key type: value type`]来简化标准库中定义`Dictionary<Key,Value>`类型的操作。 换句话说,下面两个声明是等价的:
~~~
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标准库中的可哈希协议。
关于Swift标准库中`Dictionary`类型的更多细节可查看章节Dictionaries。
> 字典类型语法
> _字典类型_ → [[_类型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type) : [_类型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type) ]
## 可选类型
Swift定义后缀`?`来作为标准库中的定义的命名型类型`Optional<T>`的简写。换句话说,下面两个声明是等价的:
~~~
var optionalInteger: Int?
var optionalInteger: Optional<Int>
~~~
在上述两种情况下,变量`optionalInteger`都被声明为可选整型类型。注意在类型和`?`之间没有空格。
类型`Optional<T>`是一个枚举,有两种形式,`None`和`Some(T)`,又来代表可能出现或可能不出现的值。任意类型都可以被显式的声明(或隐式的转换)为可选类型。当声明一个可选类型时,确保使用括号给`?`提供合适的作用范围。比如说,声明一个整型的可选数组,应写作`(Int[])?`,写成`Int[]?`的话则会出错。
如果你在声明或定义可选变量或特性的时候没有提供初始值,它的值则会自动赋成缺省值`nil`。
如果一个可选类型的实例包含一个值,那么你就可以使用后缀操作符`!`来获取该值,正如下面描述的:
~~~
optionalInteger = 42
optionalInteger! // 42
~~~
使用`!`操作符获取值为`nil`的可选项会导致运行错误(runtime error)。
你也可以使用可选链和可选绑定来选择性的执行可选表达式上的操作。如果值为`nil`,不会执行任何操作因此也就没有运行错误产生。
更多细节以及更多如何使用可选类型的例子,见章节Optionals。
> 可选类型语法
> _可选类型_ → [_类型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type) ?
## 隐式解析可选类型
Swift语言定义后缀`!`作为标准库中命名类型`ImplicitlyUnwrappedOptional<T>`的简写。换句话说,下面两个声明等价:
~~~
var implicitlyUnwrappedString: String!
var implicitlyUnwrappedString: ImplicitlyUnwrappedOptional<String>
~~~
上述两种情况下,变量`implicitlyUnwrappedString`被声明为一个隐式解析可选类型的字符串。注意类型与`!`之间没有空格。
你可以在使用可选类型的地方同样使用隐式解析可选类型。比如,你可以将隐式解析可选类型的值赋给变量、常量和可选特性,反之亦然。
有了可选,你在声明隐式解析可选变量或特性的时候就不用指定初始值,因为它有缺省值`nil`。
由于隐式解析可选的值会在使用时自动解析,所以没必要使用操作符`!`来解析它。也就是说,如果你使用值为`nil`的隐式解析可选,就会导致运行错误。
使用可选链会选择性的执行隐式解析可选表达式上的某一个操作。如果值为`nil`,就不会执行任何操作,因此也不会产生运行错误。
关于隐式解析可选的更多细节,见章节Implicitly Unwrapped Optionals。
> 隐式解析可选类型(Implicitly Unwrapped Optional Type)语法
> _隐式解析可选类型_ → [_类型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type) !
## 协议合成类型
协议合成类型是一种遵循具体协议列表中每个协议的类型。协议合成类型可能会用在类型注解和泛型参数中。
协议合成类型的形式如下:
~~~
protocol<Protocol 1, Procotol 2>
~~~
协议合成类型允许你指定一个值,其类型遵循多个协议的条件且不需要定义一个新的命名型协议来继承其它想要遵循的各个协议。比如,协议合成类型`protocol<Protocol A, Protocol B, Protocol C>`等效于一个从`Protocol A`,`Protocol B`, `Protocol C`继承而来的新协议`Protocol D`,很显然这样做有效率的多,甚至不需引入一个新名字。
协议合成列表中的每项必须是协议名或协议合成类型的类型别名。如果列表为空,它就会指定一个空协议合成列表,这样每个类型都能遵循。
> 协议合成类型语法
> _协议合成类型_ → protocol [_协议标识符列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#protocol_identifier_list) _可选_ >
> _协议标识符列表_ → [_协议标识符_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#protocol_identifier) | [_协议标识符_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#protocol_identifier) , [_协议标识符列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#protocol_identifier_list)
> _协议标识符_ → [_类型标识_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_identifier)
## 元类型
元类型是指所有类型的类型,包括类、结构体、枚举和协议。
类、结构体或枚举类型的元类型是相应的类型名紧跟`.Type`。协议类型的元类型——并不是运行时遵循该协议的具体类型——是该协议名字紧跟`.Protocol`。比如,类`SomeClass`的元类型就是`SomeClass.Type`,协议`SomeProtocol`的元类型就是`SomeProtocal.Protocol`。
你可以使用后缀`self`表达式来获取类型。比如,`SomeClass.self`返回`SomeClass`本身,而不是`SomeClass`的一个实例。同样,`SomeProtocol.self`返回`SomeProtocol`本身,而不是运行时遵循`SomeProtocol`的某个类型的实例。还可以对类型的实例使用`dynamicType`表达式来获取该实例在运行阶段的类型,如下所示:
~~~
class SomeBaseClass {
class func printClassName() {
println("SomeBaseClass")
}
}
class SomeSubClass: SomeBaseClass {
override class func printClassName() {
println("SomeSubClass")
}
}
let someInstance: SomeBaseClass = SomeSubClass()
// someInstance is of type SomeBaseClass at compile time, but
// someInstance is of type SomeSubClass at runtime
someInstance.dynamicType.printClassName()
// prints "SomeSubClass
~~~
> 注意 不能创建元类型类的实例,因为不能保证其子类会提供初始化的代码。
>
> 元(Metatype)类型语法
> _元类型_ → [_类型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type) . Type | [_类型_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type) . Protocol
## 类型继承子句
类型继承子句被用来指定一个命名型类型继承的哪个类、遵循的哪些协议。类型继承子句也用来指定一个类需要遵循的协议。类型继承子句开始于冒号`:`,其后是类所需遵循的协议或者类型标识符列表或者两者均有。
类可以继承单个超类,遵循任意数量的协议。当定义一个类时,超类的名字必须出现在类型标识符列表首位,然后跟上该类需要遵循的任意数量的协议。如果一个类不是从其它类继承而来,那么列表可以以协议开头。关于类继承更多的讨论和例子,见章节Inheritance。
其它命名型类型可能只继承或遵循一个协议列表。协议类型可能继承于其它任意数量的协议。当一个协议类型继承于其它协议时,其它协议的条件集合会被整合在一起,然后其它从当前协议继承的任意类型必须遵循所有这些条件。正如在协议声明中所讨论的那样,可以把类的关键字放到类型继承子句中的首位,这样就可以用一个类的条件来标记一个协议声明。
枚举定义中的类型继承子句可以是一个协议列表,或是指定原始值的枚举——一个单独的指定原始值类型的命名型类型。使用类型继承子句来指定原始值类型的枚举定义的例子,见章节Raw Values。
> 类型继承子句语法
> _类型继承子句_ → : [_类需求_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#class_requirement) , [_类型继承列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_inheritance_list) _类型继承子句_ → : [_类需求_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#class_requirement) _类型继承子句_ → : [_类型继承列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_inheritance_list) _类型继承列表_ → [_类型标识_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_identifier) | [_类型标识_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_identifier) , [_类型继承列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/03_Types.html#type_inheritance_list) _类需求_ → 类
## 类型推断
Swift广泛的使用类型推断,从而允许你可以忽略代码中很多变量和表达式的类型或部分类型。比如,对于`var x: Int = 0`,你可以完全忽略类型而简写成`var x = 0`——编译器会正确的推断出`x`的类型`Int`。类似的,当完整的类型可以从上下文推断出来时,你也可以忽略类型的一部分。比如,如果你写了`let dict: Dictionary = ["A": 1]`,编译提也能推断出`dict`的类型是`Dictionary<String, Int>`。
在上面的两个例子中,类型信息从表达式树(expression tree)的叶子节点传向根节点。也就是说,`var x: Int = 0`中`x`的类型首先根据`0`的类型进行推断,然后将该类型信息传递到根节点(变量`x`)。
在Swift中,类型信息也可以反方向流动——从根节点传向叶子节点。在下面的例子中,常量`eFloat`上的显式类型注解(`:Float`)导致数字字面量`2.71828`的类型是`Float`而非`Double`。
~~~
let e = 2.71828 // The type of e is inferred to be Double.
let eFloat: Float = 2.71828 // The type of eFloat is Float.
~~~
Swift中的类型推断在单独的表达式或语句水平上进行。这意味着所有用于推断类型的信息必须可以从表达式或其某个子表达式的类型检查中获取。
词法结构
最后更新于:2022-04-01 05:04:50
> 1.0 翻译:[superkam](https://github.com/superkam) 校对:[numbbbbb](https://github.com/numbbbbb)
>
> 2.0 翻译+校对:[buginux]([KYawn](https://github.com/buginux)
本页包含内容:
[TOC=2]
Swift 的“词法结构( _lexical structure_ )”描述了能构成该语言中合法标记( _tokens_ )的字符序列。这些合法标记组成了语言中最底层的构建基块,并在之后的章节中用于描述语言的其他部分。
通常情况下,标记是在随后将介绍的语法约束下,由 Swift 源文件的输入文本中提取可能的最长子串生成。这种方法称为“最长匹配项( _longest match_ )”,或者“最大适合”( _maximal munch_ )。
## 空白与注释
空白( _whitespace_ )有两个用途:分隔源文件中的标记和帮助区分运算符属于前缀还是后缀(参见 [运算符](51325)),在其他情况下则会被忽略。以下的字符会被当作空白:空格( _space_ )(U+0020)、换行符( _line feed_ )(U+000A)、回车符( _carriage return_ )(U+000D)、水平制表符( _horizontal tab_ )(U+0009)、垂直制表符(_vertical tab_ )(U+000B)、换页符( _form feed_ )(U+000C)以及空( _null_ )(U+0000)。
注释( _comments_ )被编译器当作空白处理。单行注释由 `//` 开始直至遇到换行符( _line feed_ )(U+000A)或者回车符( _carriage return_ )(U+000D)。多行注释由 `/*` 开始,以 `*/` 结束。注释允许嵌套,但注释标记必须匹配。
## 标识符
标识符( _identifiers_ )可以由以下的字符开始:大写或小写的字母 `A` 到 `Z`、下划线 `_`、基本多文种平面( _Basic Multilingual Plane_ )中的 Unicode 非组合字符以及基本多文种平面以外的非专用区( _Private Use Area_ )字符。首字符之后,允许使用数字和 Unicode 字符组合。
使用保留字( _reserved word_ )作为标识符,需要在其前后增加反引号 \`。例如,`class` 不是合法的标识符,但可以使用 \`class\`。反引号不属于标识符的一部分,\`x\` 和 `x` 表示同一标识符。
闭包( _closure_ )中如果没有明确指定参数名称,参数将被隐式命名为 `$0`、`$1`、`$2`等等。 这些命名在闭包作用域范围内是合法的标识符。
> 标识符语法
> _标识符_ → [ _头部标识符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#identifier_head) [ _标识符字符组_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#identifier_characters)可选
> _标识符_ → [ _头部标识符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#identifier_head) [ _标识符字符组_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#identifier_characters)可选
> _标识符_ → [ _隐式参数名_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#implicit_parameter_name)
> _标识符列表_ → [_标识符_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#identifier) | [_标识符_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#identifier) , [_标识符列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#identifier_list)
>
> _头部标识符_ → 大写或小写字母 A - Z
> _头部标识符_ → U+00A8, U+00AA, U+00AD, U+00AF, U+00B2–U+00B5, or U+00B7–U+00BA
> _头部标识符_ → U+00BC–U+00BE, U+00C0–U+00D6, U+00D8–U+00F6, or U+00F8–U+00FF
> _头部标识符_ → U+0100–U+02FF, U+0370–U+167F, U+1681–U+180D, or U+180F–U+1DBF
> _头部标识符_ → U+1E00–U+1FFF
> _头部标识符_ → U+200B–U+200D, U+202A–U+202E, U+203F–U+2040, U+2054, or U+2060–U+206F
> _头部标识符_ → U+2070–U+20CF, U+2100–U+218F, U+2460–U+24FF, or U+2776–U+2793
> _头部标识符_ → U+2C00–U+2DFF or U+2E80–U+2FFF
> _头部标识符_ → U+3004–U+3007, U+3021–U+302F, U+3031–U+303F, or U+3040–U+D7FF
> _头部标识符_ → U+F900–U+FD3D, U+FD40–U+FDCF, U+FDF0–U+FE1F, or U+FE30–U+FE44
> _头部标识符_ → U+FE47–U+FFFD
> _头部标识符_ → U+10000–U+1FFFD, U+20000–U+2FFFD, U+30000–U+3FFFD, or U+40000–U+4FFFD
> _头部标识符_ → U+50000–U+5FFFD, U+60000–U+6FFFD, U+70000–U+7FFFD, or U+80000–U+8FFFD
> _头部标识符_ → U+90000–U+9FFFD, U+A0000–U+AFFFD, U+B0000–U+BFFFD, or U+C0000–U+CFFFD
> _头部标识符_ → U+D0000–U+DFFFD or U+E0000–U+EFFFD
> _标识符字符_ → 数值 0 - 9
> _标识符字符_ → U+0300–U+036F, U+1DC0–U+1DFF, U+20D0–U+20FF, or U+FE20–U+FE2F
> _标识符字符_ → [ _头部标识符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#identifier_head)
> _标识符字符组_ → [ _标识符字符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#identifier_character) [_标识符字符列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#identifier_characters)可选
>
> _隐式参数名_ → $ _十进制数字列表_
## 关键字和符号
下面这些被保留的关键字(_keywords_)不允许用作标识符,除非被反引号转义,具体描述请参考 [标识符](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#identifiers)。
* 用在声明中的关键字:_class_、_deinit_、_enum_、_extension_、_func_、_import_、_init_、_let_、_protocol_、_static_、_struct_、_subscript_、_typealias_、_var_
* 用在语句中的关键字:_break_、_case_、_continue_、_default_、_do_、_else_、_fallthrough_、_if_、_in_、_for_、_return_、_switch_、_where_、_while_
* 用在表达式和类型中的关键字:_as_、_dynamicType_、_is_、_new_、_super_、_self_、_Self_、_Type_、___COLUMN___、___FILE___、___FUNCTION___、___LINE___
* 用在模式中的关键字: _
* 特定上下文中被保留的关键字:_associativity_、_didSet_、_get_、_infix_、_inout_、_left_、_mutating_、_none_、_nonmutating_、_operator_、_override_、_postfix_、_precedence_、_prefix_、_right_、_set_、_unowned_、_unowned(safe)_、_unowned(unsafe)_、_weak_、_willSet_,这些关键字在特定上下文之外可以被用于标识符。
以下标记被当作保留符号,不能用于自定义操作符:`(`、`)`、`{`、`}`、`[`、`]`、`.`、`,`、`:`、`;`、`=`、`@`、`#`、`&(作为前缀操作符)`、`->`、`````、`?` 和 `!(作为后缀操作符)`。
## 字面量
字面量是用来表示源码中某种特定类型的值,比如一个数字或字符串。
下面是字面量的一些示例:
~~~
42 // 整型字面量
3.14159 // 浮点型字面量
"Hello, world!" // 字符串型字面量
true // 布尔型字面量
~~~
字面量本身并不包含类型信息。事实上,一个字面量会被解析为拥有无限的精度,然后 Swift 的类型推导会尝试去推导出这个字面量的类型。比如,在 `let x: Int8 = 42` 这个声明中,Swift 使用了显式类型标注(`: Int8`)来推导出 `42` 这个整型字面量的类型是 `Int8`。如果没有可用的类型信息, Swift 则会从标准库中定义的字面量类型中推导出一个默认的类型。整型字面量的默认类型是 `Int`,浮点型字面量的默认类型是 `Double`,字符串型字面量的默认类型是 `String`,布尔型字面量的默认类型是 `Bool`。比如,在 `let str = "Hello, world"` 这个声明中,字符串 `"Hello, world"`的默认推导类型就是 `String`。
当为一个字面量值指定了类型标注的时候,这个注解的类型必须能通过这个字面量值实例化后得到。也就是说,这个类型必须遵守这些 Swift 标准库协议中的一个:整型字面量的`IntegerLiteralConvertible`协议、符点型字面量的`FloatingPointLiteralConvertible`协议、字符串字面量的`StringLiteralConvertible`协议以及布尔型字面量的`BooleanLiteralConvertible`协议。比如,`Int8` 遵守了 `IntegerLiteralConvertible`协议,因此它能在 `let x: Int8 = 42` 这个声明中作为整型字面量 `42` 的类型标注。
> 字面量语法
> _字面量_ → [_数字型字面量_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#numeric_literal) | [_字符串型字面量_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#string_literal) | [_布尔型字面量_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#boolean_literal) | [_nil型字面量_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#nil_literal)
>
> _数字型字面量_ → -可选[_整型字面量_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#integer_literal) | -可选[_符点型字面量_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#floating_point_literal)
> _布尔型字面量_ → true | false
> _nil型字面量_ → nil
### 整型字面量
整型字面量(_integer literals_)表示未指定精度整型数的值。整型字面量默认用十进制表示,可以加前缀来指定其他的进制,二进制字面量加 `0b`,八进制字面量加 `0o`,十六进制字面量加 `0x`。
十进制字面量包含数字 `0` 至 `9`。二进制字面量只包含 `0` 或 `1`,八进制字面量包含数字 `0` 至 `7`,十六进制字面量包含数字 `0` 至 `9` 以及字母 `A` 至 `F` (大小写均可)。
负整数的字面量在整型字面量前加减号 `-`,比如 `-42`。
整型字面面可以使用下划线 `_` 来增加数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。
除非特别指定,整型字面量的默认推导类型为 Swift 标准库类型中的 `Int`。Swift 标准库还定义了其他不同长度以及是否带符号的整数类型,请参考 [整数类型](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html#//apple_ref/doc/uid/TP40014097-CH5-ID323)。
> 整型字面量语法
> _整型字面量_ → [_二进制字面量_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#binary_literal)
> _整型字面量_ → [_八进制字面量_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#octal_literal)
> _整型字面量_ → [_十进制字面量_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#decimal_literal)
> _整型字面量_ → [_十六进制字面量_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#hexadecimal_literal)
> _二进制字面量_ → 0b [_二进制数字_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#binary_digit) [_二进制字面量字符组_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#binary_literal_characters)可选
> _二进制数字_ → 数值 0 到 1
> _二进制字面量字符_ → [_二进制数字_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#binary_digit) | _
> _二进制字面量字符组_ → [_二进制字面量字符_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#binary_literal_character) [_二进制字面量字符组_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#binary_literal_characters)可选
>
> _八进制字面量_ → 0o [_八进字数字_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#octal_digit) [_八进制字符列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#octal_literal_characters)可选
> _八进字数字_ → 数值 0 到 7
> _八进制字符_ → [_八进字数字_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#octal_digit) | _
> _八进制字符组_ → [_八进制字符_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#octal_literal_character) [_八进制字符列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#octal_literal_characters)可选
> _十进制字面量_ → [_十进制数字_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#decimal_digit) [_十进制字符组_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#decimal_literal_characters)可选
> _十进制数字_ → 数值 0 到 9
> _十进制数字列表_ → [_十进制数字_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#decimal_digit) [_十进制数字列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#decimal_digits)可选
> _十进制字符_ → [_十进制数字_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#decimal_digit) | _
> _十进制字符列表_ → [_十进制字符_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#decimal_literal_character) [_十进制字符列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#decimal_literal_characters)可选
> _十六进制字面量_ → 0x [_十六进制数字_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#hexadecimal_digit) [_十六进制字面量字符列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#hexadecimal_literal_characters)可选
> _十六进制数字_ → 数值 0 到 9, 字母 a 到 f, 或 A 到 F
> _十六进制字符_ → [_十六进制数字_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#hexadecimal_digit) | _
> _十六进制字面量字符列表_ → [_十六进制字符_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#hexadecimal_literal_character) [_十六进制字面量字符列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#hexadecimal_literal_characters)可选
### 浮点型字面量
浮点型字面量(_floating-point literals_)表示未指定精度浮点数的值。
浮点型字面量默认用十进制表示(无前缀),也可以用十六进制表示(加前缀 `0x`)。
十进制浮点型字面量(_decimal floating-point literals_)由十进制数字串后跟小数部分或指数部分(或两者皆有)组成。十进制小数部分由小数点 `.` 后跟十进制数字串组成。指数部分由大写或小写字母 `e` 为前缀后跟十进制数字串组成,这串数字表示 `e` 之前的数量乘以 10 的几次方。例如:`1.25e2` 表示 `1.25 ⨉ 10^2`,也就是 `125.0`;同样,`1.25e-2`表示 `1.25 ⨉ 10^-2`,也就是 `0.0125`。
十六进制浮点型字面量(_hexadecimal floating-point literals_)由前缀 `0x` 后跟可选的十六进制小数部分以及十六进制指数部分组成。十六进制小数部分由小数点后跟十六进制数字串组成。指数部分由大写或小写字母 `p` 为前缀后跟十进制数字串组成,这串数字表示 `p` 之前的数量乘以 2 的几次方。例如:`0xFp2` 表示 `15 ⨉ 2^2`,也就是 `60`;同样,`0xFp-2` 表示 `15 ⨉ 2^-2`,也就是 `3.75`。
负的浮点型字面量由一元运算符减号 `-` 和浮点型字面量组成,例如 `-42.5`。
浮点型字面量允许使用下划线 `_` 来增强数字的可读性,下划线会被系统忽略,因此不会影响字面量的值。同样地,也可以在数字前加 `0`,并不会影响字面量的值。
除非特别指定,浮点型字面量的默认推导类型为 Swift 标准库类型中的 `Double`,表示64位浮点数。Swift 标准库也定义了 `Float` 类型,表示32位浮点数。
> 浮点型字面量语法
> _浮点数字面量_ → [_十进制字面量_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#decimal_literal) [_十进制分数_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#decimal_fraction)可选 [_十进制指数_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#decimal_exponent)可选
> _浮点数字面量_ → [_十六进制字面量_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#hexadecimal_literal) [_十六进制分数_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#hexadecimal_fraction)可选 [_十六进制指数_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#hexadecimal_exponent)
> _十进制分数_ → . [_十进制字面量_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#decimal_literal)
> _十进制指数_ → [_浮点数e_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#floating_point_e) [_正负号_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#sign)可选 [_十进制字面量_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#decimal_literal)
> _十六进制分数_ → . [_十六进制数字_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#hexadecimal_digit) [_十六进制字面量字符列表_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#hexadecimal_literal_characters)可选
> _十六进制指数_ → [_浮点数p_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#floating_point_p) [_正负号_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#sign)可选 [_十进制字面量_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#decimal_literal)
> _浮点数e_ → e | E
> _浮点数p_ → p | P
> _正负号_ → + | -
### 字符串型字面量
字符串型字面量( _string literal_ )由被包在双引号中的一串字符组成,形式如下:
~~~
"characters"
~~~
字符串型字面量中不能包含未转义的双引号 (`"`)、未转义的反斜线(`\`)、回车符( _carriage return_ )或换行符( _line feed_ )。
可以在字符串字面量中使用的转义特殊符号如下:
* 空字符(Null Character)`\0`
* 反斜线(Backslash)`\\`
* 水平制表符(Horizontal Tab)`\t`
* 换行符(Line Feed)`\n`
* 回车符(Carriage Return)`\r`
* 双引号(Double Quote)`\"`
* 单引号(Single Quote)`\'`
* Unicode标量 `\u{n}`,n为一到八位的十六进制数字
字符串字面量允许在反斜杠小括号 `\()` 中插入表达式的值。插入表达式( _interpolated expression_ )不能包含未转义的双引号 `"`、未转义的反斜线 `\`、回车符或者换行符。表达式结果的类型必须在 _String_ 类中有对应的初始化方法。
例如,以下所有字符串字面量的值都是相同的:
~~~
"1 2 3"
"1 2 \(3)"
"1 2 \(1 + 2)"
let x = 3; "1 2 \(x)"
~~~
字符串字面量的默认推导类型为 `String`。组成字符串的字符默认推导类型为 `Character`。更多有关 `String` 和`Character` 的信息请参照 [字符串和字符](http://wiki.jikexueyuan.com/project/swift/chapter2/03_Strings_and_Characters.html)。
> 字符型字面量语法
> _字符串字面量_ → " [_引用文本_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#quoted_text)可选 "
> _引用文本_ → [_引用文本条目_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#quoted_text_item) [_引用文本_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#quoted_text) 可选
> _引用文本条目_ → [_转义字符_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#escaped_character)
> _引用文本条目_ → ( [_表达式_](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html) )
> _引用文本条目_ → 除了", \, U+000A, 或者 U+000D的所有Unicode的字符
> _转义字符_ → \0 | \ | \t | \n | \r | \" | \'
> _转义字符_ → \u { [_unicode标量数字_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#unicode_scalar_digits) } _unicode标量数字_ → 一到八位的十六进制数字
## 运算符
Swift 标准库定义了许多可供使用的运算符,其中大部分在 [基础运算符](51325) 和 [高级运算符](51363) 中进行了阐述。这一小节将描述哪些字符能用于自定义运算符。
自定义运算符可以由以下其中之一的 ASCII 字符 `/`、`=`、 `-`、`+`、`!`、`*`、`%`、`<`、`>`、`&`、`|`、`^`、`?` 以及 `~`, 或者后面语法中规定的任一个 Unicode 字符开始。在第一个字符之后,允许使用组合型 Unicode 字符。也可以使用两个或者多个的点号来自定义运算符(比如, `....`)。虽然可以自定义包含问号`?`的运算符,但是这个运算符不能只包含单独的一个问号。
~~~
注意:
以下这些标记 =, ->, //, /*, */, ., <(前缀运算符), &, and ?, ?(中缀运算符), >(后缀运算符), ! 以及 ? 是被系统保留的。这些标记不能被重载,也不能用于自定义操作符。
~~~
运算符两侧的空白被用来区分该运算符是否为前缀运算符( _prefix operator_ )、后缀运算符( _postfix operator_ )或二元运算符( _binary operator_ )。规则总结如下:
* 如果运算符两侧都有空白或两侧都无空白,将被看作二元运算符。例如:`a+b` 和 `a + b` 中的运算符 `+` 被看作二元运算符。
* 如果运算符只有左侧空白,将被看作前缀一元运算符。例如 `a ++b` 中的 `++` 被看作前缀一元运算符。
* 如果运算符只有右侧空白,将被看作后缀一元运算符。例如 `a++ b` 中的 `++` 被看作后缀一元运算符。
* 如果运算符左侧没有空白并紧跟 `.`,将被看作后缀一元运算符。例如 `a++.b` 中的 `++` 被看作后缀一元运算符(即上式被视为 `a++ .b` 而非 `a ++ .b`)。
鉴于这些规则,运算符前的字符 `(`、`[` 和 `{` ;运算符后的字符 `)`、`]` 和 `}` 以及字符 `,`、`;` 和 `:` 都被视为空白。
以上规则需注意一点,如果预定义运算符 `!` 或 `?` 左侧没有空白,则不管右侧是否有空白都将被看作后缀运算符。如果将`?` 用作可选链(_optional-chaining_)操作符,左侧必须无空白。如果用于条件运算符 `? :`,必须两侧都有空白。
在某些特定的构造中 ,以 `<` 或 `>` 开头的运算符会被分离成两个或多个标记,剩余部分以同样的方式会被再次分离。因此,在 `Dictionary<String, Array<Int>>` 中没有必要添加空白来消除闭合字符 `>` 的歧义。在这个例子中, 闭合字符`>` 不会被视为单独的标记,因而不会被误解析为 `>>` 运算符的一部分。
要学习如何自定义运算符,请参考 [自定义操作符](http://wiki.jikexueyuan.com/project/swift/chapter2/25_Advanced_Operators.html#custom_operators) 和 [运算符声明](http://wiki.jikexueyuan.com/project/swift/chapter3/05_Declarations.html#operator_declaration)。要学习如何重载运算符,请参考 [运算符方法](http://wiki.jikexueyuan.com/project/swift/chapter2/25_Advanced_Operators.html#operator_functions)。
> 运算符语法语法
> _运算符_ → [_头部运算符_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#operator_head) [_运算符字符组_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#operator_characters)可选
> _运算符_ → [_头部点运算符_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#dot_operator_head) [_点运算符字符组_](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#dot_operator_characters)可选
>
> _头部运算符_ → / | = | + | ! |* | % | | > |& | | |/ | ~ | ? | _头部运算符_ → U+00A1–U+00A7
> _头部运算符_ → U+00A9 or U+00AB
> _头部运算符_ → U+00AC or U+00AE
> _头部运算符_ → U+00B0–U+00B1, U+00B6, U+00BB, U+00BF, U+00D7, or U+00F7
> _头部运算符_ → U+2016–U+2017 or U+2020–U+2027
> _头部运算符_ → U+2030–U+203E
> _头部运算符_ → U+2041–U+2053
> _头部运算符_ → U+2055–U+205E
> _头部运算符_ → U+2190–U+23FF
> _头部运算符_ → U+2500–U+2775
> _头部运算符_ → U+2794–U+2BFF
> _头部运算符_ → U+2E00–U+2E7F
> _头部运算符_ → U+3001–U+3003
> _头部运算符_ → U+3008–U+3030
> _运算符字符_ → [ _头部运算符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#operator_head)
> _运算符字符_ → U+0300–U+036F
> _运算符字符_ → U+1DC0–U+1DFF
> _运算符字符_ → U+20D0–U+20FF
> _运算符字符_ → U+FE00–U+FE0F
> _运算符字符_ → U+FE20–U+FE2F
> _运算符字符_ → U+E0100–U+E01EF
>
> _运算符字符组_ → [ _运算符字符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#operator_character) _运算符字符组_ 可选
>
> _头部点运算符_ → ..
> _头部点运算符字符_ → . | [ _运算符字符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#operator_character)
> _头部点运算符字符组_ → [ _点运算符字符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#dot_operator_character) [ _点运算符字符组_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#dot_operator_characters)可选
>
> _二元运算符_ → [ _运算符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#operator)
> _前置运算符_ → [ _运算符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#operator)
> _后置运算符_ → [ _运算符_ ](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#operator)
关于语言参考
最后更新于:2022-04-01 05:04:47
> 1.0 翻译:[dabing1022](https://github.com/dabing1022) 校对:[numbbbbb](https://github.com/numbbbbb)
>
> 2.0 翻译+校对:[KYawn](https://github.com/KYawn)
本页内容包括:
[TOC]
本书的这一节描述了Swift编程语言的形式语法。这里描述的语法是为了帮助您更详细的了解该语言,而不是让您直接实现一个解析器或编译器。
Swift语言相对小一点,这是由于在Swift代码中几乎所有常见的类型、函数以及运算符都已经在Swift标准库中定义了。虽然这些类型、函数和运算符并不是Swift语言自身的一部分,但是它们被广泛应用于本书的讨论和代码范例中。
## 如何阅读语法
用来描述Swift编程语言形式语法的标记遵循下面几个约定:
* 箭头(→)用来标记语法产式,可以理解为“可以包含”。
* *斜体*文字用来表示句法分类,并出现在一个语法产式规则两侧。
* 义词和标点符号由粗体固定宽度的文本标记,而且只出现在一个语法产式规则的右侧。
* 选择性的语法产式由竖线(|)分隔。当可选用的语法产式太多时,为了阅读方便,它们将被拆分为多行语法产式规则。
* 少数情况下,常规字体文字用来描述语法产式规则的右边。
* 可选的句法分类和文字用尾标`opt`来标记。
举个例子,getter-setter的语法块的定义如下:
> GRAMMAR OF A GETTER-SETTER BLOCK
> _getter-setter-block_ → { [*getter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause) [*setter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause) *opt* } | { [*setter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause) [*getter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause)}
这个定义表明,一个getter-setter方法块可以由一个getter子句后跟一个可选的setter子句构成,然后用大括号括起来,或者由一个setter子句后跟一个getter子句构成,然后用大括号括起来。下面的两个语法产式等价于上述的语法产式,并明确指出了如何取舍:
> GRAMMAR OF A GETTER-SETTER BLOCK
> getter-setter-block → { [*getter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause) [*setter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause) *opt*}
> getter-setter-block → { [*setter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/setter-clause) [*getter-clause*](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/swift/grammar/getter-clause) }
语言参考
最后更新于:2022-04-01 05:04:44
高级操作符
最后更新于:2022-04-01 05:04:42
> 1.0 翻译:[xielingwang](https://github.com/xielingwang) 校对:[numbbbbb](https://github.com/numbbbbb)
>
> 2.0 翻译+校对:[buginux](https://github.com/buginux)
本页内容包括:
[TOC=2]
除了在之前介绍过的基本运算符,Swift 中还有许多可以对数值进行复杂操作的高级运算符。这些高级运算符包含了在 C 和 Objective-C 中已经被大家所熟知的位运算符和移位运算符。
与C语言中的算术运算符不同,Swift 中的算术运算符默认是不会溢出的。所有溢出行为都会被捕获并报告为错误。如果想让系统允许溢出行为,可以选择使用 Swift 中另一套默认支持溢出的运算符,比如溢出加法运算符(`&+`)。所有的这些溢出运算符都是以 `&` 开头的。
在定义自有的结构体、类和枚举时,最好也同时为它们提供标准swift运算符的实现。Swift简化了运算符的自定义实现,也使判断不同类型所对应的行为更为简单。
我们不用被预定义的运算符所限制。在 Swift 当中可以自由地定义中缀、前缀、后缀和赋值运算符,以及相应的优先级与结合性。这些运算符在代码中可以像预设的运算符一样使用,我们甚至可以扩展已有的类型以支持自定义的运算符。
## 位运算符
位运算符(`Bitwise operators`)可以操作一个数据结构中每个独立的位。它们通常被用在底层开发中,比如图形编程和创建设备驱动。位运算符在处理外部资源的原始数据时也十分有用,比如对自定义通信协议传输的数据进行编码和解码。
Swift 支持C语言中的全部位运算符,具体如下:
### 按位取反运算符(`bitwise NOT operator`)
按位取反运算符(`~`) 可以对一个数值的全部位进行取反:
![Art/bitwiseNOT_2x.png](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-08-12_55caba3e46b06.png)
按位取反操作符是一个前置运算符,需要直接放在操作数的之前,并且它们之间不能添加任何空格。
~~~
let initialBits: UInt8 = 0b00001111
let invertedBits = ~initialBits // 等于 0b11110000
~~~
`UInt8` 类型的整数有 8 个比特位,可以存储 0 ~ 255之间的任意整数。这个例子初始化了一个 `UInt8` 类型的整数,并赋值为二进制的 `00001111`,它的前 4 位都为`0`,后 4 位都为`1`。这个值等价于十进制的 `15` 。
接着使用按位取反运算符创建了一个名为 `invertedBits` 的常量,这个常量的值与全部位取反后的 `initialBits` 相等。即所有的 `0` 都变成了 `1`,同时所有的 `1` 都变成 `0`。`invertedBits` 的二进制值为 `11110000`,等价于无符号十进制数的 `240`。
### 按位与运算符(Bitwise AND Operator)
按位与运算符(`&`)可以对两个数的比特位进行合并。它返回一个新的数,只有当两个操作数的对应位_都_为 `1` 的时候,该数的对应位才为 `1`。
![Art/bitwiseAND_2x.png](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-08-12_55caba419ad64.png "Art/bitwiseAND_2x.png")
在下面的示例当中,`firstSixBits` 和 `lastSixBits` 中间 4 个位的值都为 1 。按位与运算符对它们进行了运算,得到二进制数值 `00111100`,等价于无符号十进制数的 `60`:
~~~
let firstSixBits: UInt8 = 0b11111100
let lastSixBits: UInt8 = 0b00111111
let middleFourBits = firstSixBits & lastSixBits // 等于 00111100
~~~
### 按位或运算符(Bitwise OR Operator)
按位或运算符(`|`)可以对两个数的比特位进行比较。它返回一个新的数,只要两个操作数的对应位中有_任意_一个为 `1`时,该数的对应位就为 `1`。
![Art/bitwiseOR_2x.png](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-08-12_55caba4908166.png "Art/bitwiseOR_2x.png")
在下面的示例当中,`someBits` 和 `moreBits` 将不同的位设置为 `1`。接位或运算符对它们进行了运算,得到二进制数值`11111110`,等价于无符号十进制数的 `254`:
~~~
let someBits: UInt8 = 0b10110010
let moreBits: UInt8 = 0b01011110
let combinedbits = someBits | moreBits // 等于 11111110
~~~
### 按位异或运算符(Bitwise XOR Opoerator)
按位异或运算符(`^`)可以对两个数的比特位进行比较。它返回一个新的数,当两个操作数的对应位不相同时,该数的对应位就为 `1`:
![Art/bitwiseXOR_2x.png](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-08-12_55caba4f0e6e1.png "Art/bitwiseXOR_2x.png")
在下面的示例当中,`firstBits` 和 `otherBits` 都有一个自己设置为 `1` 而对方设置为 `0` 的位。 按位异或运算符将这两个位都设置为 `1`,同时将其它位都设置为 `0`:
~~~
let firstBits: UInt8 = 0b00010100
let otherBits: UInt8 = 0b00000101
let outputBits = firstBits ^ otherBits // 等于 00010001
~~~
### 按位左移/右移运算符
按位左移运算符(`<<`)和按位右移运算符(`>>`)可以对一个数进行指定位数的左移和右移,但是需要遵守下面定义的规则。
对一个数进行按位左移或按位右移,相当于对这个数进行乘以 2 或除以 2 的运算。将一个整数左移一位,等价于将这个数乘以 2,同样地,将一个整数右移一位,等价于将这个数除以 2。
#### 无符号整型的移位操作
对无符号整型进行移位的规则如下:
1. 已经存在的比特位按指定的位数进行左移和右移。
2. 任何移动超出整型存储边界的位都会被丢弃。
3. 用 0 来填充移动后产生的空白位。
这种方法称为逻辑移位(`logical shift`)。
以下这张图展示了 `11111111 << 1`(即把 `11111111` 向左移动 1 位),和 `11111111 >> 1`(即把 `11111111` 向右移动 1 位) 的结果。蓝色的部分是被移位的,灰色的部分是被抛弃的,橙色的部分则是被填充进来的。
![Art/bitshiftUnsigned_2x.png](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-08-12_55caba556eca0.png "Art/bitshiftUnsigned_2x.png")
下面的代码演示了 Swift 中的移位操作:
~~~
let shiftBits: UInt8 = 4 // 即二进制的00000100
shiftBits << 1 // 00001000
shiftBits << 2 // 00010000
shiftBits << 5 // 10000000
shiftBits << 6 // 00000000
shiftBits >> 2 // 00000001
~~~
可以使用移位操作对其他的数据类型进行编码和解码:
~~~
let pink: UInt32 = 0xCC6699
let redComponent = (pink & 0xFF0000) >> 16 // redComponent 是 0xCC, 即 204
let greenComponent = (pink & 0x00FF00) >> 8 // greenComponent 是 0x66, 即 102
let blueComponent = pink & 0x0000FF // blueComponent 是 0x99, 即 153
~~~
这个示例使用了一个命名为 `pink` 的 `UInt32` 型常量来存储层叠样式表(`CSS`)中粉色的颜色值。该 `CSS` 的十六进制颜色值 `#CC6699`, 在 Swift 中表示为 `0xCC6699`。然后利用按位与运算符(`&`)和按位右移运算符(`>>`)从这个颜色值中分解出红(`CC`)、绿(`66`)以及蓝(`99`)三个部分。
红色部分是通过对 `0xCC6699` 和 `0xFF0000` 进行按位与运算后得到的。`0xFF0000` 中的 `0` 部分作为_掩码_,掩盖了`OxCC6699` 中的第二和第三个字节,使得数值中的 `6699` 被忽略,只留下 `0xCC0000`。
然后,再将这个数按向右移动 16 位(`>> 16`)。十六进制中每两个字符表示 8 个比特位,所以移动 16 位后 `0xCC0000` 就变为 `0x0000CC`。这个数和`0xCC`是等同的,也就是十进制数值的 `204`。
同样的,绿色部分通过对 `0xCC6699` 和 `0x00FF00` 进行按位与运算得到 `0x006600`。然后将这个数向右移动 8 位,得到`0x66`,也就是十进制数值的 `102`。
最后,蓝色部分通过对 `0xCC6699` 和 `0x0000FF` 进行按位与运算得到 `0x000099`。并且不需要进行向右移位,所以结果为`0x99` ,也就是十进制数值的 `153`。
#### 有符号整型的移位操作
对比无符号整型来说,有符整型的移位操作相对复杂得多,这种复杂性源于有符号整数的二进制表现形式。(为了简单起见,以下的示例都是基于 8 位有符号整数的,但是其中的原理对任何位数的有符号整数都是通用的。)
有符号整数使用第 1 个比特位(通常被称为符号位)来表示这个数的正负。符号位为 `0` 代表正数,为 `1` 代表负数。
其余的比特位(通常被称为数值位)存储了这个数的真实值。有符号正整数和无符号数的存储方式是一样的,都是从 `0` 开始算起。这是值为 `4` 的 `Int8` 型整数的二进制位表现形式:
![Art/bitshiftSignedFour_2x.png](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-08-12_55caba65f3c22.png "Art/bitshiftSignedFour_2x.png")
符号位为 `0`,说明这是一个正数,另外 7 位则代表了十进制数值 `4` 的二进制表示。
负数的存储方式略有不同。它存储的是 `2` 的 n 次方减去它的真实值绝对值,这里的 n 为数值位的位数。一个 8 位的数有 7 个数值位,所以是 2 的 7 次方,即 128。
这是值为 `-4` 的 `Int8` 型整数的二进制位表现形式:
![Art/bitshiftSignedMinusFour_2x.png](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-08-12_55caba6b11d57.png "Art/bitshiftSignedMinusFour_2x.png")
这次的符号位为 `1`,说明这是一个负数,另外 7 个位则代表了数值 `124`(即 `128 - 4`) 的二进制表示。
![Art/bitshiftSignedMinusFourValue_2x.png](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-08-12_55caba6fba352.png "Art/bitshiftSignedMinusFourValue_2x.png")
负数的表示通常被称为二进制补码(`two's complement`)表示法。用这种方法来表示负数乍看起来有点奇怪,但它有几个优点。
首先,如果想对 `-1` 和 `-4` 进行加法操作,我们只需要将这两个数的全部 8 个比特位进行相加,并且将计算结果中超出 8 位的数值丢弃:
![Art/bitshiftSignedAddition_2x.png](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-08-12_55caba75a22a2.png "Art/bitshiftSignedAddition_2x.png")
其次,使用二进制补码可以使负数的按位左移和右移操作得到跟正数同样的效果,即每向左移一位就将自身的数值乘以 2,每向右一位就将自身的数值除以 2。要达到此目的,对有符号整数的右移有一个额外的规则:
* 当对正整数进行按位右移操作时,遵循与无符号整数相同的规则,但是对于移位产生的空白位使用_符号位_进行填充,而不是用 0。
![Art/bitshiftSigned_2x.png](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-08-12_55caba7a82002.png "Art/bitshiftSigned_2x.png")
这个行为可以确保有符号整数的符号位不会因为右移操作而改变,这通常被称为算术移位(`arithmetic shift`)。
由于正数和负数的特殊存储方式,在对它们进行右移的时候,会使它们越来越接近 0。在移位的过程中保持符号位不变,意味着负整数在接近 `0` 的过程中会一直保持为负。
## 溢出运算符
在默认情况下,当向一个整数赋超过它容量的值时,Swift 默认会报错,而不是生成一个无效的数。这个行为给我们操作过大或着过小的数的时候提供了额外的安全性。
例如,`Int16` 型整数能容纳的有符号整数范围是 `-32768` 到 `32767`,当为一个 `Int16` 型变量赋的值超过这个范围时,系统就会报错:
~~~
var potentialOverflow = Int16.max
// potentialOverflow 的值是 32767, 这是 Int16 能容纳的最大整数
potentialOverflow += 1
// 这里会报错
~~~
为过大或者过小的数值提供错误处理,能让我们在处理边界值时更加灵活。
然而,也可以选择让系统在数值溢出的时候采取截断操作,而非报错。可以使用 Swift 提供的三个溢出操作符(`overflow operators`)来让系统支持整数溢出运算。这些操作符都是以 `&` 开头的:
* 溢出加法 `&+`
* 溢出减法 `&-`
* 溢出乘法 `&*`
### 数值溢出
数值有可能出现上溢或者下溢。
这个示例演示了当我们对一个无符号整数使用溢出加法(`&+`)进行上溢运算时会发生什么:
~~~
var unsignedOverflow = UInt8.max
// unsignedOverflow 等于 UInt8 所能容纳的最大整数 255
unsignedOverflow = unsignedOverflow &+ 1
// 此时 unsignedOverflow 等于 0
~~~
`unsignedOverflow` 被初始化为 `UInt8` 所能容纳的最大整数(`255`,以二进制表示即 `11111111`)。然后使用了溢出加法运算符(`&+`)对其进行加 1 操作。这使得它的二进制表示正好超出 `UInt8` 所能容纳的位数,也就导致了数值的溢出,如下图所示。数值溢出后,留在 `UInt8` 边界内的值是 `00000000`,也就是十进制数值的 0。
![Art/overflowAddition_2x.png](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-08-12_55caba7c4af34.png "Art/overflowAddition_2x.png")
同样地,当我们对一个无符号整数使用溢出减法(`&-`)进行下溢运算时也会产生类似的现象:
~~~
var unsignedOverflow = UInt8.min
// unsignedOverflow 等于 UInt8 所能容纳的最小整数 0
unsignedOverflow = unsignedOverflow &- 1
// 此时 unsignedOverflow 等于 255
~~~
`UInt8` 型整数能容纳的最小值是 0,以二进制表示即 `00000000`。当使用溢出减法运算符对其进行减 1 操作时,数值会产生下溢并被截断为 `11111111`, 也就是十进制数值的 255。
![Art/overflowUnsignedSubtraction_2x.png](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-08-12_55caba898f19d.png "Art/overflowAddition_2x.png")
溢出也会发生在有符号整型数值上。在对有符号整型数值进行溢出加法或溢出减法运算时,符号位也需要参与计算,正如[按位左移/右移运算符](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-ID34)所描述的。
~~~
var signedOverflow = Int8.min
// signedOverflow 等于 Int8 所能容纳的最小整数 -128
signedOverflow = signedOverflow &- 1
// 此时 signedOverflow 等于 127
~~~
`Int8` 型整数能容纳的最小值是 -128,以二进制表示即 `10000000`。当使用溢出减法操作符对其进行减 1 操作时,符号位被翻转,得到二进制数值 `01111111`,也就是十进制数值的 `127`,这个值也是 `Int8` 型整数所能容纳的最大值。
![Art/overflowSignedSubtraction_2x.png](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-08-12_55caba9589bf6.png "Art/overflowSignedSubtraction_2x.png")
对于无符号与有符号整型数值来说,当出现上溢时,它们会从数值所能容纳的最大数变成最小的数。同样地,当发生下溢时,它们会从所能容纳的最小数变成最大的数。
## 优先级和结合性
运算符的优先级(`precedence`)使得一些运算符优先于其他运算符,高优先级的运算符会先被计算。
结合性(`associativity`)定义了相同优先级的运算符是如何结合(或关联)的 —— 是与左边结合为一组,还是与右边结合为一组。可以将这意思理解为“它们是与左边的表达式结合的”或者“它们是与右边的表达式结合的”。
在复合表达式的运算顺序中,运算符的优先级和结合性是非常重要的。举例来说,为什么下面这个表达式的运算结果是`4`?
~~~
2 + 3 * 4 % 5
// 结果是 4
~~~
如果严格地从左到右进行运算,则运算的过程是这样的:
* 2 + 3 = 5
* 5 * 4 = 20
* 20 % 5 = 0
但是正确答案是 `4` 而不是 `0`。优先级高的运算符要先于优先级低的运算符进行计算。与C语言类似,在 Swift 当中,乘法运算符(`*`)与取余运算符(`%`)的优先级高于加法运算符(`+`)。因此,它们的计算顺序要先于加法运算。
而乘法与取余的优先级相同。这时为了得到正确的运算顺序,还需要考虑结合性。乘法与取余运算都是左结合的。可以将这考虑成为这两部分表达式都隐式地加上了括号:
~~~
2 + ((3 * 4) % 5)
~~~
`(3 * 4) = 12`,所以表达式相当于:
~~~
2 + (12 % 5)
~~~
`12 % 5 = 2`,所以表达式相当于:
~~~
2 + 2
~~~
此时可以容易地看出计算的结果为 `4`。
如果想查看完整的 Swift 运算符优先级和结合性规则,请参考[表达式](http://wiki.jikexueyuan.com/project/swift/chapter3/04_Expressions.html)。
> 注意:
> 对于C语言和 Objective-C 来说,Swift 的运算符优先级和结合性规则是更加简洁和可预测的。但是,这也意味着它们于那些基于C的语言不是完全一致的。在对现有的代码进行移植的时候,要注意确保运算符的行为仍然是按照你所想的那样去执行。
## 运算符函数
类和结构可以为现有的操作符提供自定义的实现,这通常被称为运算符重载(`overloading`)。
下面的例子展示了如何为自定义的结构实现加法操作符(`+`)。算术加法运算符是一个两目运算符(`binary operator`),因为它可以对两个目标进行操作,同时它还是中缀(`infix`)运算符,因为它出现在两个目标中间。
例子中定义了一个名为 `Vector2D` 的结构体用来表示二维坐标向量`(x, y)`,紧接着定义了一个可以对两个 `Vector2D` 结构体进行相加的运算符函数(`operator function`):
~~~
struct Vector2D {
var x = 0.0, y = 0.0
}
func + (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y + right.y)
}
~~~
该运算符函数被定义为一个全局函数,并且函数的名字与它要进行重载的 `+` 名字一致。因为算术加法运算符是双目运算符,所以这个运算符函数接收两个类型为 `Vector2D` 的输入参数,同时有一个 `Vector2D` 类型的返回值。
在这个实现中,输入参数分别被命名为 `left` 和 `right`,代表在 `+` 运算符左边和右边的两个 `Vector2D` 对象。函数返回了一个新的 `Vector2D` 的对象,这个对象的 `x` 和 `y` 分别等于两个参数对象的 `x` 和 `y` 的值之和。
这个函数被定义成全局的,而不是 `Vector2D` 结构的成员方法,所以任意两个 `Vector2D` 对象都可以使用这个中缀运算符:
~~~
let vector = Vector2D(x: 3.0, y: 1.0)
let anotherVector = Vector2D(x: 2.0, y: 4.0)
let combinedVector = vector + anotherVector
// combinedVector 是一个新的Vector2D, 值为 (5.0, 5.0)
~~~
这个例子实现两个向量 `(3.0,1.0)` 和 `(2.0,4.0)` 的相加,并得到新的向量 `(5.0,5.0)`。这个过程如下图示:
![Art/vectorAddition_2x.png](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-08-12_55caba9d5a437.png "Art/vectorAddition_2x.png")
### 前缀和后缀运算符
上个例子演示了一个双目中缀运算符的自定义实现。类与结构体也能提供标准单目运算符(`unary operators`)的实现。单目运算符只有一个操作目标。当运算符出现在操作目标之前时,它就是前缀(`prefix`)的(比如 `-a`),而当它出现在操作目标之后时,它就是后缀(`postfix`)的(比如 `i++`)。
要实现前缀或者后缀运算符,需要在声明运算符函数的时候在 `func` 关键字之前指定 `prefix` 或者 `postfix` 限定符:
~~~
prefix func - (vector: Vector2D) -> Vector2D {
return Vector2D(x: -vector.x, y: -vector.y)
}
~~~
这段代码为 `Vector2D` 类型实现了单目减运算符(`-a`)。由于单目减运算符是前缀运算符,所以这个函数需要加上`prefix` 限定符。
对于简单数值,单目减运算符可以对它们的正负性进行改变。对于 `Vector2D` 来说,单目减运算将其 `x` 和 `y` 属性的正负性都进行了改变。
~~~
let positive = Vector2D(x: 3.0, y: 4.0)
let negative = -positive
// negative 是一个值为 (-3.0, -4.0) 的 Vector2D 实例
let alsoPositive = -negative
// alsoPositive 是一个值为 (3.0, 4.0) 的 Vector2D 实例
~~~
### 复合赋值运算符
复合赋值运算符(`Compound assignment operators`)将赋值运算符(`=`)与其它运算符进行结合。比如,将加法与赋值结合成加法赋值运算符(`+=`)。在实现的时候,需要把运算符的左参数设置成 `inout` 类型,因为这个参数的值会在运算符函数内直接被修改。
~~~
func += (inout left: Vector2D, right: Vector2D) {
left = left + right
}
~~~
因为加法运算在之前已经定义过了,所以在这里无需重新定义。在这里可以直接利用现有的加法运算符函数,用它来对左值和右值进行相加,并再次赋值给左值:
~~~
var original = Vector2D(x: 1.0, y: 2.0)
let vectorToAdd = Vector2D(x: 3.0, y: 4.0)
original += vectorToAdd
// original 的值现在为 (4.0, 6.0)
~~~
还可以将赋值与 `prefix` 或 `postfix` 限定符结合起来,下面的代码为 `Vector2D` 实例实现了前缀自增运算符(`++a`):
~~~
prefix func ++ (inout vector: Vector2D) -> Vector2D {
vector += Vector2D(x: 1.0, y: 1.0)
return vector
}
~~~
这个前缀自增运算符使用了前面定义的加法赋值操作。它对 `Vector2D` 的 `x` 和 `y` 属性都进行了加 `1` 操作,再将结果返回:
~~~
var toIncrement = Vector2D(x: 3.0, y: 4.0)
let afterIncrement = ++toIncrement
// toIncrement 的值现在为 (4.0, 5.0)
// afterIncrement 的值同样为 (4.0, 5.0)
~~~
> 注意: 不能对默认的赋值运算符(`=`)进行重载。只有组合赋值符可以被重载。同样地,也无法对三目条件运算符`a ? b : c` 进行重载。
### 等价操作符
自定义的类和结构体没有对等价操作符(`equivalence operators`)进行默认实现,等价操作符通常被称为“相等”操作符(`==`)与“不等”操作符(`!=`)。对于自定义类型,Swift 无法判断其是否“相等”,因为“相等”的含义取决于这些自定义类型在你的代码中所扮演的角色。
为了使用等价操作符来对自定义的类型进行判等操作,需要为其提供自定义实现,实现的方法与其它中缀运算符一样:
~~~
func == (left: Vector2D, right: Vector2D) -> Bool {
return (left.x == right.x) && (left.y == right.y)
}
func != (left: Vector2D, right: Vector2D) -> Bool {
return !(left == right)
}
~~~
上述代码实现了“相等”运算符(`==`)来判断两个 `Vector2D` 对象是否有相等。对于 `Vector2D` 类型来说,“相等”意味“两个实例的 `x`属性 和 `y` 属性都相等”,这也是代码中用来进行判等的逻辑。示例里同时也实现了“不等”操作符(`!=`),它简单地将“相等”操作符进行取反后返回。
现在我们可以使用这两个运算符来判断两个 `Vector2D` 对象是否相等。
~~~
let twoThree = Vector2D(x: 2.0, y: 3.0)
let anotherTwoThree = Vector2D(x: 2.0, y: 3.0)
if twoThree == anotherTwoThree {
print("These two vectors are equivalent.")
}
// prints "These two vectors are equivalent."
~~~
### 自定义运算符
除了实现标准运算符,在 Swift 当中还可以声明和实现自定义运算符(`custom operators`)。可以用来自定义运算符的字符列表请参考[操作符](http://wiki.jikexueyuan.com/project/swift/chapter3/02_Lexical_Structure.html#operators)
新的运算符要在全局作用域内,使用 `operator` 关键字进行声明,同时还要指定 `prefix`、`infix` 或者 `postfix` 限定符:
~~~
operator prefix +++ {}
~~~
上面的代码定义了一个新的名为 `+++` 的前缀运算符。对于这个运算符,在 Swift 中并没有意义,因为我们针对`Vector2D` 的实例来定义它的意义。对这个示例来讲,`+++` 被实现为“前缀双自增”运算符。它使用了前面定义的复合加法操作符来让矩阵对自身进行相加,从而让 `Vector2D` 实例的 `x` 属性和 `y`属性的值翻倍:
~~~
prefix func +++ (inout vector: Vector2D) -> Vector2D {
vector += vector
return vector
}
~~~
`Vector2D` 的 `+++` 的实现和 `++` 的实现很相似, 唯一不同的是前者对自身进行相加, 而后者是与另一个值为 `(1.0, 1.0)`的向量相加.
~~~
var toBeDoubled = Vector2D(x: 1.0, y: 4.0)
let afterDoubling = +++toBeDoubled
// toBeDoubled 现在的值为 (2.0, 8.0)
// afterDoubling 现在的值也为 (2.0, 8.0)
~~~
### 自定义中缀运算符的优先级和结合性
自定义的中缀(`infix`)运算符也可以指定优先级(`precedence`)和结合性(`associativity`)。[优先级和结合性](http://wiki.jikexueyuan.com/project/swift/chapter2/25_Advanced_Operators.html#PrecedenceandAssociativity)中详细阐述了这两个特性是如何对中缀运算符的运算产生影响的。
结合性(`associativity`)可取的值有`left`,`right` 和 `none`。当左结合运算符跟其他相同优先级的左结合运算符写在一起时,会跟左边的操作数进行结合。同理,当右结合运算符跟其他相同优先级的右结合运算符写在一起时,会跟右边的操作数进行结合。而非结合运算符不能跟其他相同优先级的运算符写在一起。
结合性(`associativity`)的默认值是 `none`,优先级(`precedence`)如果没有指定,则默认为 `100`。
以下例子定义了一个新的中缀运算符 `+-`,此操作符是左结合的,并且它的优先级为 `140`:
~~~
infix operator +- { associativity left precedence 140 }
func +- (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y - right.y)
}
let firstVector = Vector2D(x: 1.0, y: 2.0)
let secondVector = Vector2D(x: 3.0, y: 4.0)
let plusMinusVector = firstVector +- secondVector
// plusMinusVector 是一个 Vector2D 类型,并且它的值为 (4.0, -2.0)
~~~
这个运算符把两个向量的 `x` 值相加,同时用第一个向量的 `y` 值减去第二个向量的 `y` 值。因为它本质上是属于“加型”运算符,所以将它的结合性和优先级被设置为(`left` 和 `140`),这与 `+` 和 `-` 等默认的中缀加型操作符是相同的。完整的 Swift 操作符默认结合性与优先级请参考表达式。
> 注意: 当定义前缀与后缀操作符的时候,我们并没有指定优先级。然而,如果对同一个操作数同时使用前缀与后缀操作符,则后缀操作符会先被执行。