语句(Statements)
1.0 翻译:coverxit 校对:numbbbbb, coverxit, stanzhai
2.0 翻译+校对:littledogboy
本页包含内容:
在 Swift 中,有三种类型的语句:简单语句、编译器控制语句和控制流语句。简单语句是最常见的,用于构造表达式或者声明。编译器控制语句允许程序改变编译器的行为,包含编译配置语句和线路控制语句。
控制流语句则用于控制程序执行的流程,Swift 中有多种类型的控制流语句:循环语句、分支语句和控制转移语句。循环语句用于重复执行代码块;分支语句用于执行满足特定条件的代码块;控制转移语句则用于改变代码的执行顺序。另外,Swift 提供了 do
语句,用于构建局部作用域,还用于错误的捕获和处理;还提供了 defer
语句,用于退出当前作用域之前执行清理操作。
是否将分号(;
)添加到语句的末尾是可选的。但若要在同一行内写多条独立语句,则必须使用分号。
语句语法
</a> 语句 → 表达式 ;可选
语句 → 声明 ;可选
语句 → 循环语句 ;可选
语句 → 分支语句 ;可选
语句 → 带标签的语句 ;可选
语句 → 控制转移语句 ;可选
语句 → defer 语句 ;可选
语句 → do 语句 :可选
语句 → 编译器控制语句
多条语句 → 语句 多条语句可选
循环语句
循环语句会根据特定的循环条件来重复执行代码块。Swift 提供四种类型的循环语句:for
语句、for-in
语句、while
语句和 repeat-while
语句。
通过 break
语句和 continue
语句可以改变循环语句的控制流。有关这两条语句,详情参见 Break 语句 和 Continue 语句。
循环语句语法
循环语句 → for 语句
循环语句 → for-in 语句
循环语句 → while 语句
循环语句 → repeat-while 语句
For 语句
for
语句只有在循环条件为真时重复执行代码块,同时计数器递增。
for
语句的形式如下:
for 初始化; 条件; 增量 {
语句
}
初始化、条件和增量语句之间必须以分号相隔,循环体中的语句必须以花括号包裹。
for
语句的执行流程如下:
- 初始化只会被执行一次,通常用于声明和初始化在接下来的循环中需要使用的变量。
- 判断条件的值。如果为
true
,循环体中的语句将会被执行,然后转到第 3 步;如果为false
,循环体中的语句以及增量语句都不会被执行,for
语句至此执行完毕。 - 执行增量语句,然后重复第 2 步。
在初始化语句中定义的变量仅在 for
循环的作用域内有效。
条件的结果必须符合 BooleanType
协议。
for 语句语法
</a> for 语句 → for for初始条件可选 ; 表达式可选 ; 表达式可选 代码块
for语句 → for ( for初始条件可选 ; 表达式可选 ; 表达式可选 ) 代码块
for 初始条件 → 变量声明 | 表达式列表
For-In 语句
for-in
语句会为集合(或符合 Sequence
协议的任意类型)中的每一项执行一次代码块。
for-in
语句的形式如下:
for 项 in 集合 {
循环体语句
}
for-in
语句在循环开始前会调用集合表达式的 generate()
方法来获取一个符合 Generator
协议的类型的值。接下来循环开始,反复调用该值的 next()
方法。如果其返回值不是 None
,它将会被赋给“项”,然后执行循环体语句,执行完毕后回到循环开始处,继续重复这一过程;否则,既不会赋值也不会执行循环体语句,for-in
语句至此执行完毕。
While 语句
只要循环条件为真,while
语句就会重复执行代码块。
while
语句的形式如下:
while 条件 {
语句
}
while
语句的执行流程如下:
- 判断条件的值。如果为
true
,转到第 2 步;如果为false
,while
语句至此执行完毕。 - 执行循环体中的语句,然后重复第 1 步。
由于会在执行循环体中的语句前判断条件的值,因此循环体中的语句可能会被执行若干次,也可能一次也不会被执行。
条件的结果必须符合 BooleanType
协议。另外,条件语句也可以使用可选绑定,请参阅 可选绑定。
while 语句语法
条件列表 → 条件 | 条件 , 条件列表
</a> 条件 → 可用性条件 | case条件 | 可选绑定条件
case 条件 → case 模式 构造器 where子句可选
可选绑定条件 → 可选绑定头 可选绑定附加列表可选 where子句可选
</a> 可选绑定头 → let 模式 构造器 | var 模式 构造器
可选绑定附加部分列表 → 可选绑定附加部分 | 可选绑定附加部分 , 可选绑定附加部分列表
可选绑定附加部分 → 模式 构造器 | 可选绑定头
Repeat-While 语句
repeat-while
语句至少执行一次代码块,之后只要循环条件为真,就会重复执行代码块。
repeat-while
语句的形式如下:
repeat {
语句
} while 条件
repeat-while
语句的执行流程如下:
- 执行循环体中的语句,然后转到第 2 步。
- 判断条件的值。如果为
true
,重复第 1 步;如果为false
,repeat-while
语句至此执行完毕。
由于条件的值是在循环体中的语句执行后才进行判断,因此循环体中的语句至少会被执行一次。
条件的结果必须符合 BooleanType
协议。另外,条件语句也可以使用可选绑定,请参阅 可选绑定。
分支语句
分支语句会根据一个或者多个条件来执行指定部分的代码。分支语句中的条件将会决定程序如何分支以及执行哪部分代码。Swift 提供两种类型的分支语句:if
语句和 switch
语句。
if
语句和 switch
语句中的控制流可以用 break
语句改变,请参阅 Break 语句。
If 语句
if
语句会根据一个或多个条件来决定执行哪一块代码。
if
语句有两种基本形式,无论哪种形式,都必须有花括号。
第一种形式是当且仅当条件为真时执行代码,像下面这样:
if 条件 {
语句
}
第二种形式是在第一种形式的基础上添加 else
语句,当只有一个 else
语句时,像下面这样:
if 条件 {
若条件为真则执行这部分语句
} else {
若条件为假则执行这部分语句
}
else
语句也可包含 if
语句,从而形成一条链来测试更多的条件,像下面这样:
if 条件1 {
若条件1为真则执行这部分语句
} else if 条件2 {
若条件2为真则执行这部分语句
} else {
若前两个条件均为假则执行这部分语句
}
if
语句中条件的结果必须符合 BooleanType
协议。另外,条件语句也可以使用可选绑定,请参阅 可选绑定。
if 语句语法
</a> if 语句 → if 条件子句 代码块 else子句可选
else 子句 → else 代码块 | else if语句
Guard 语句
如果一个或者多个条件不成立,可用 guard
语句用来退出当前作用域。
guard
语句的格式如下:
guard 条件 else {
语句
}
guard
语句中条件的结果必须符合 BooleanType
协议,而且条件语句可以使用可选绑定,请参阅 可选绑定。
在 guard
语句中进行可选绑定的常量或者变量,其可用范围从声明开始直到作用域结束。
guard
语句必须有 else
子句,而且必须在该子句中调用标记 noreturn
特性的函数,或者使用下面的语句退出当前作用域:
return
break
continue
throw
关于控制转移语句,请参阅 控制转移语句。
Switch 语句
switch
语句会根据控制表达式的值来决定执行哪部分代码。
switch
语句的形式如下:
switch 控制表达式 {
case 模式1:
语句
case 模式2 where 条件:
语句
case 模式3 where 条件, 模式4 where 条件:
语句
default:
语句
}
switch
语句会先计算控制表达式的值,然后与每一个 case
的模式进行匹配。如果匹配成功,程序将会执行对应的 case
中的语句。另外,每一个 case
都不能为空,也就是说在每一个 case
中必须至少有一条语句。如果你不想在匹配到的 case
中执行代码,只需在该 case
中写一条 break
语句即可。
可以用作控制表达式的值是十分灵活的。除了标量类型外,如 Int
、Character
,你可以使用任何类型的值,包括浮点数、字符串、元组、自定义类型的实例和可选类型,甚至是指定的 Range
或枚举类型中的成员值。关于如何在 switch
语句中使用这些类型,请参阅 控制流 一章中的 Switch。
每个 case
的模式后面可以有一个 where
子句。where
子句由 where
关键字紧跟一个提供额外测试条件的表达式组成。因此,当且仅当控制表达式匹配一个 case
的模式且 where
子句的表达式为真时,case
中的语句才会被执行。在下面的例子中,控制表达式只会匹配包含两个相等元素的元组,例如 (1, 1)
:
case let (x, y) where x == y:
正如上面这个例子,也可以在模式中使用 let
(或 var
)语句来绑定常量(或变量)。这些常量(或变量)可以在对应的 where
子句以及 case
中的代码中使用。但是,如果一个 case
中含有多个模式,那么这些模式都不能绑定常量(或变量)。
switch
语句也可以包含默认分支,使用 default
关键字表示。只有所有 case
都无法匹配控制表达式时,默认分支中的代码才会被执行。一个 switch
语句只能有一个默认分支,而且必须在 switch
语句的最后面。
switch
语句中 case
的匹配顺序和源代码中的书写顺序保持一致。因此,当多个模式都能匹配控制表达式时,只有第一个匹配的 case
中的代码会被执行。
Switch 语句必须是详尽的
在 Swift 中,switch
语句中控制表达式的每一个可能的值都必须至少有一个 case
与之对应。在某些无法面面俱到的情况下(例如,表达式的类型是 Int
),你可以使用 default
分支满足该要求。
不存在隐式落空
当匹配到的 case
中的代码执行完毕后,switch
语句会直接退出,而不会继续执行下一个 case
。这就意味着,如果你想执行下一个 case
,需要显式地在当前 case
中使用 fallthrough
语句。关于 fallthrough
语句的更多信息,请参阅 Fallthrough 语句。
switch 语句语法
switch 语句 → switch 表达式 { switch-case列表可选 }
</a> switch case 列表 → switch-case switch-case列表可选
switch case → case标签 多条语句 | default标签 多条语句
case 标签 → case case项列表 :
</a> case 项列表 → 模式 where子句可选 | 模式 where子句可选 , case项列表
default 标签 → default :
带标签的语句
你可以在循环语句或 switch
语句前面加上标签,它由标签名和紧随其后的冒号(:
)组成。在 break
和 continue
后面跟上标签名可以显式地在循环语句或 switch
语句中改变相应的控制流。关于这两条语句用法,请参阅 Break 语句 和 Continue 语句。
标签的作用域在该标签所标记的语句内。你可以不使用带标签的语句,但只要使用它,在作用域内需保证标签名唯一。
关于使用带标签的语句的例子,请参阅 控制流 一章中的 带标签的语句。
带标签的语句语法
</a> 带标签的语句 → 语句标签 循环语句 | 语句标签 if语句 | 语句标签 switch语句
语句标签 → 标签名称 :
标签名称 → 标识符
控制转移语句
控制转移语句能够无条件地把控制权从一片代码转移到另一片代码,从而改变代码执行的顺序。Swift 提供五种类型的控制转移语句:break
语句、continue
语句、fallthrough
语句、return
语句和 throw
语句。
控制转移语句语法
控制转移语句 → break 语句
控制转移语句 → continue 语句
控制转移语句 → fallthrough 语句
控制转移语句 → return 语句
控制转移语句 → throw 语句
Break 语句
break
语句用于终止循环语句或 switch
语句的执行。使用 break
语句时,可以只写 break
这个关键词,也可以在 break
后面跟上标签名,像下面这样:
break
break标签名
当 break
语句后面带标签名时,可用于终止由这个标签标记的循环语句或 switch
语句的执行。
而只写 break
时,则会终止 switch
语句或包含 break
语句的最内层循环的执行。
在这两种情况下,控制权都会被传递给循环语句或 switch
语句后面的第一行语句。
关于使用 break
语句的例子,请参阅 控制流 一章的 Break 和 带标签的语句。
break 语句语法
break 语句 → break 标签名称可选
Continue 语句
continue
语句用于终止循环中当前迭代的执行,但不会终止该循环的执行。使用 continue
语句时,可以只写 continue
这个关键词,也可以在 continue
后面跟上标签名,像下面这样:
continue
continue标签名
当 continue
语句后面带标签名时,可用于终止由这个标签标记的循环中当前迭代的执行。
而当只写 continue
时,可用于终止上下文中包含 continue
语句的最内层循环中当前迭代的执行。
在这两种情况下,控制权都会被传递给循环外面的第一行语句。
在 for
语句中,continue
语句执行后,增量表达式还是会被计算,这是因为每次循环体执行完毕后,增量表达式都会被计算。
关于使用 continue
语句的例子,请参阅 控制流 一章的 Continue 和 带标签的语句。
continue 语句语法
continue 语句 → continue 标签名称可选
Fallthrough 语句
fallthrough
语句用于在 switch
语句中转移控制权。fallthrough
语句会把控制权从 switch
语句中的一个 case
转移到下一个 case
。这种控制权转移是无条件的,即使下一个 case
的模式与 switch
语句的控制表达式的值不匹配。
fallthrough
语句可出现在 switch
语句中的任意 case
中,但不能出现在最后一个 case
中。同时,fallthrough
语句也不能把控制权转移到使用了可选绑定的 case
。
关于在 switch
语句中使用 fallthrough
语句的例子,请参阅 控制流 一章的 控制转移语句。
Return 语句
return
语句用于在函数或方法的实现中将控制权转移给调用者,接着程序将会从调用者的位置继续向下执行。
使用 return
语句时,可以只写 return
这个关键词,也可以在 return
后面跟上表达式,像下面这样:
return
return表达式
当 return
语句后面带表达式时,表达式的值将会返回给调用者。如果表达式的值的类型与函数或者方法声明的返回类型不匹配,Swift 则会在返回表达式的值之前将表达式的值的类型转换为返回类型。
注意
正如 可失败构造器 中所描述的,return nil
在可失败构造器中用于表明构造失败。
而只写 return
时,仅仅是将控制权从该函数或方法转移给调用者,而不返回一个值(也就是说,函数或方法的返回类型为 Void
或者说 ()
)。
return 语句语法
return 语句 → return 表达式可选
Available 语句
可用性条件可作为 if
,while
,guard
语句的条件,可以在运行时基于特定的平台参数来查询 API 的可用性。
可用性条件的形式如下:
if #available(平台名称 版本, ..., *) {
如果 API 可用,则执行这部分语句
} else {
如果 API 不可用,则执行这部分语句
}
使用可用性条件来执行一个代码块时,取决于使用的接口在运行时是否可用。编译器会根据可用性条件提供的信息以及运行时的平台来决定是否执行相应的代码块。
可用性条件使用一系列逗号分隔的平台名称和版本。使用 iOS
,OSX
,以及 watchOS
等作为平台名称,并写上相应的版本号。*
参数是必须写的,用于处理未来的潜在平台。可用性条件确保了运行时的平台不低于条件中指定的平台版本时才执行代码块。
与布尔类型的条件不同,不能用逻辑运算符 &&
和 ||
合并可用性条件。
可用性条件语法
可用性条件 → #available ( 可用性参数列表 )
</a> 可用性参数列表 → 可用性参数 | 可用性参数 , 可用性参数列表
可用性参数 → 平台名称 平台版本
可用性条件 → *
平台名称 → iOS | iOSApplicationExtension
平台名称 → OSX | OSXApplicationExtension
平台名称 → watchOS
平台版本 → 十进制数字
平台版本 → 十进制数字 . 十进制数字
平台版本 → 十进制数字 . 十进制数字 . 十进制数字
Throw 语句
throw
语句出现在抛出函数或者抛出方法体内,或者类型被 throws
关键字标记的闭包表达式体内。
throw
语句使程序在当前作用域结束执行,并向外围作用域传播错误。抛出的错误会一直传播,直到被 do
语句的 catch
子句处理掉。
throw
语句由 throw
关键字紧跟一个表达式组成,如下所示:
throw
表达式
表达式的结果必须符合 ErrorType
协议。
关于如何使用 throw
语句的例子,请参阅 错误处理 一章的 用 throwing 函数传递错误。
throw 语句语法
throw 语句 → throw 表达式
Defer 语句
defer
语句用于在退出当前作用域之前执行代码。
defer
语句形式如下:
defer {
语句
}
在 defer
语句中的语句无论程序控制如何转移都会执行。这意味着 defer
语句可以被使用在以下这些情况,例如关闭文件描述,或者即使抛出了错误也需要执行的一些动作。
如果多个 defer
语句出现在同一作用域内,那么它们执行的顺序与出现的顺序相反。给定作用域中的第一个 defer
语句,会在最后执行,这意味着最后执行的 defer
语句中涉及的资源可以被其他 defer
语句清理掉。
func f() {
defer { print("First") }
defer { print("Second") }
defer { print("Third") }
}
f()
// 打印 “Third”
// 打印 “Second”
// 打印 “First”
defer
语句中的语句无法将控制权转移到 defer
语句外部。
defer 语句语法
延迟语句 → defer 代码块
Do 语句
do
语句用于引入一个新的作用域,该作用域中可以含有一个或多个 catch
子句,catch
子句中定义了一些匹配错误条件的模式。do
语句作用域内定义的常量和变量,只能在 do
语句作用域内使用。
Swift 中的 do
语句与 C 中限定代码块界限的大括号({}
)很相似,并且在程序运行的时候并不会造成系统开销。
do
语句的形式如下:
do {
try 表达式
语句
} catch 模式1 {
语句
} catch 模式2 where 条件 {
语句
}
如同 switch
语句,编译器会判断 catch
子句是否有遗漏。如果 catch
子句没有遗漏,则认为错误被处理。否则,错误会自动传播到外围作用域,被一个 catch
语句处理掉或者继续向外抛出,抛出函数必须以 throws
关键字声明。
为了确保错误已经被处理,可以让 catch
子句使用匹配所有错误的模式,如通配符模式(_
)。如果一个 catch
子句不指定一种具体模式,catch
子句会匹配任何错误,并绑定到名为 error
的局部变量。有关在 catch
子句中使用模式的更多信息,请参阅 模式。
关于如何在 do
语句中使用一些 catch
子句的例子,请参阅 处理错误。
do 语句语法
</a> do 语句 → do 代码块 多条 catch子句可选
多条 catch 子句 → catch子句 多条 catch子句可选
catch 子句 → catch 模式可选 where子句可选 代码块
编译器控制语句
编译器控制语句允许程序改变编译器的行为。Swift 有两种编译器控制语句:编译配置语句和线路控制语句。
编译配置语句
编译配置语句可以根据一个或多个配置来有条件地编译代码。
每一个编译配置语句都以 #if
开始,#endif
结束。如下是一个简单的编译配置语句:
#if 编译配置项
语句
#endif
和 if
语句的条件不同,编译配置的条件是在编译时进行判断的。只有编译配置在编译时判断为 true
的情况下,相应的语句才会被编译和执行。
编译配置可以是 true
和 false
的字面量,也可以是使用 -D
命令行标志的标识符,或者是下列表格中的任意一个平台测试函数。
函数 | 可用参数 |
---|---|
os() |
OSX , iOS , watchOS , tvOS |
arch() |
i386 , x86_64 , arm , arm64 |
注意
arch(arm)
编译配置在 ARM 64位设备上不会返回true
。如果代码在 32 位的 iOS 模拟器上编译,arch(i386)
编译配置返回true
。
你可以使用逻辑操作符 &&
、||
和 !
来组合多个编译配置,还可以使用圆括号来进行分组。
就像 if
语句一样,你可以使用 #elseif
子句来添加任意多个条件分支来测试不同的编译配置。你也可以使用 #else
子句来添加最终的条件分支。包含多个分支的编译配置语句例子如下:
#if 编译配置1
如果编译配置1成立则执行这部分代码
#elseif 编译配置2
如果编译配置2成立则执行这部分代码
#else
如果编译配置均不成立则执行这部分代码
#endif
注意
即使没有被编译,编译配置中的语句仍然会被解析。
编译配置语句语法
单个编译配置语句 → #if 编译配置 语句可选 多个编译配置elseif子句可选 - 单个编译配置else子句可选 #endif
</a> 多个编译配置 elseif 子句 → 单个编译配置elseif子句 多个编译配置elseif子句可选
单个编译配置 elseif 子句 → #elseif 编译配置 语句可选
单个编译配置 else 子句 → #else 语句可选
编译配置 → 平台测试函数
编译配置 → 标识符
编译配置 → 布尔值字面量
编译配置 → ( 编译配置 )
编译配置 → ! 编译配置
编译配置 → 编译配置 && 编译配置
编译配置 → 编译配置 || 编译配置
平台测试函数 → os ( 操作系统 )
平台测试函数 → arch ( 架构 )
</a> 操作系统 → OSX | iOS | watchOS | tvOS
架构 → i386 | x86_64 | arm | arm64
线路控制语句
线路控制语句用来为被编译的源代码指定一个与原始行号和文件名不同的行号和文件名。使用线路控制语句可以改变源代码的位置,以便进行分析和调试。
线路控制语句形式如下:
#line
行号
文件名
线路控制语句会改变之后的字面量表达式 __LINE__
和 __FILE__
的值。行号
是一个大于 0 的整形字面量,会改变 __LINE__
的值。文件名
是一个字符串字面量,会改变 __FILE__
的值。
你可以通过 #line
语句,即不指定行号和文件名,来将源代码的位置重置回默认的行号和文件名。
线路控制语句必须独占一行,而且不能是源代码文件的最后一行。
线路控制语句语法
</a> 线路控制语句 → #line
线路控制语句 → #line 行号 文件名
行号 → 大于 0 的十进制整数
文件名 → 静态字符串字面量