在Swift中,有三种语句:简单语句,编译器控制语句和控制流语句。简单的语句是最常见的,由表达式或声明组成。编译器控制语句允许程序更改编译器行为的各个方面,并包含一个条件编译块和一个行控制语句。
控制流程语句用于控制程序中的执行流程。 Swift中有几种类型的控制流语句,包括循环语句,分支语句和控制传输语句。循环语句允许重复执行一段代码,分支语句只允许某些代码块在满足某些条件时执行,并且控制传输语句提供了一种方法来改变执行代码的顺序。另外,Swift提供了一个do语句来引入范围,捕获和处理错误,以及在当前范围退出之前运行清理操作的延迟语句。
分号(;)可以选择出现在任何语句之后,如果它们出现在同一行上,则用于分隔多个语句。
语句的语法
statement → expression-;-opt-
statement → declaration-;-opt-
statement → loop-statement-;-opt-
statement → branch-statement-;-opt-
statement → labeled-statement-;-opt-
statement → control-transfer-statement-;-opt-
statement → defer-statement-;-opt-
statement → do-statement-;-opt-
statement → compiler-control-statement-
statements → statement-statements-opt-
循环语句
循环语句允许重复执行一段代码,具体取决于循环中指定的条件。 Swift有三个循环语句:一个for-in语句,一个while语句和一个repeat-while语句。
循环语句中的控制流可以通过break语句和continue语句来更改,并在下面的Break语句和Continue语句中进行了讨论。
loop-statement → for-in-statement-
loop-statement → while-statement-
loop-statement → repeat-while-statement-
For-In声明
for-in语句允许对符合Sequence协议的集合(或任何类型)中的每个项目执行一次代码块。
For-in声明具有以下形式:
foritemincollection {
statements
}
在集合表达式上调用makeIterator()方法来获取迭代器类型的值 - 即符合IteratorProtocol协议的类型。 程序通过调用迭代器上的next()方法开始执行一个循环。 如果返回值不为零,则将其分配给项目模式,程序将执行语句,然后在循环的开始处继续执行。 否则,程序不执行分配或执行语句,并且它完成执行for-in语句
for-in-statement → for-case-opt-pattern-in-expression-where-clause-opt-code-block-
声明
while语句允许一段代码重复执行,只要条件保持为真。
一段时间的陈述有以下形式:
whilecondition {
statements
}
while语句执行如下:
条件被评估。
如果为true,则继续执行步骤2.如果为false,则程序完成执行while语句。
程序执行语句,然后执行返回到步骤1。
由于在语句执行之前评估条件的值,所以while语句中的语句可以执行零次或多次。
条件的值必须是Bool类型或桥接到Bool的类型。 该条件也可以是可选绑定声明,如可选绑定中所述
while-statement → while-condition-list-code-block-
condition-list → condition-condition-,-condition-list-
condition → expression-availability-condition-case-condition-optional-binding-condition-
case-condition → case-pattern-initializer-
optional-binding-condition → let-pattern-initializer-var-pattern-initializer-
repeat-while声明
只要条件保持成立,repeat-while语句就允许一段代码执行一次或多次。
repeat while语句具有以下形式:
repeat {
statements
} whilecondition
repeat-while语句执行如下:
程序执行语句,并继续执行第2步。
条件被评估。
如果为true,则执行返回到步骤1.如果为false,则程序完成执行repeat-while语句。
由于在语句执行后评估条件的值,所以repeat-while语句中的语句至少执行一次。
条件的值必须是Bool类型或桥接到Bool的类型。 该条件也可以是可选绑定声明,如可选绑定中所述
repeat-while-statement → repeat-code-block-while-expression-
分支语句
分支语句允许程序根据一个或多个条件的值执行代码的某些部分。 分支语句中指定的条件值控制程序如何分支,因此执行的是哪个代码块。 Swift有三个分支语句:if语句,guard语句和switch语句。
if语句或switch语句中的控制流可以通过break语句进行更改,并在下面的Break语句中进行了讨论
branch-statement → if-statement-
branch-statement → guard-statement-
branch-statement → switch-statement-
If 声明
if语句用于根据对一个或多个条件的评估来执行代码。
if语句有两种基本形式。 在每一种形式中,开启和关闭支撑都是必需的。
第一种形式只允许在条件为真时执行代码并具有以下形式:
if condition {
statements
}
if语句的第二种形式提供了一个额外的else子句(由else关键字引入),用于在条件为真时执行一部分代码,并在相同条件为假时执行另一部分代码。 当存在单个else子句时,if语句具有以下形式:
if condition {
statements to execute if condition is true
} else {
statements to execute if condition is false
}
if语句的else子句可以包含另一个if语句来测试多个条件。 以这种方式链接在一起的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语句中任何条件的值必须是Bool类型或桥接到Bool的类型。 该条件也可以是可选绑定声明,如可选绑定中所述
if-statement → if-condition-list-code-block-else-clause-opt-
else-clause → else-code-block-else-if-statement-
guard 声明
如果没有满足一个或多个条件,则使用警戒语句将程序控制转移出范围。
Guard 声明有以下形式:
guard conditionelse {
statements
}
guard语句中任何条件的值必须是Bool类型或桥接到Bool的类型。 该条件也可以是可选绑定声明,如可选绑定中所述。
任何在guard语句条件中从可选绑定声明中赋值的常量或变量都可以用于guard语句的其余部分的封闭范围。
guard语句的else子句是必需的,并且必须使用Never返回类型调用函数,或者使用以下语句之一在guard语句的封闭范围外调用程序控制:
return
break
continue
throw
控制转移报表在下面的控制转移报表中讨论。 有关Never返回类型的函数的更多信息,请参见永不返回的函数。
guard-statement → guard-condition-list-else-code-block-
switch句
switch语句允许根据控制表达式的值执行某些代码块。
switch语句具有以下形式:
switch control expression {
case pattern 1:
statements
case pattern 2wherecondition:
statements
case pattern 3wherecondition,
pattern 4wherecondition:
statements
default:
statements
}
评估switch语句的控件表达式,然后与每种情况下指定的模式进行比较。如果找到匹配项,程序将执行该案例范围内列出的语句。每种情况的范围不能为空。因此,您必须在每个案例标签的冒号(:)后面至少包含一条语句。如果您不打算在匹配案例的正文中执行任何代码,请使用单个break语句。
您的代码可以分支的表达式的值非常灵活。例如,除了标量类型的值(例如整数和字符)之外,您的代码还可以在任何类型的值上进行分支,包括浮点数,字符串,元组,自定义类的实例和可选项。控件表达式的值甚至可以与枚举中的大小写的值相匹配,并检查是否包含在指定的值范围内。有关如何在switch语句中使用这些不同类型的值的示例,请参阅切换到控制流。
switch case可以在每个模式之后选择性地包含where子句。 where子句由where关键字后跟一个表达式引入,用于在案例中的模式被认为与控制表达式匹配之前提供附加条件。如果存在where子句,则只有在控件表达式的值与案例的某个模式匹配并且where子句的表达式求值为true时,才会执行相关案例中的语句。例如,只有当它是一个包含两个具有相同值的元素(例如(1,1))的元组时,控件表达式才与下例中的情况匹配。
case let (x, y) wherex == y:
如上例所示,案例中的模式也可以使用let关键字绑定常量(它们也可以使用var关键字绑定变量)。这些常量(或变量)可以在相应的where子句中和案例范围内的其余代码中引用。如果案例包含与控制表达式匹配的多个模式,则所有模式必须包含相同的常量或变量绑定,并且每个绑定变量或常量在所有案例模式中必须具有相同类型。
switch语句还可以包含默认情况,由default关键字引入。只有在没有其他情况符合控制表达式的情况下,才会执行默认情况下的代码。switch语句只能包含一个默认情况,它必须出现在switch语句的末尾。
虽然模式匹配操作的实际执行顺序,特别是案例中模式的评估顺序未指定,但switch语句中的模式匹配的行为就好像评估是按源顺序执行的 - 即,它们的顺序出现在源代码中。因此,如果多个案例包含评估为相同值的模式,并且因此可以匹配控制表达式的值,那么程序将仅按照源顺序执行第一个匹配案例中的代码。
开关语句必须是彻底的
在Swift中,控件表达式类型的每个可能值都必须与至少一个案例模式的值相匹配。当这种做法不可行时(例如,当控件表达式的类型为Int时),可以包含默认情况以满足要求。
执行不隐含地通过案例
在匹配的情况下的代码完成执行后,程序退出switch语句。程序执行不会继续或“落入”下一个案例或默认案例。也就是说,如果您希望执行从一种情况继续下一种情况,那么明确地包括一个贯穿式语句,该语句只是由fallthrough关键字组成,在您希望继续执行的情况下。有关贯彻声明的更多信息,请参阅下面的“贯彻声明”
switch-statement → switch-expression-{-switch-cases-opt-}-
switch-cases → switch-case-switch-cases-opt-
switch-case → case-label-statements-default-label-statements-
case-label → case-case-item-list-:-
case-item-list → pattern-where-clause-opt-pattern-where-clause-opt-,-case-item-list-
default-label → default-:-
where-clause → where-where-expression-
where-expression → expression-
标记语句
您可以为循环语句,if语句,switch语句或do语句添加一个语句标签,该语句标签由紧跟冒号(:)后的标签名称组成。 使用带有break和continue语句的语句标签可以明确地说明如何在循环语句或switch语句中更改控制流,如下面的Break语句和Continue语句所述。
带标签语句的范围是语句标签后面的整个语句。 您可以嵌套标记的语句,但每个语句标签的名称必须是唯一的。
有关更多信息和查看如何使用语句标签的示例,请参阅控制流中的标记语句。
labeled-statement → statement-label-loop-statement-
labeled-statement → statement-label-if-statement-
labeled-statement → statement-label-switch-statement-
labeled-statement → statement-label-do-statement-
statement-label → label-name-:-
label-name → identifier-
控制转移语句
通过无条件地将程序控制从一段代码转移到另一段代码,控制传输语句可以改变程序中代码的执行顺序。 Swift有五个控制传输语句:break语句,continue语句,fallthrough语句,return语句和throw语句。
control-transfer-statement → break-statement-
control-transfer-statement → continue-statement-
control-transfer-statement → fallthrough-statement-
control-transfer-statement → return-statement-
control-transfer-statement → throw-statement-
break
break语句结束while,if语句或switch语句的程序执行。 break语句可以仅由break关键字组成,也可以由break关键字和语句标签的名称组成,如下所示。
break
breaklabel name
当break语句后跟一个语句标签的名称时,它会结束循环的执行,if语句或由该标签命名的switch语句。
当break语句后面没有跟着语句标签的名称时,它会结束switch语句的程序执行或其发生的最内层循环语句。 您不能使用未标记的break语句来突破if语句。
在这两种情况下,程序控制都会在封闭循环或切换语句之后传送到第一行代码(如果有的话)。
有关如何使用break语句的示例,请参阅控制流程中的中断和标记语句。
break-statement → break-label-name-opt-
continue语句
continue语句结束当前循环语句迭代的程序执行,但不会停止循环语句的执行。 continue语句可以仅由continue关键字组成,也可以由continue关键字后跟一个语句标签的名称组成,如下所示。
continue
continuelabel name
当continue语句后跟一个语句标签的名称时,它会结束由该标签命名的循环语句的当前迭代的程序执行。
当continue语句后面没有语句标签名称时,它会结束程序执行当前迭代的最内层封闭循环语句。
在这两种情况下,程序控制都会转换为封闭循环语句的条件。
在for语句中,增量表达式在执行continue语句后仍然计算,因为增量表达式是在循环体的执行之后计算的。
有关如何使用continue语句的示例,请参阅控制流中的继续和标记语句。
GRAMMAR的继续声明
continue-statement → continue-label-name-opt-
fallthrough-
fallthrough-语句由fallthrough-关键字组成,并且仅在switch语句的大小写块中出现。 贯穿式语句会导致程序执行从switch语句中的一个case继续到下一个case。 即使case标签的模式与switch语句的控制表达式的值不匹配,程序仍继续执行下一个案例。
循环语句可以出现在switch语句中的任何地方,而不仅仅是case块的最后一个语句,但不能在最后一个case块中使用。 它也不能将控制转移到模式包含值绑定模式的案例块中。
有关如何在switch语句中使用贯穿式语句的示例,请参阅控制流中的控制传输语句
fallthrough-statement → fallthrough-
return声明
返回语句出现在函数或方法定义的主体中,并导致程序执行返回到调用函数或方法。 程序执行继续执行函数或方法调用之后的位置。
return语句可以仅由return关键字组成,也可以由返回关键字和一个表达式组成,如下所示。
return
returne xpression
当返回语句后跟一个表达式时,表达式的值将返回给调用函数或方法。 如果表达式的值与函数或方法声明中声明的返回类型的值不匹配,则表达式的值在返回到调用函数或方法之前转换为返回类型。
注意
如Failable Initializers中所述,返回语句的特殊形式(返回nil)可用于failable初始化程序来指示初始化失败。
当一个表达式没有跟随返回语句时,它只能用于从不返回值的函数或方法返回(即函数或方法的返回类型为Void或())时。
return-statement → return-expression-opt-
throw语句声明
抛出语句出现在抛出函数或方法的主体中,或者在关系表达式的主体中,其类型用throws关键字标记。
throw语句会导致程序结束当前作用域的执行并开始将错误传播到其封闭作用域。 抛出的错误继续传播,直到它由do语句的catch子句处理。
一个throw语句由throw关键字和一个表达式组成,如下所示。
throw expression
表达式的值必须具有符合Error协议的类型。
有关如何使用throw语句的示例,请参阅在错误处理中使用抛出函数传播错误。
throw-statement → throw-expression-
Defer声明
Defer语句用于在将程序控制转移到延迟语句出现的范围之外之前执行代码。
Defer声明具有以下形式:
defer {
statements
}
无论程序控制如何传输,defer语句中的语句都会执行。 这意味着可以使用defer语句来执行手动资源管理,例如关闭文件描述符,并执行即使发生错误也需要执行的操作。
如果多个defer语句出现在同一个作用域中,它们出现的顺序与它们执行的顺序相反。 首先执行给定范围中的最后一个defer语句意味着最后一个defer语句中的语句可以引用将由其他defer语句清理的资源。
func f() {
defer { print("First") }
defer { print("Second") }
defer { print("Third") }
}
f()
// Prints "Third"
// Prints "Second"
// Prints "First"
延迟声明中的声明不能将程序控制转移到延迟声明之外。
defer-statement → defer-code-block-
do声明
do语句用于引入新的作用域,并可以选择性地包含一个或多个catch子句,其中包含与定义的错误条件相匹配的模式。 在do语句范围内声明的变量和常量只能在该范围内访问。
Swift中的do语句与C中使用的花括号({})类似,用于分隔代码块,并且不会在运行时产生性能成本。
do语句具有以下形式:
do {
try expression
statements
} catch pattern 1 {
statements
} catch pattern 2wherecondition {
statements
}
像switch语句一样,编译器试图推断catch子句是否详尽无遗。 如果可以做出这样的决定,则认为错误被处理。 否则,错误可以传播出包含范围,这意味着错误必须由封闭的catch子句处理,或者包含函数必须用throws声明。
为确保处理错误,请使用带有匹配所有错误(例如通配符模式(_))的模式的catch子句。 如果catch子句未指定模式,则catch子句会将任何错误与名为error的本地常量进行匹配并绑定。 有关可以在catch子句中使用的模式的更多信息,请参阅模式。
要查看如何在多个catch子句中使用do语句的示例,请参阅处理错误。
do-statement → do-code-block-catch-clauses-opt-
catch-clauses → catch-clause-catch-clauses-opt-
catch-clause → catch-pattern-opt-where-clause-opt-code-block-
编译器控制语句
编译器控制语句允许程序改变编译器行为的各个方面。 Swift有两个编译器控制语句:一个条件编译块和一个行控制语句。
compiler-control-statement → conditional-compilation-block-
compiler-control-statement → line-control-statement-
条件编译块
条件编译块允许代码根据一个或多个编译条件的值进行条件编译。
每个条件编译块都以#if编译指令开始,并以#endif编译指令结束。 一个简单的条件编译块具有以下形式:
#ifcompilation condition
statements
#endif
与if语句的条件不同,编译条件在编译时进行评估。 因此,仅当编译条件在编译时评估为true时才会编译和执行这些语句。
编译条件可以包括true和false布尔文字,与-D命令行标志一起使用的标识符,或下表中列出的任何平台条件。
Platform condition | Valid arguments |
os() | macOS, iOS, watchOS, tvOS, Linux |
arch() | i386, x86_64, arm, arm64 |
swift() | >= followed by a version number |
canImport() | A module name |
targetEnvironment() | simulator |
swift()平台条件的版本号由一个主号码,可选的次要号码,可选的补丁号码等组成,点(。)分隔版本号的每个部分。 > =和版本号之间不能有空格。
canImport()平台条件的参数是所有平台上可能不存在的模块的名称。此条件测试是否可以导入模块,但不实际导入它。如果模块存在,则平台条件返回true;否则,它返回false。
当为模拟器编译代码时,targetEnvironment()平台条件返回true;否则,它返回false。
注意
对于ARM 64器件,拱(臂)平台条件不会返回true。当为32位iOS模拟器编译代码时,arch(i386)平台条件返回true。
您可以使用逻辑运算符&&,||和!来组合编译条件。并使用括号进行分组。这些运算符与用于组合普通布尔表达式的逻辑运算符具有相同的关联性和优先级。
与if语句类似,您可以添加多个条件分支以测试不同的编译条件。您可以使用#elseif子句添加任意数量的附加分支。您还可以使用#else子句添加最终的附加分支。包含多个分支的条件编译块具有以下形式:
#if compilation condition 1
statements to compile if compilation condition 1 is true
#else if compilation condition 2
statements to compile if compilation condition 2 is true
#else
statements to compile if both compilation conditions are false
#endif
注意
即使未编译,也会解析条件编译块主体中的每条语句。 但是,如果编译条件包含swift()平台条件,则会出现异常:仅当编译器的Swift版本与平台条件中指定的内容匹配时才解析语句。 这个异常确保较旧的编译器不会尝试解析在较新版本的Swift中引入的语法。
conditional-compilation-block → if-directive-clause-elseif-directive-clauses-opt-else-directive-clause-opt-endif-directive-
if-directive-clause → if-directive-compilation-condition-statements-opt-
else if-directive-clauses → elseif-directive-clause-elseif-directive-clauses-opt-
else if-directive-clause → elseif-directive-compilation-condition-statements-opt-
else-directive-clause → else-directive-statements-opt-
if-directive → #if-
else if-directive → #elseif-
else-directive → #else-
endif-directive → #endif-
compilation-condition → platform-condition-
compilation-condition → identifier-
compilation-condition → boolean-literal-
compilation-condition → (-compilation-condition-)-
compilation-condition → !-compilation-condition-
compilation-condition → compilation-condition-&&-compilation-condition-
compilation-condition → compilation-condition-||-compilation-condition-
platform-condition → os-(-operating-system-)-
platform-condition → arch-(-architecture-)-
platform-condition → swift-(->=-swift-version-)-
platform-condition → canImport-(-module-name-)-
platform-condition → targetEnvironment-(-environment-)-
operating-system → macOS-iOS-watchOS-tvOS-
architecture → i386-x86_64-arm-arm64-
swift-version → decimal-digits-swift-version-continuation-opt-
swift-version-continuation → .-decimal-digits-swift-version-continuation-opt-
module-name → identifier-
environment → simulator-
行控制语句
行控制语句用于指定行号和文件名,该行号和文件名可以与正在编译的源代码的行号和文件名不同。 使用行控制语句来更改Swift使用的源代码位置,以进行诊断和调试。
行控制语句具有以下形式:
#sourceLocation(file: filename, line: line number)
#sourceLocation()
行控制语句的第一种形式会更改#line和#file文字表达式的值,从行控制语句后面的代码行开始。 行号更改#line的值,并且是大于零的任何整数字面值。 文件名改变#file的值,并且是一个字符串文字。
线控制语句的第二种形式#sourceLocation()将源代码位置重置为默认行编号和文件名
line-control-statement → #sourceLocation-(-file:-file-name-,-line:-line-number-)-
line-control-statement → #sourceLocation-(-)-
line-number → A decimal integer greater than zero
file-name → static-string-literal-
可用性条件
基于指定的平台参数,可用性条件用作if,while和guard语句的条件,以在运行时查询API的可用性。
可用性条件具有以下形式:
if #available(platform nameversion, ..., *) {
statements to execute if the APIs are available
} else {
fallback statements to execute if the APIs are unavailable
}
您使用可用性条件来执行代码块,具体取决于您要使用的API在运行时是否可用。 编译器在验证该代码块中的API可用时使用可用性条件中的信息。
可用性条件采用逗号分隔的平台名称和版本列表。 使用iOS,macOS,watchOS和tvOS作为平台名称,并包含相应的版本号。 *参数是必需的,并且指定在任何其他平台上,由可用性条件保护的代码块的主体在由目标指定的最小部署目标上执行。
与布尔条件不同,您不能使用逻辑运算符(如&&和||)组合可用性条件。
availability-condition → #available-(-availability-arguments-)-
availability-arguments → availability-argument-availability-argument-,-availability-arguments-
availability-argument → platform-name-platform-version-
availability-argument → *-
platform-name → iOS-iOSApplicationExtension-
platform-name → macOS-macOSApplicationExtension-
platform-name → watchOS-
platform-name → tvOS-
platform-version → decimal-digits-
platform-version → decimal-digits-.-decimal-digits-
platform-version → decimal-digits-.-decimal-digits-.-decimal-digits-