Swift 中有三种类型的语句:简单语句、编译器控制语句和控制流语句。简单语句最为常见,由表达式或声明组成。编译器控制语句允许程序更改编译器的某些行为,包括条件编译块和行控制语句。
控制流语句用于控制程序的执行流程。Swift 中有几种类型的控制流语句,包括循环语句、分支语句和控制转移语句。循环语句允许重复执行代码块,分支语句允许仅在满足特定条件时执行特定代码块,而控制转移语句则提供了一种改变代码执行顺序的方法。此外,Swift 还提供了 do 语句来引入作用域、捕获和处理错误,以及 defer 语句,用于在当前作用域退出之前运行清理操作。
分号( ; )可选地出现在任何语句之后,用于分隔出现在同一行的多个语句。
语句语法
语句 → 表达式 ; ?
语句 → 声明 ; ?
语句 → 循环语句 ; ?
语句 → 分支语句 ; ?
语句 → 标记语句 ; ?
语句 → 控制转移语句 ; ?
语句 → 延迟语句 ; ?
语句 → do 语句 ; ?
语句 → 编译器控制语句
语句 → 语句语句 ?
循环语句
循环语句允许根据循环中指定的条件重复执行代码块。Swift 有三种循环语句: for - in 语句、 while 语句和 repeat - while 语句。
循环语句中的控制流可以通过 break 语句和 continue 语句进行改变,如下文的 Break 语句和 Continue 语句中所述。
循环语句的语法
循环语句 → for-in 语句
循环语句 → while 语句
循环语句 → repeat-while 语句
For-In 语句
for - in 语句允许对符合 Sequence 协议的集合(或任何类型)中的每个项目执行一次代码块。
for - in 语句具有以下形式:
for <#item#> in <#collection#> {
<#statements#>
}
在集合表达式上调用 make Iterator() 方法,以获取迭代器类型的值——即遵循 Iterator Protocol 协议的类型。程序通过调用迭代器的 next() 方法开始执行循环。如果返回的值不是 nil ,则将其赋值给元素模式,程序执行语句 ,然后从循环的开头继续执行。否则,程序既不执行赋值,也不执行语句 ,并结束 for - in 语句的执行。
for-in 语句的语法
for-in 语句 → for case ? pattern in 表达式 where 子句 ? 代码块
While 语句
只要条件保持为真, while 语句就可以重复执行代码块。
while 语句具有以下形式:
while <#condition#> {
<#statements#>
}
while 语句的执行方式如下:
- 评估条件 。
- 如果为 true ,则执行继续执行步骤 2。如果为 false ,则程序已完成 while 语句的执行。
- 程序执行语句 ,然后执行返回到步骤 1。
由于条件的值在语句执行之前进行评估,因此 while 语句中的语句可以执行零次或多次。
条件的值必须是 Bool 类型,或者是可以桥接到 Bool 类型。条件也可以是可选绑定声明,如可选绑定中所述。
while 语句的语法
while 语句 → while 条件列表代码块
条件列表 → 条件 | 条件 , 条件列表
条件 → 表达式 | 可用性条件 | 案例条件 | 可选约束条件
案例条件 → case 模式初始化程序
可选绑定条件 → let 模式初始化器 ? | var 模式初始化器 ?
Repeat-While 语句
只要条件保持为真, repeat - while 语句就允许执行代码块一次或多次。
repeat - while 语句具有以下形式:
repeat {
<#statements#>
} while <#condition#>
repeat - while 语句的执行如下:
- 程序执行语句 ,并继续执行步骤 2。
- 评估条件 。
- 如果为 true ,则执行返回步骤 1。如果为 false ,则程序完成执行 repeat - while 语句。
因为条件的值是在语句执行之后评估的,所以 repeat - while 语句中的语句至少执行一次。
条件的值必须是 Bool 类型或桥接到 Bool 类型。
repeat-while 语句的语法
repeat-while 语句 → repeat 代码块 while 表达式
分支语句
分支语句允许程序根据一个或多个条件的值执行特定部分的代码。分支语句中指定的条件值控制程序的分支方式,从而控制执行哪个代码块。Swift 有三种分支语句: if 语句、 guard 语句和 switch 语句。
if 语句或 switch 语句中的控制流可以通过 break 语句进行更改,这将在下面的 Break 语句中讨论。
分支语句的语法
分支语句 → if 语句
分支语句 → 保护语句
分支语句 → switch 语句
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 语句的语法
if 语句 → if 条件列表代码块 else 子句 ?
else 子句 → else 代码块 | else if 语句
守卫声明
如果一个或多个条件不满足,则使用 guard 语句将程序控制转移出范围。
guard 语句具有以下形式:
guard <#condition#> else {
<#statements#>
}
guard 语句中任何条件的值都必须是 Bool 类型,或者可以桥接到 Bool 类型。条件也可以是可选绑定声明,如可选绑定中所述。
在 guard 语句条件中,从可选绑定声明中分配值的任何常量或变量都可以用于保护语句的其余封闭范围。
guard 语句的 else 子句是必需的,并且必须调用具有 Never 返回类型的函数,或者使用以下语句之一将程序控制转移到 guard 语句的封闭范围之外:
- return
- break
- continue
- throw
控制转移语句将在下文的控制转移语句中讨论。有关 Never 返回类型的函数的更多信息,请参阅 Never Return 的函数 。
保护语句的语法
保护语句 → guard 条件列表 else 代码块
Switch 语句
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 语句的控制表达式会被求值,然后与每个 case 中指定的模式进行比较。如果匹配成功,程序将执行该 case 作用域内列出的语句 。每个 case 的作用域不能为空。因此,每个 case 标签的冒号 ( : ) 后必须至少包含一个语句。如果您不打算执行匹配 case 主体中的任何代码,请使用单个 break 语句。
代码可以分支的表达式值非常灵活。例如,除了标量类型的值(例如整数和字符)之外,代码还可以分支任何类型的值,包括浮点数、字符串、元组、自定义类的实例以及可选项。 控制表达式的值甚至可以与枚举中 case 的值匹配,并检查其是否包含在指定的值范围内。有关如何在 switch 语句中使用这些不同类型的值的示例,请参阅控制流中的 Switch 。
switch case 可以在每个模式后选择性地包含一个 where 子句。where 子句由 where 关键字后跟一个表达式引入,用于在 case 中的模式被认为与控制表达式匹配之前提供附加条件。如果存在 where 子句,则仅当控制表达式的值与 case 的某个模式匹配且 where 子句的表达式计算结果为 true 时,才会执行相关 case 中的语句 。例如,在下面的示例中, 控制表达式仅当它是一个包含两个相同值元素的元组(例如 (1, 1) 时才与 case 匹配。
case let (x, y) where x == y:
如上例所示,case 中的模式也可以使用 let 关键字绑定常量(也可以使用 var 关键字绑定变量)。这些常量(或变量)可以在相应的 where 子句中以及 case 作用域内的其余代码中引用。如果 case 包含多个与控制表达式匹配的模式,则所有模式都必须包含相同的常量或变量绑定,并且每个绑定的变量或常量在 case 的所有模式中都必须具有相同的类型。
switch 语句还可以包含一个默认 case,由 default 关键字引入。只有当没有其他 case 与控制表达式匹配时,才会执行默认 case 中的代码。一个 switch 语句只能包含一个默认 case,并且必须出现在 switch 语句的末尾。
虽然模式匹配操作的实际执行顺序,尤其是案例中模式的求值顺序,是未指定的,但 switch 语句中的模式匹配的行为就像是按照源码顺序执行的——也就是说,按照它们在源代码中出现的顺序。因此,如果多个案例包含求值结果相同的模式,从而可以匹配控制表达式的值,则程序只会按照源码顺序执行第一个匹配案例中的代码。
Switch 语句必须详尽
在 Swift 中,控制表达式类型的每个可能值都必须与至少一个 case 模式的值匹配。如果这不可行(例如,当控制表达式的类型为 Int ),您可以添加一个默认 case 来满足要求。
切换未来的枚举案例
非冻结枚举是一种特殊的枚举,即使在您编译并发布应用之后,将来也可能会获得新的枚举用例。切换非冻结枚举需要额外考虑。当库的作者将某个枚举标记为非冻结时,他们保留添加新枚举用例的权利,并且与该枚举交互的任何代码都必须能够处理这些未来的用例而无需重新编译。在库演进模式下编译的代码、Swift 标准库中的代码、Apple 框架的 Swift 覆盖层以及 C 和 Objective-C 代码都可以声明非冻结枚举。有关冻结和非冻结枚举的信息,请参阅冻结 。
在切换非冻结枚举值时,即使枚举的每个分支都已具有对应的 switch 分支,也始终需要添加一个默认分支。您可以将 @unknown 属性应用于默认分支,这表示默认分支应仅匹配将来添加的枚举分支。如果默认分支与编译器已知的任何枚举分支匹配,Swift 会发出警告。此未来警告告知您,库作者在枚举中添加了一个没有对应 switch 分支的新分支。
以下示例切换了 Swift 标准库 Mirror .Ancestor Representation 枚举的所有三个现有分支。如果您将来添加其他分支,编译器会生成警告,提示您需要更新 switch 语句以涵盖新的分支。
let representation: Mirror.AncestorRepresentation = .generated
switch representation {
case .customized:
print("Use the nearest ancestor’s implementation.")
case .generated:
print("Generate a default mirror for all ancestor classes.")
case .suppressed:
print("Suppress the representation of all ancestor classes.")
@unknown default:
print("Use a representation that was unknown when this code was compiled.")
}
// Prints "Generate a default mirror for all ancestor classes."
执行不会隐式失败
匹配的 case 中的代码执行完毕后,程序将从 switch 语句退出。程序不会继续执行,也不会“跳转到”下一个 case 或默认 case。也就是说,如果您希望执行从一个 case 继续到下一个 case,请在您希望继续执行的 case 中显式添加 fallthrough 语句(该语句仅由 fallthrough 关键字组成)。有关 fallthrough 语句的更多信息,请参阅下文的 Fallthrough 语句 。
switch 语句的语法
switch 语句 → switch 表达式 { switch-cases ? }
开关盒 → 开关盒开关盒 ?
switch-case → case-label 语句
switch-case → default-label 语句
switch-case → 条件 switch-case
案例标签 → 属性 ? case 案例项目列表 :
案例项目列表 → 模式 where 子句 ? | 模式 where 子句 ? , 案例项目列表
默认标签 → 属性 ? default :
where 子句 → where where 表达式
where 表达式 → 表达式
条件 switch case → switch-if 指令子句 switch-elseif 指令子句 ? switch-else 指令子句 ? endif 指令
switch-if 指令子句 → if 指令编译条件 switch-case ?
switch-elseif 指令子句 → elseif 指令子句 switch-elseif 指令子句 ?
switch-elseif 指令子句 → elseif 指令编译条件 switch-cases ?
switch-else 指令子句 → else 指令 switch-cases ?
标记语句
您可以在循环语句、 if 语句、 switch 语句或 do 语句前添加语句标签,该标签由标签名称后紧跟冒号 (😃 组成。在 break 和 continue 语句中使用语句标签可以明确说明如何在循环语句或 switch 语句中更改控制流,如下文 Break 语句和 Continue 语句中所述。
带标签的语句的作用域是该语句标签之后的整个语句。标签语句可以嵌套,但每个语句标签的名称必须是唯一的。
有关更多信息以及如何使用语句标签的示例,请参阅控制流中的标记语句 。
标记语句的语法
标记语句 → 语句标签循环语句
标记语句 → 语句标签 if 语句
标签语句 → 语句标签 switch 语句
标记语句 → 语句标签 do 语句
语句标签 → 标签名称 :
标签名称 → 标识符
控制转移语句
控制转移语句可以无条件地将程序控制权从一段代码转移到另一段代码,从而改变程序中代码的执行顺序。Swift 有五种控制转移语句: break 语句、 continue 语句、 fallthrough 语句、 return 语句和 throw 语句。
控制转移语句的语法
控制转移语句 → break 语句
控制转移语句 → continue 语句
控制转移语句 → fallthrough 语句
控制转移语句 → 返回语句
控制转移语句 → throw 语句
Break 语句
break 语句用于结束程序中循环、 if 语句或 switch 语句 break break 组成,也可以由 break 关键字后跟语句标签名称组成,如下所示。
break
break <#label name#>
当 break 语句后跟语句标签的名称时,它将结束由该标签命名的循环、 if 语句或 switch 语句的程序执行。
如果 break 语句后没有语句标签的名称,它将结束程序中 switch 语句或其所在的最内层循环语句的执行。您不能使用未标记的 break 语句来跳出 if 语句。
在这两种情况下,程序控制都会转移到封闭循环或 switch 语句(如果有)后面的第一行代码。
有关如何使用 break 语句的示例,请参阅控制流中的 Break 和标记语句 。
break 语句的语法
中断语句 → break 标签名称 ?
Continue 语句
continue 语句会结束程序对循环语句当前迭代的执行,但不会停止循环语句本身 continue 执行。continue 语句可以仅由 continue 关键字组成,也可以由 continue 关键字后跟语句标签名称组成,如下所示。
continue
continue <#label name#>
当 continue 语句后跟语句标签的名称时,它将结束由该标签命名的循环语句的当前迭代的程序执行。
当 continue 语句后面没有语句标签的名称时,它将结束程序对其所在的最内层循环语句的当前迭代的执行。
在这两种情况下,程序控制都会转移到封闭循环语句的条件。
在 for 语句中,在执行 continue 语句后仍会评估增量表达式,因为增量表达式是在执行循环体之后评估的。
有关如何使用 continue 语句的示例,请参阅控制流中的 Continue 和标记语句 。
continue 语句的语法
continue 语句 → continue 标签名称 ?
Fallthrough 语句
fallthrough 语句由 fallthrough 关键字组成,并且仅出现在 switch 语句的 case 块中。fallthrough fallthrough 使程序继续执行 switch 语句中的一个 case 语句。即使 case 标签的模式与 switch 语句的控制表达式的值不匹配,程序也会继续执行下一个 case 语句。
fallthrough 语句可以出现在 switch 语句中的任何位置,而不仅仅是作为 case 块的最后一条语句,但它不能在最后一个 case 块中使用。它也不能将控制权转移到模式中包含值绑定模式的 case 块中。
有关如何在 switch 语句中使用 fallthrough 语句的示例,请参阅控制流中的控制转移语句 。
fallthrough 语句的语法
fallthrough 语句 → fallthrough
退货声明
return 语句出现在函数或方法定义的主体中,使程序执行返回到调用函数或方法。程序将从函数或方法调用之后的位置继续执行。
return 语句可以仅由 return 关键字组成,也可以由 return 关键字后跟一个表达式组成,如下所示。
return
return <#expression#>
当 return 语句后跟一个表达式时,表达式的值将返回给调用函数或方法。如果表达式的值与函数或方法声明中声明的返回类型的值不匹配,则表达式的值会先转换为返回类型,然后再返回给调用函数或方法。
笔记
如可失败初始化器中所述,可在可失败初始化器中使用 return 语句的特殊形式( return nil )来指示初始化失败。
当 return 语句后面没有表达式时,它只能用于从不返回值的函数或方法中返回(即,当函数或方法的返回类型为 Void 或 () 时)。
返回语句的语法
返回语句 → return 表达式 ?
Throw 语句
throw 语句出现在抛出异常的函数或方法的主体内,或者出现在类型以 throws 关键字标记的闭包表达式的主体内。
throw 语句会导致程序结束当前作用域的执行,并开始将错误传播到其封闭作用域。抛出的错误会持续传播,直到被 do 语句的 catch 子句处理。
throw 语句由 throw 关键字和后跟表达式组成,如下所示。
throw <#expression#>
表达式的值必须是遵循 Error 协议的类型。如果包含 throw 语句的 do 语句或函数声明了其抛出的错误类型,则表达式的值必须是该类型的实例。
有关如何使用 throw 语句的示例,请参阅错误处理中的使用抛出函数传播错误 。
throw 语句的语法
throw 语句 → throw 表达式
Defer 语句
defer 语句用于在将程序控制转移到 defer 语句出现的范围之外之前执行代码。
defer 语句具有以下形式:
defer {
<#statements#>
}
无论程序控制权如何转移, defer 语句中的语句都会执行。这意味着 defer 语句可以用来执行手动资源管理(例如关闭文件描述符),以及执行即使抛出错误也需要执行的操作。
defer 语句中的语句在包含 defer 语句的范围末尾执行。
func f(x: Int) {
defer { print("First defer") }
if x < 10 {
defer { print("Second defer") }
print("End of if")
}
print("End of function")
}
f(x: 5)
// Prints "End of if"
// Prints "Second defer"
// Prints "End of function"
// Prints "First defer"
在上面的代码中, if 语句中的 defer 在函数 f 中声明的 defer 之前执行,因为 if 语句的作用域在函数作用域之前结束。
如果同一作用域中出现多个 defer 语句,则它们的出现顺序与执行顺序相反。首先执行给定作用域中的最后一个 defer 语句意味着该最后一个 defer 语句内的语句可以引用将被其他 defer 语句清理的资源。
func f() {
defer { print("First defer") }
defer { print("Second defer") }
print("End of function")
}
f()
// Prints "End of function"
// Prints "Second defer"
// Prints "First defer"
defer 语句中的语句不能将程序控制转移到 defer 语句之外。
defer 语句的语法
延迟语句 → defer 代码块
Do 语句
do 语句用于引入新的作用域,并且可以选择性地包含一个或多个 catch 子句,这些子句包含与定义的错误条件匹配的模式。在 do 语句作用域中声明的变量和常量只能在该作用域内访问。
Swift 中的 do 语句类似于 C 中用于分隔代码块的花括号 ( {} ),并且不会在运行时产生性能成本。
do 语句具有以下形式:
do {
try <#expression#>
<#statements#>
} catch <#pattern 1#> {
<#statements#>
} catch <#pattern 2#> where <#condition#> {
<#statements#>
} catch <#pattern 3#>, <#pattern 4#> where <#condition#> {
<#statements#>
} catch {
<#statements#>
}
do 语句可以选择指定其引发的错误类型,其形式如下:
do throws(<#type#>) {
try <#expression#>
} catch <#pattern> {
<#statements#>
} catch {
<#statements#>
}
如果 do 语句包含 throws 子句,则 do 代码块只能抛出指定类型的错误。该类型必须是遵循 Error 协议的具体类型、遵循 Error 协议的不透明类型,或者是装箱协议类型 any Error 。如果 do 语句未指定抛出的错误类型,Swift 会按如下方式推断错误类型:
- 如果 do 代码块中的每个 throws 语句和 try 表达式都嵌套在详尽的错误处理机制中,则 Swift 推断 do 语句不会抛出错误。
- 如果 do 代码块包含的代码在详尽的错误处理之外仅抛出单一类型的错误(除了抛出 Never ),那么 Swift 会推断 do 语句会抛出该具体的错误类型。
- 如果 do 代码块包含抛出详尽错误处理之外的多种类型的错误的代码,则 Swift 推断 do 语句会抛出 any Error 。
有关处理具有明确类型的错误的更多信息,请参阅指定错误类型 。
如果 do 代码块中的任何语句抛出错误,程序控制权将转移到第一个与错误模式匹配的 catch 子句。如果所有子句均不匹配,则错误将传播到周围的作用域。如果顶层未处理错误,程序执行将停止并出现运行时错误。
与 switch 语句类似,编译器会尝试推断 catch 子句是否已完全处理。如果能够完全处理,则认为错误已得到处理。否则,错误可能会超出其所属作用域,这意味着错误必须由其所属的 catch 子句处理,或者其所属函数必须使用 throws 声明。
如果 catch 子句包含多个模式,则只要其中任何一个模式匹配错误,该 catch 子句就会匹配该错误。如果一个 catch 子句包含多个模式,则所有模式必须包含相同的常量或变量绑定,并且每个绑定的变量或常量在该 catch 子句的所有模式中都必须具有相同的类型。
为确保错误得到处理,请使用 catch 子句,其中包含与所有错误匹配的模式,例如通配符模式 ( _ )。如果 catch 子句未指定模式,则 catch 子句会匹配任何错误并将其绑定到名为 error 的局部常量。有关可在 catch 子句中使用的模式的更多信息,请参阅模式 。
要查看如何使用带有多个 catch 子句的 do 语句的示例,请参阅处理错误 。
do 语句的语法
do 语句 → do throws 子句 ? 代码块 catch 子句 ?
catch 子句 → catch 子句 catch 子句 ?
catch 子句 → catch catch 模式列表 ? 代码块
捕获模式列表 → 捕获模式 | 捕获模式 , 捕获模式列表
捕获模式 → 模式 where 子句 ?
编译器控制语句
编译器控制语句允许程序更改编译器的某些行为。Swift 有三种编译器控制语句:条件编译块、行控制语句和编译时诊断语句。
编译器控制语句的语法
编译器控制语句 → 条件编译块
编译器控制语句 → 行控制语句
编译器控制语句 → 诊断语句
条件编译块
条件编译块允许根据一个或多个编译条件的值有条件地编译代码。
每个条件编译块都以 #if 编译指令开头,以 #endif 编译指令结尾。 一个简单的条件编译块具有以下形式:
#if <#compilation condition#>
<#statements#>
#endif
与 if 语句的条件不同, 编译条件在编译时进行评估。因此,只有当编译条件在编译时评估为 true , 语句才会被编译和执行。
编译条件可以包括 true 和 false 布尔文字、与 -D 命令行标志一起使用的标识符或下表列出的任何平台条件。
平台状况
有效参数 | 说明 |
---|---|
os() | mac OS , i OS , watch OS , tv OS , vision OS , Linux , Windows |
arch() | i386 、 x86 _64 、 arm 、 arm64 |
swift() | >= 或 < 后跟版本号 |
compiler() | >= 或 < 后跟版本号 |
canImport() | 模块名称 |
targetEnvironment() | simulator , mac Catalyst |
swift() 和 compiler() 平台条件的版本号由主版本号、可选的次版本号、可选的补丁号等组成,版本号各部分之间用点 ( . ) 分隔。比较运算符和版本号之间不能有空格。compiler compiler() 的版本号是编译器的版本号,与传递给编译器的 Swift 版本设置无关。swift swift() 的版本号是当前正在编译的语言版本。例如,如果您在 Swift 4.2 模式下使用 Swift 5 编译器编译代码,则编译器版本为 5,语言版本为 4.2。使用这些设置,以下代码将打印所有三条消息:
#if compiler(>=5)
print("Compiled with the Swift 5 compiler or later")
#endif
#if swift(>=4.2)
print("Compiled in Swift 4.2 mode or later")
#endif
#if compiler(>=5) && swift(<5)
print("Compiled with the Swift 5 compiler or later in a Swift mode earlier than 5")
#endif
// Prints "Compiled with the Swift 5 compiler or later"
// Prints "Compiled in Swift 4.2 mode or later"
// Prints "Compiled with the Swift 5 compiler or later in a Swift mode earlier than 5"
can Import() 平台条件的参数是模块的名称,该模块可能并非在所有平台上都存在。该模块的名称中可以包含句点 ( . )。此条件测试是否可以导入该模块,但实际上并不导入它。如果模块存在,则平台条件返回 true ;否则,返回 false 。
当针对指定环境编译代码时, target Environment() 平台条件返回 true ;否则返回 false 。
笔记
对于 ARM 64 设备, arch(arm) 平台条件不会返回 true 。当针对 32 位 iOS 模拟器编译代码时, arch(i386) 平台条件会返回 true 。
您可以使用逻辑运算符 && 、 || 和 ! 来组合和否定编译条件,并使用括号进行分组。这些运算符与用于组合普通布尔表达式的逻辑运算符具有相同的结合性和优先级。
与 if 语句类似,您可以添加多个条件分支来测试不同的编译条件。您可以使用 #elseif 子句添加任意数量的附加分支。您还可以使用 #else 子句添加最后一个附加分支。包含多个分支的条件编译块具有以下形式:
#if <#compilation condition 1#>
<#statements to compile if compilation condition 1 is true#>
#elseif <#compilation condition 2#>
<#statements to compile if compilation condition 2 is true#>
#else
<#statements to compile if both compilation conditions are false#>
#endif
笔记
即使未经编译,条件编译块主体中的每个语句也会被解析。但是,如果编译条件包含 swift() 或 compiler() 平台条件,则存在例外:只有当语言或编译器版本与平台条件中指定的版本匹配时,才会解析这些语句。此例外确保旧版编译器不会尝试解析新版 Swift 中引入的语法。
有关如何在条件编译块中包装显式成员表达式的信息,请参阅显式成员表达式 。
条件编译块的语法
条件编译块 → if 指令子句 elseif 指令子句 ? else 指令子句 ? endif 指令
if 指令子句 → if 指令编译条件语句 ?
elseif 指令子句 → elseif 指令子句 elseif 指令子句 ?
elseif 指令子句 → elseif 指令编译条件语句 ?
else 指令子句 → else 指令语句 ?
if 指令 → #if
elseif 指令 → #elseif
else 指令 → #else
endif 指令 → #endif
编译条件 → 平台条件
编译条件 → 标识符
编译条件 → 布尔字面量
编译条件 → ( 编译条件 )
编译条件 → ! 编译条件
编译条件 → 编译条件 && 编译条件
编译条件 → 编译条件 || 编译条件
平台条件 → os ( 操作系统 )
平台条件 → arch ( 架构 )
平台条件 → swift ( >= swift 版本 ) | swift ( < swift 版本 )
平台条件 → compiler ( >= swift-version ) | compiler ( < swift-version )
平台条件 → can Import ( 导入路径 )
平台条件 → target Environment ( environment )
操作系统 → mac OS | i OS | watch OS | tv OS | vision OS | Linux | Windows
架构 → i386 | x86 _64 | arm | arm64
swift 版本 → 十进制数字 swift 版本延续 ?
swift-版本延续 → . 十进制数字 swift-版本延续 ?
环境 → simulator | mac Catalyst
线路控制语句
行控制语句用于指定行号和文件名,这些行号和文件名可以与正在编译的源代码的行号和文件名不同。使用行控制语句可以更改 Swift 用于诊断和调试目的的源代码位置。
行控制语句具有以下形式:
#sourceLocation(file: <#file path#>, line: <#line number#>)
#sourceLocation()
行控制语句的第一种形式从行控制语句后面的代码行开始,更改 #line 、 #file 、 #file ID 和 #file Path 文字表达式的值。 行号更改 #line 的值,并且是任何大于零的整数文字。 文件路径更改 #file 、 #file ID 和 #file Path 的值,并且是字符串文字。指定的字符串将成为 #file Path 的值,字符串的最后一个路径组件由 #file ID 的值使用。有关 #file 、 #file ID 和 #file Path 的信息,请参阅文字表达式 。
行控制语句的第二种形式 #source Location() 将源代码位置重置回默认行号和文件路径。
行控制语句的语法
行控制语句 → #source Location ( file: 文件路径 , line: 行号 )
行控制语句 → #source Location ( )
行号 → 大于零的十进制整数
文件路径 → 静态字符串文字
编译时诊断语句
在 Swift 5.9 之前, #warning 和 #error 语句会在编译期间发出诊断信息。现在,此行为由 Swift 标准库中的 warning(😃 和 error(😃 宏提供。
可用条件
可用性条件用作 if 、 while 和 guard 语句的条件,根据指定的平台参数查询运行时 API 的可用性。
可用性条件具有以下形式:
if #available(<#platform name#> <#version#>, <#...#>, *) {
<#statements to execute if the APIs are available#>
} else {
<#fallback statements to execute if the APIs are unavailable#>
}
您可以使用可用性条件来执行代码块,具体取决于您要使用的 API 在运行时是否可用。编译器在验证该代码块中的 API 是否可用时,会使用可用性条件中的信息。
可用性条件采用逗号分隔的平台名称和版本列表。平台名称请使用 i OS 、 mac OS 、 watch OS 、 tv OS 和 vision OS ,并包含相应的版本号。 * 参数是必需的,用于指定在任何其他平台上,可用性条件所保护的代码块主体都将在目标指定的最低部署目标上执行。
与布尔条件不同,您不能使用 && 和 || 等逻辑运算符来组合可用性条件。不要使用 ! 来否定可用性条件,而是使用不可用性条件,其形式如下:
if #unavailable(<#platform name#> <#version#>, <#...#>) {
<#fallback statements to execute if the APIs are unavailable#>
} else {
<#statements to execute if the APIs are available#>
}
#unavailable 形式是一种语法糖,用于否定条件。在不可用条件下, * 参数是隐式的,不得包含。其含义与可用条件下的 * 参数相同。
可用性条件的语法
可用性条件 → #available ( 可用性参数 )
可用性条件 → #unavailable ( 可用性参数 )
可用性参数 → 可用性参数 | 可用性参数 , 可用性参数
可用性参数 → 平台名称平台版本
可用性参数 → *
平台名称 → i OS | i OSApplication Extension
平台名称 → mac OS | mac OSApplication Extension
平台名称 → mac Catalyst | macCatalystApplicationExtension
平台名称 → watch OS | watch OSApplication Extension
平台名称 → tv OS | tv OSApplication Extension
平台名称 → vision OS | vision OSApplication Extension
平台版本 → 十进制数字
平台版本 → 十进制数字 . 十进制数字
平台版本 → 十进制数字 . 十进制数字 . 十进制数字