Comment on page
声明
声明(declaration) 用以向程序里引入新的名字或者结构。举例来说,可以使用声明来引入函数和方法,变量和常量,或者定义新的具有命名的枚举、结构体、类和协议类型。还可以使用声明来扩展一个既有的具有命名的类型的行为,或者在程序里引入在其它地方声明的符号。
在 Swift 中,大多数声明在某种意义上讲也是定义,因为它们在声明时往往伴随着实现或初始化。由于协议并不提供实现,大多数协议成员仅仅只是声明而已。为了方便起见,也是因为这些区别在 Swift 中并不是很重要,“声明”这个术语同时包含了声明和定义两种含义。
声明语法
Swift 的源文件中的顶级代码(top-level code)由零个或多个语句、声明和表达式组成。默认情况下,在一个源文件的顶层声明的变量,常量和其他具有命名的声明可以被同模块中的每一个源文件中的代码访问。可以使用一个访问级别修饰符来标记声明来覆盖这种默认行为,请参阅 访问控制级别。
顶级声明语法
代码块(code block) 可以将一些声明和控制结构体组织在一起。它有如下的形式:
{
语句
}
代码块中的“语句”包括声明、表达式和各种其他类型的语句,它们按照在源码中的出现顺序被依次执行。
代码块语法
导入声明(import declaration) 让你可以使用在其他文件中声明的内容。导入语句的基本形式是导入整个模块,它由
import
关键字和紧随其后的模块名组成:import 模块
可以对导入操作提供更细致的控制,如指定一个特殊的子模块或者指定一个模块或子模块中的某个声明。提供了这些限制后,在当前作用域中,只有被导入的符号是可用的,而不是整个模块中的所有声明。
import 导入类型 模块.符号名
import 模块.子模块
导入声明语法
导入类型 → typealias | struct | class | enum | protocol | let | var | func
常量声明(constant declaration) 可以在程序中引入一个具有命名的常量。常量以关键字
let
来声明,遵循如下格式:let 常量名称: 类型 = 表达式
常量声明在“常量名称”和用于初始化的“表达式”的值之间定义了一种不可变的绑定关系;当常量的值被设定之后,它就无法被更改。这意味着,如果常量以类对象来初始化,对象本身的内容是可以改变的,但是常量和该对象之间的绑定关系是不能改变的。
当一个常量被声明为全局常量时,它必须拥有一个初始值。在函数或者方法中声明一个常量时,它并不需要拥有一个初始值,只需要保证在第一次对其进行读操作之前为其设置一个值。在类或者结构体中声明一个常量时,它将作为常量属性(constant property)。常量声明不能是计算型属性,因此也没有存取方法。
如果常量名称是元组形式,元组中每一项的名称都会和初始化表达式中对应的值进行绑定。
let (firstNumber, secondNumber) = (10, 42)
在上例中,
firstNumber
是一个值为 10
的常量,secnodeName
是一个值为 42
的常量。所有常量都可以独立地使用:print("The first number is \(firstNumber).")
// 打印“The first number is 10.”
print("The second number is \(secondNumber).")
// 打印“The second number is 42.”
声明一个常量类型属性要使用
static
声明修饰符。类的常量类型属性总是隐式地被标记为 final
;你无法用 class
或 final
声明修饰符实现允许或禁止被子类重写的目的。类型属性在 类型属性 中有介绍。常量声明语法
变量声明(variable declaration) 可以在程序中引入一个具有命名的变量,它以关键字
var
来声明。变量声明有几种不同的形式,可以声明不同种类的命名值和可变值,如存储型和计算型变量和属性,属性观察器,以及静态变量属性。所使用的声明形式取决于变量声明的适用范围和打算声明的变量类型。
注意
使用如下形式声明一个存储型变量或存储型变量属性:
var 变量名称: 类型 = 表达式
可以在全局范围,函数内部,或者在类和结构体的声明中使用这种形式来声明一个变量。当变量以这种形式在全局范围或者函数内部被声明时,它代表一个存储型变量。当它在类或者结构体中被声明时,它代表一个存储型变量属性(stored variable property)。
用于初始化的表达式不可以在协议的声明中出现,在其他情况下,该表达式是可选的。如果没有初始化表达式,那么变量声明必须包含类型注解(
:
type)。如同常量声明,如果变量名称是元组形式,元组中每一项的名称都会和初始化表达式中对应的值进行绑定。
正如名字所示,存储型变量和存储型变量属性的值会存储在内存中。
使用如下形式声明一个计算型变量或计算型属性:
var 变量名称: 类型 {
get {
语句
}
set(setter 名称) {
语句
}
}
可以在全局范围、函数内部,以及类、结构体、枚举、扩展的声明中使用这种形式的声明。当变量以这种形式在全局范围或者函数内部被声明时,它表示一个计算型变量。当它在类、结构体、枚举、扩展声明的上下文中被声明时,它表示一个计算型属性(computed property)。
getter 用来读取变量值,setter 用来写入变量值。setter 子句是可选的,getter 子句是必须的。不过也可以将这些子句都省略,直接返回请求的值,正如在 只读计算型属性 中描述的那样。但是如果提供了一个 setter 子句,就必须也提供一个 getter 子句。
setter 的圆括号以及 setter 名称是可选的。如果提供了 setter 名称,它就会作为 setter 的参数名称使用。如果不提供 setter 名称,setter 的参数的默认名称为
newValue
,正如在 便捷 setter 声明 中描述的那样。与存储型变量和存储型属性不同,计算型变量和计算型属性的值不存储在内存中。
可以在声明存储型变量或属性时提供
willSet
和 didSet
观察器。一个包含观察器的存储型变量或属性以如下形式声明:var 变量名称: 类型 = 表达式 {
willSet(setter 名称) {
语句
}
didSet(setter 名称) {
语句
}
}
可以在全局范围、函数内部,或者类、结构体的声明中使用这种形式的声明。当变量以这种形式在全局范围或者函数内部被声明时,观察器表示一个存储型变量观察器。当它在类和结构体的声明中被声明时,观察器表示一个属性观察器。
用于初始化的表达式在类或者结构的声明中是可选的,但是在其他声明中则是必须的。如果可以从初始化表达式中推断出类型信息,那么可以不提供类型注解。
当变量或属性的值被改变时,
willSet
和 didSet
观察器提供了一种观察方法。观察器会在变量的值被改变时调用,但不会在初始化时被调用。willSet
观察器只在变量或属性的值被改变之前调用。新的值作为一个常量传入 willSet
观察器,因此不可以在 willSet
中改变它。didSet
观察器在变量或属性的值被改变后立即调用。和 willSet
观察器相反,为了方便获取旧值,旧值会传入 didSet
观察器。这意味着,如果在变量或属性的 didiset
观察器中设置值,设置的新值会取代刚刚在 willSet
观察器中传入的那个值。在
willSet
和 didSet
中,圆括号以及其中的 setter 名称是可选的。如果提供了一个 setter 名称,它就会作为 willSet
和 didSet
的参数被使用。如果不提供 setter 名称,willSet
观察器的默认参数名为 newValue
,didSet
观察器的默认参数名为 oldValue
。提供了
willSet
时,didSet
是可选的。同样的,提供了 didSet
时,willSet
则是可选的。变量声明语法
类型别名(type alias) 声明可以在程序中为一个既有类型声明一个别名。类型别名声明语句使用关键字
typealias
声明,遵循如下的形式:typealias 类型别名 = 现存类型
当声明一个类型的别名后,可以在程序的任何地方使用“别名”来代替现有类型。现有类型可以是具有命名的类型或者混合类型。类型别名不产生新的类型,它只是使用别名来引用现有类型。
类型别名声明可以通过泛型参数来给一个现有泛型类型提供名称。类型别名为现有类型的一部分或者全部泛型参数提供具体类型。例如:
typealias StringDictionary<Value> = Dictionary<String, Value>
// 下列两个字典拥有同样的类型
var dictionary1: StringDictionary<Int> = [:]
var dictionary2: Dictionary<String, Int> = [:]
当一个类型别名带着泛型参数一起被声明时,这些参数的约束必须与现有参数的约束完全匹配。例如:
typealias DictionaryOfInts<Key: Hashable> = Dictionary<Key, Int>
因为类型别名可以和现有类型 相互交换使用,类型别名不可以引入额外的类型约束。
如果在声明处省略所有泛型参数,一个类型别名可以传递已有类型的所有泛型参数。例如,此处声明的
Diccionario
类型别名拥有和 Dictionary
同样的约束和泛型参数。typealias Diccionario = Dictionary
在协议声明中,类型别名可以为那些经常使用的类型提供一个更短更方便的名称,例如:
protocol Sequence {
associatedtype Iterator: IteratorProtocol
typealias Element = Iterator.Element
}
func sum<T: Sequence>(_ sequence: T) -> Int where T.Element == Int {
// ...
}
假如没有类型别名,sum 函数将必须引用关联类型通过 T.Iterator.Element 的形式来替代 T.Element。
类型别名声明语法
使用函数声明(function declaration) 在程序中引入新的函数或者方法。在类、结构体、枚举,或者协议中声明的函数会作为方法。函数声明使用关键字
func
,遵循如下的形式:func 函数名称(参数列表) -> 返回类型 {
语句
}
如果函数返回
Void
类型,返回类型可以省略,如下所示:func 函数名称(参数列表) {
语句
}
函数声明中语句只包含一个表达式,可以理解为返回该表达式的值。
函数可以使用元组类型作为返回类型来返回多个值。
函数定义可以出现在另一个函数声明内。这种函数被称作嵌套函数(nested function)。
大多数时候,嵌套函数都是可逃逸的函数。仅当一个嵌套函数捕获了某个确保了永不逃逸的值——例如一个输入输出参数——或者传入一个非逃逸函数参数的时候,这个嵌套函数才是非逃逸的。
函数的参数列表由一个或多个函数参数组成,参数间以逗号分隔。函数调用时的参数顺序必须和函数声明时的参数顺序一致。最简单的参数列表有着如下的形式:
参数名称
: 参数类型
每个参数有一个参数名称,这个名称与实参标签一样都可以在函数体内被使用。默认情况下,参数名也会被作为实参标签来使用。例如:
func f(x: Int, y: Int) -> Int { return x + y }
f(x: 1, y: 2) // 参数 x 和 y 都有标签
可以按照如下两种形式之一,重写参数名称的默认行为:
实参标签
参数名称
: 参数类型
_ 参数名称
: 参数类型
在参数名称前的名称会作为这个参数的显式实参标签,它可以和参数名称不同。在函数或方法调用时,相对应的参数必须使用这个实参标签。
参数名称前的下划线(
_
)可以去除参数的实参标签。在函数或方法调用时,相对应的参数必须去除标签。func repeatGreeting(_ greeting: String, count n: Int) { /* Greet n times */ }
repeatGreeting("Hello, world!", count: 2) // count 有标签, greeting 没有
输入输出参数被传递时遵循如下规则:
- 1.函数调用时,参数的值被拷贝。
- 2.函数体内部,拷贝后的值被修改。
- 3.函数返回后,拷贝后的值被赋值给原参数。
这种行 为被称为拷入拷出(copy-in copy-out) 或值结果调用(call by value result)。例如,当一个计算型属性或者一个具有属性观察器的属性被用作函数的输入输出参数时,其 getter 会在函数调用时被调用,而其 setter 会在函数返回时被调用。
作为一种优化手段,当参数值存储在内存中的物理地址时,在函数体内部和外部均会使用同一内存位置。这种优化行为被称为引用调用(call by reference),它满足了拷入拷出模式的所有要求,且消除了复制带来的开销。在代码中,要规范使用拷入拷出模式,不要依赖于引用调用。
不要使用传递给输入输出参数的值,即使原始值在当前作用域中依然可用。当函数返回时,你对原始值所做的更改会被拷贝的值所覆盖。不要依赖于引用调用的优化机制来试图避免这种覆盖。
不能将同一个值传递给多个输入输出参数,因为这种情况下的拷贝与覆盖行为的顺序是不确定的,因此原始值的最终值也将无法确定。
如果一个闭包或者嵌套函数捕获了一个输入输出参数,那么这个闭包或者嵌套函数必须是非逃逸的。如果你需要捕获一个输入输出参数,但并不对其进行修改,那么你可以使用捕获列表来显式地表明这是个不可变捕获。
func someFunction(a: inout Int) -> () -> Int {
return { [a] in return a + 1 }
}
如果你需要捕获并修改一个输入输出参数,使用一个显式局部拷贝来进行修改操作,在一些例如多线程的场景中,这样做可以确保函数返回之前所有的修改都已完成。
如果嵌套函数在外层函数返回后才调用,嵌套函数对输入输出参数造成的任何改变将不会影响到原始值。例如:
func multithreadedFunction(queue: DispatchQueue, x: inout Int) {
// 创建一个局部拷贝并在适当时候手动拷贝回去
var localX = x
defer { x = localX }
// 并行地操作 localX,然后在函数返回前一直等待
queue.async { someMutatingOperation(&localX) }
queue.sync {}
}
参数可以被忽略,数量可以不固定,还可以为其提供默认值,使用形式如下:
_ : 参数类型
参数名称: 参数类型...
参数名称: 参数类型 = 默认参数值
以下划线(
_
)命名的参数会被显式忽略,无法在函数内使用。一个参数的基本类型名称如果紧跟着三个点(
...
),会被视为可变参数。紧随在可变参数后的参数必须拥有参数标签。一个函数可以拥有多个可变参数。可变参数会作为包含该参数类型元素的数组处理。举例来讲,可变参数 Int...
会作为 [Int]
来处理。关于使用可变参数的例子,请参阅 可变参数。如果在参数类型后面有一个以等号(
=
)连接的表达式,该参数会拥有默认值,即给定表达式的值。当函数被调用时,给定的表达式会被求值。如果参数在函数调用时被省略了,就会使用其默认值。func f(x: Int = 42) -> Int { return x }
f() // 有效,使用默认值
f(x: 7) // 有效,使用传入的值
f(7) // 无效,缺少参数标签
枚举或结构体的方法如果会修改
self
,则必须以 mutating
声明修饰符标记。子类重写超类中的方法必须以
override
声明修饰符标记。重写方法时不使用 override
修饰符,或者被 override
修饰符修饰的方法并未对超类方法构成重写,都会导致编译错误。枚举或者结构体中的类型方法,要以
static
声明修饰符标记,而对于类中的类型方法,除了使用 static
,还可使用 class
声明修饰符标记。类中使用 class
声明修饰的方法可以被子类实现重写;类中使用 class final
或 static
声明修饰的方法不可被重写。一些含有特殊名称的方法允许使用函数调用语法糖。如果一个类型定义了某个此类型的方法,那这些类型的实例对象都可以使用函数调用语法。这些函数调用会被解析为某个具有特殊名称的实例方法调用。
如同 dynamicCallable 中描述的一样,只要定义了
dynamicallyCall(withArguments:)
方法或者 dynamicallyCall(withKeywordArguments:)
方法,一个类、结构体或者枚举类型都支持函数调用语法。同时如下面的描述一样,定义了一个函数调用方法(call-as-function method)也可以达到上述效果。如果一个类 型同时定义了一个函数调用方法和使用 dynamicCallable
属性的方法,那么在合适的情况下,编译器会优先使用函数调用方法。函数调用方法的名称是
callAsFunction()
,或者任意一个以 callAsFunction(
开头并跟随着一些已标签化或未标签化的参数——例如 callAsFunction(_:_:)
和 callAsFunction(something:)
都是合法的函数调用方法名称。如下的函数调用是相同的:
struct CallableStruct {
var value: Int
func callAsFunction(_ number: Int, scale: Int) {
print(scale * (number + value))
}
}
let callable = CallableStruct(value: 100)
callable(4, scale: 2)
callable.callAsFunction(4, scale: 2)
// 两次函数调用都会打印 208
函数调用方法和使用
dynamicCallable
属性定义的方法在编码到类型系统中的信息量和运行时的动态行为能力上会有些差别。当你定义了一个函数调用方法时,你需要指定参数的数量,以及每个参数的类型和标签。与此不同的是,dynamicCallable
属性定义的方法只需要指定用于承载参数的数组类型。函数调用方法或
dynamicCallable
属性定义的方法并不允许你在任何上下文中把实例对象作为函数类型来处理。示例代码如下:let someFunction1: (Int, Int) -> Void = callable(_:scale:) // Error
let someFunction2: (Int, Int) -> Void = callable.callAsFunction(_:scale:)
可以抛出错误的函数或方法必须使用
throws
关键字标记。这类函数和方法被称为抛出函数和抛出方法。它们有着下面的形式:func 函数名称(参数列表) throws -> 返回类型 {
语句
}
抛出函数或抛出方法的调用必须包裹在
try
或者 try!
表达式中(也就是说,在作用域内使用 try
或者 try!
运算符)。throws
关键字是函数的类型的一部分,非抛出函数是抛出函数的子类型。所以,可以在使用抛出函数的地方使用非抛出函数。不能仅基于函数能否抛出错误来进行函数重写。也就是说,可以基于函数的函数类型的参数能否抛出错误来进行函数重写。
抛出方法不能重写非抛出方法,而且抛出方法不能满足协议对于非抛出方法的要求。也就是说,非抛出方法可以重写抛出方法,而且非抛出方法可以满足协议对于抛出方法的要求。
函数或方法可以使用
rethrows
关键字来声明,从而表明仅当该函数或方法的一个函数类型的参数抛出错误时,该函数或方法才抛出错误。这类函数和方法被称为重抛函数和重抛方法。重新抛出错误的函数或方法必须至少有一个参数的类型为抛出函数。func someFunction(callback: () throws -> Void) rethrows {
try callback()
}
重抛函数或者方法不能够从自身直接抛出任何错误,这意味着它不能够包含
throw
语句。它只能够传递作为参数的抛出函数所抛出的错误。例如,在 do-catch
语句中调用抛出函数,并在 catch
子句中抛出其它错误都是不允许的。func alwaysThrows() throws {
throw SomeError.error
}
func someFunction(callback: () throws -> Void) rethrows {
do {
try callback()
try alwaysThrows() // 非法, alwaysThrows() 不是一个抛出函数类型的参数
} catch {
throw AnotherError.error
}
}
抛出方法不能重写重抛方法,而且抛出方法不能满足协议对于重抛方法的要求。也就是说,重抛方法可以重写抛出方法,而且重抛方法可以满足对抛出方法的协议要求。
异步运行的函数和方法必须用
async
关键字标记。这些函数和方法称为异步函数和异步方法。它们有着下面的形式:func 函数名称(参数列表) async -> 返回类型 {
语句
}
对异步函数或异步方法的调用必须包含在
await
表达式中,即它们必须在 await
操作符的作用域内。async
关键字是函数类型中的一部分。同步函数是异步函数的子类型。所以,你可以在使用异步函数的地方,使用同步函数。同步方法可以重写异步方法,且同步方法可以满足对异步方法的协议要求。你可以根据函数是否异步来重写函数。在调用点,上下文决定了应用哪种函数进行重写:异步上下文使用异步函数;同步上下文使用同步函数。
异步方法无法重写同步方法,异步方法也无法满足同步方法的协议要求。反过来说,同步方法可以重写异步方法,并且同步方法可以满足异步方法的协议要求。
Swift 定义了
Never
类型,它表示函数或者方法不会返回给它的调用者。Never
返回类型的函数或方法可以称为不归,不归函数、方法要么引发不可恢复的错误,要么永远不停地运作,这会使调用后本应执行得代码就不再执行了。但即使是不归函数、方法,抛错函数和重抛出函数也可以将程序控制转移到合适的 catch
代码块。你可以重写一个不归方法,但是新的 方法必须保持原有的返回类型和没有返回的行为。
函数声明语法
在程序中使用枚举声明(enumeration declaration) 来引入一个枚举类型。
枚举声明有两种基本形式,使用关键字
enum
来声明。枚举声明体包含零个或多个值,称为枚举用例,还可包含任意数量的声明,包括计算型属性、实例方法、类型方法、构造器、类型别名,甚至其他枚举、结构体、类和 actor。枚举声明不能包含析构器或者协议声明。枚举类型可以采纳任意数量的协议,但是枚举不能从类、结构体和其他枚举继承。
不同于类或者结构体,枚举类型并不隐式提供默认构造器,所有构造器必须显式声明。一个构造器可以委托给枚举中的其他构造器,但是构造过程仅当构造器将一个枚举用例赋值给
self
后才算完成。如下的形式声明了一个包含任意类型枚举用例的枚举变量:
enum 枚举名称: 采纳的协议 {
case 枚举用例1
case 枚举用例2(关联值类型)
}
这种形式的枚举声明在其他语言中有时被叫做可识别联合。
在这种形式中,每个用例块由关键字
case
开始,后面紧接一个或多个以逗号分隔的枚举用例。每个用例名必须是独一无二的。每个用例也可以指定它所存储的指定类型的值,这些类型在关联值类型的元组中被指定,紧跟用例名之后。具有关联值的枚举用例可以像函数一样使用,通过指定的关联值创建枚举实例。和真正的函数一样,你可以获取枚举用例的引用,然后在后续代码中调用它。
enum Number {
case integer(Int)
case real(Double)
}
// f 的类型为 (Int) -> Number
let f = Number.integer
// 利用 f 把一个整数数组转成 Number 数组
let evenInts: [Number] = [0, 2, 4, 6].map(f)
枚举类型可以具有递归结构,就是说,枚举用例的关联值类型可以是枚举类型自身。然而,枚举类型的实例具有值语义,这意味着它们在内存中有固定布局。为了支持递归,编译器必须插入一个间接层。
要让某个枚举用例支持递归,使用
indirect
声明修饰符标记该用例。enum Tree<T> {
case empty
indirect case node(value: T, left: Tree, right:Tree)
}
要让一个枚举类型的所有用例都支持递归,使用
indirect
修饰符标记整个枚举类型,当枚举有多个用例且每个用例都需 要使用 indirect
修饰符标记的时候这将非常便利。被
indirect
修饰符标记的枚举用例必须有一个关联值。使用 indirect
修饰符标记的枚举类型可以既包含有关联值的用例,同时还可包含没有关联值的用例。但是,它不能再单独使用 indirect
修饰符来标记某个用例。以下形式声明了一种枚举类型,其中各个枚举用例的类型均为同一种基本类型:
enum 枚举名称: 原始值类型, 采纳的协议 {
case 枚举用例1 = 原始值1
case 枚举用例2 = 原始值2
}
在这种形式中,每一个用例块由
case
关键字开始,后面紧跟一个或多个以逗号分隔的枚举用例。和第一种形式的枚举用例不同,这种形式的枚举用例包含一个基础值,叫做原始值,各个枚举用例的原始值的类型必须相同。这些原始值的类型通过原始值类型指定,必须表示一个整数、浮点数、字符串或者字符。原始值类型必须符合 Equatable
协议和下列字面量转换协议中的一种:整型字面量需符合 IntergerLiteralConvertible
协议,浮点型字面量需符合 FloatingPointLiteralConvertible
协议,包含任意数量字符的字符串型字面量需符合 StringLiteralConvertible
协议,仅包含一个单一字符的字符串型字面量需符合 ExtendedGraphemeClusterLiteralConvertible
协议。每一个用例的名字和原始值必须唯一。如果原始值类型被指定为
Int
,则不必为用例显式地指定原始值,它们会隐式地被赋值 0
、1
、2
等。每个未被赋值的 Int
类型的用例会被隐式地赋值,其值为上一个用例的原始值加 1
。enum ExampleEnum: Int {
case a, b, c = 5, d
}
在上面的例子中,
ExampleEnum.A
的原始值是 0
,ExampleEnum.B
的原始值是 1
。因为 ExampleEnum.C
的原始值被显式地设定为 5
,因此 ExampleEnum.D
的原始值会自动增长为 6
。如果原始值类型被指定为
String
类型,你不用明确地为用例指定原始值,每个没有指定原始值的用例会隐式地将用例名字作为原始值。enum GamePlayMode: String {
case cooperative, individual, competitive
}
在上面这个例子中,
GamePlayMode.cooperative
的原始值是 "cooperative"
,GamePlayMode.individual
的原始值是 "individual"
,GamePlayMode.competitive
的原始值是 "competitive"
。枚举用例具有原始值的枚举类型隐式地符合定义在 Swift 标准库中的
RawRepresentable
协议。所以,它们拥有一个 rawValue
属性和一个可失败构造器 init?(rawValue: RawValue)
。可以使用 rawValue
属性去获取枚举用例的原始值,例如 ExampleEnum.b.rawValue
。还可以根据原始值来创建一个相对应的枚举用例,只需调用枚举的可失败构造器,例如 ExampleEnum(rawValue: 5)
,这个可失败构造器返回一个可选类型的用例。要获得更多关于具有原始值的枚举用例的信息和例子,请参阅 原始值。可以使用
switch
语句来检验枚举用例的值,正如 使用 switch 语句匹配枚举值 所述。枚举类型是模式匹配的,依靠 switch
语句 case
块中的枚举用例模式,正如 枚举用例模式 所述。枚举声明语法
使用结构体声明(structure declaration) 可以在程序中引入一个结构体类型。结构体声明使用
struct
关键字,遵循如下的形式:struct 结构体名称: 采纳的协议 {
多条声明
}
结构体内可包含零个或多个声明。这些声明可以包括存储型和计算型属性、类型属性、实例方法、类型方法、构造器、下标、类型别名,甚至其他结构体、类、actor 和枚举声明。结构体声明不能包含析构器或者协议声明。关于结构体的详细讨论和示例,请参阅 类和结构体。
结构体可以采纳任意数量的协议,但是不能继承自类、枚举或者