声明
声明在程序中引入了新的名称或构造。 例如,您使用声明来引入函数和方法,变量和常量,并定义新的命名枚举,结构,类和协议类型。 您还可以使用声明来扩展现有命名类型的行为,并将符号导入到您的程序中并在别处声明。
在Swift中,大多数声明也是定义,它们在声明的同时被执行或初始化。 也就是说,由于协议不实现其成员,因此大多数协议成员只是声明。 为了方便起见,由于区分在Swift中并不重要,术语声明涵盖了声明和定义。
declaration → import-declaration-
declaration → constant-declaration-
declaration → variable-declaration-
declaration → typealias-declaration-
declaration → function-declaration-
declaration → enum-declaration-
declaration → struct-declaration-
declaration → class-declaration-
declaration → protocol-declaration-
declaration → initializer-declaration-
declaration → deinitializer-declaration-
declaration → extension-declaration-
declaration → subscript-declaration-
declaration → operator-declaration-
declaration → precedence-group-declaration-
declarations → declaration-declarations-opt-
顶级代码
Swift源文件中的顶级代码由零个或多个语句,声明和表达式组成。 默认情况下,在源文件顶层声明的变量,常量和其他已命名的声明可在每个属于同一模块的源文件中进行编码。 您可以通过使用访问级别修饰符标记声明来覆盖此默认行为,如访问控制级别中所述
top-level-declaration → statements-opt-
代码块
代码块由各种声明和控制结构用于将语句组合在一起。 它有以下形式:
{
statements
}
代码块中的语句包括声明,表达式和其他类型的语句,并按其在源代码中的顺序执行
code-block → {-statements-opt-}-
导入声明
导入声明允许您访问在当前文件之外声明的符号。 基本表单导入整个模块; 它由import关键字后跟一个模块名称组成:
import module
提供哪些符号被导入的更多详细信息限制 - 您可以在模块或子模块内指定特定的子模块或特定的声明。 使用此详细表单时,只有导入的符号(而不是声明它的模块)在当前范围内可用。
import import kindmodule.symbol name
import module.submodule
GRAMMAR OF AN IMPORT DECLARATION
import-declaration → attributes-opt-import-import-kind-opt-import-path-
import-kind → typealias-struct-class-enum-protocol-let-var-func-
import-path → import-path-identifier-import-path-identifier-.-import-path-
import-path-identifier → identifier-operator-
常量声明
常量声明在程序中引入了一个常量命名值。 常量声明使用let关键字声明并具有以下形式:
letconstant name: type = expression
当在全局范围声明一个常量时,它必须用一个值初始化。当函数或方法的上下文中出现常量声明时,只要在第一次读取值之前保证有值,就可以稍后进行初始化。当在类或结构声明的上下文中发生常量声明时,它被认为是一个常量属性。常量声明不是计算属性,因此没有getter或setter。
要声明常量类型属性,请使用静态声明修饰符标记声明。 类型属性在类型属性中讨论。
变量声明在程序中引入了一个名为value的变量,并使用var关键字声明。
变量声明有多种形式,声明不同类型的命名,可变值,包括存储和计算的变量和属性,存储的变量和属性观察器以及静态变量属性。 要使用的适当形式取决于声明变量的范围和您要声明的变量的类型。
注意
您也可以在协议声明的上下文中声明属性,如协议属性声明中所述。
您可以通过使用覆盖声明修饰符标记子类的属性声明来覆盖子类中的属性,如覆盖中所述。
存储的变量和存储的变量属性
常量声明定义常量名称和初始化表达式值之间的不可变绑定;在设定常数值后,不能更改。也就是说,如果一个常量是用一个类对象初始化的,那么该对象本身可以改变,但是常量名和它引用的对象之间的绑定不能。
如果常量声明的常量名称是元组模式,则元组中的每个项目的名称都绑定到初始化程序表达式中的相应值。
let (firstNumber, secondNumber) = (10, 42)
在这个例子中,firstNumber是值为10的命名常量,secondNumber是值为42的命名常量。现在可以单独使用两个常量:
print("The first number is \(firstNumber).")
// Prints "The first number is 10."
print("The second number is \(secondNumber).")
// Prints "The second number is 42."
类型注释(:类型)在常量声明中是可选的,因为常量名称的类型可以推断出来,如Type Inference中所述。
有关常量的更多信息以及有关何时使用它们的指导,请参阅常量和变量以及存储属性
GRAMMAR OF A CONSTANT DECLARATION
constant-declaration → attributes-opt-declaration-modifiers-opt-let-pattern-initializer-list-
pattern-initializer-list → pattern-initializer-pattern-initializer-,-pattern-initializer-list-
pattern-initializer → pattern-initializer-opt-
initializer → =-expression-
变量声明
以下表单声明了一个存储变量或存储的变量属性:
varvariable name: type = expression
初始化器表达式不能出现在协议声明中,但在所有其他上下文中,初始化器表达式是可选的。也就是说,如果不存在初始化表达式,则变量声明必须包含显式类型注释(:type)。
与常量声明一样,如果变量名称是元组模式,则元组中每个项目的名称都绑定到初始化程序表达式中的相应值。
正如他们的名字所暗示的,存储变量或存储变量属性的值存储在内存中。
计算变量和计算属性
您可以在全局范围,函数的本地范围或类或结构声明的上下文中定义此变量声明的形式。当在全局作用域或函数的局部作用域中声明此形式的变量声明时,它被称为存储变量。当它在类或结构声明的上下文中声明时,它被称为存储变量属性。
以下表单声明了一个计算变量或计算属性:
var variable name: type {
get {
statements
}
set(setter name) {
statements
}
}
getter用于读取值,setter用于写入值。 setter子句是可选的,当只需要getter时,可以省略两个子句并直接返回请求的值,如只读计算属性中所述。但是如果你提供了一个setter子句,你还必须提供一个getter子句。
设置者名称和括号括起来是可选的。如果您提供了一个设置者名称,它将用作设置者的参数名称。如果您没有提供setter名称,则setter的默认参数名称为newValue,如速记Setter声明中所述。
与存储的命名值和存储的变量属性不同,计算的命名值或计算属性的值不存储在内存中。
有关更多信息和查看计算属性的示例,请参阅计算属性。
存储变量观察者和属性观察者
您可以在全局范围,函数的本地范围或类,结构,枚举或扩展声明的上下文中定义这种形式的变量声明。当在全局作用域或函数的局部作用域声明此形式的变量声明时,它被称为计算变量。当它在类,结构或扩展声明的上下文中声明时,它被称为计算属性。
你也可以用willSet和didSet观察者声明一个存储的变量或属性。用观察者声明的存储变量或属性具有以下形式:
var variable name: type = expression {
willSet(setter name) {
statements
}
didSet(setter name) {
statements
}
}
您可以将属性观察器添加到任何存储的属性。您也可以通过覆盖子类中的属性将属性观察器添加到任何继承的属性(无论是存储还是计算),如覆盖属性观察器中所述。
初始值表达式在类或结构声明的上下文中是可选的,但在别处需要。当从初始化表达式推断出类型时,类型注释是可选的。
当设置变量或属性的值时,willSet和didSet观察者提供了一种观察(并适当地作出响应)的方法。当变量或属性首次初始化时,不会调用观察者。相反,只有当值设置在初始化上下文之外时才会调用它们。
在设置变量或属性的值之前调用willSet观察器。新值作为常量传递给willSet观察者,因此在执行willSet子句时不能更改它。新值被设置后,didSet观察者立即被调用。与willSet观察者相反,如果您仍然需要访问该变量或属性的旧值,则该值将传递给didSet观察者。也就是说,如果您为自己的didSet观察者子句中的变量或属性赋值,那么您分配的新值将替换刚刚设置并传递给willSet观察者的那个值。
willSet和didSet子句中的setter名称和括号括号是可选的。如果您提供setter名称,则它们将用作willSet和didSet观察者的参数名称。如果不提供setter名称,则willSet观察者的默认参数名称为newValue,而didSet观察者的默认参数名称为oldValue。
当您提供willSet子句时,didSet子句是可选的。同样,当您提供didSet子句时,willSet子句是可选的。
有关更多信息并查看如何使用属性观察器的示例,请参阅Property Observers。
类型变量属性
要声明类型变量属性,请使用静态声明修饰符标记声明。类可以使用类声明修饰符标记类型计算属性,而不是允许子类重写超类的实现。类型属性在类型属性中讨论。
注意
您可以在全局范围,函数的本地范围或类或结构声明的上下文中定义此变量声明的形式。当在全局作用域或函数的局部作用域声明此形式的变量声明时,观察者被称为存储变量观察者。当在类或结构声明中声明它时,观察者被称为财产观察员。
在类声明中,static关键字与使用类和final声明修饰符标记声明具有相同的效果。
GRAMMAR OF A VARIABLE DECLARATION
variable-declaration → variable-declaration-head-pattern-initializer-list-
variable-declaration → variable-declaration-head-variable-name-type-annotation-code-block-
variable-declaration → variable-declaration-head-variable-name-type-annotation-getter-setter-block-
variable-declaration →variable-declaration-head-variable-name-type-annotation-getter-setter-keyword-block-
variable-declaration → variable-declaration-head-variable-name-initializer-willSet-didSet-block-
variable-declaration → variable-declaration-head-variable-name-type-annotation-initializer-opt-willSet-didSet-block-
variable-declaration-head → attributes-opt-declaration-modifiers-opt-var-
variable-name → identifier-
getter-setter-block → code-block-
getter-setter-block → {-getter-clause-setter-clause-opt-}-
getter-setter-block → {-setter-clause-getter-clause-}-
getter-clause → attributes-opt-mutation-modifier-opt-get-code-block-
setter-clause → attributes-opt-mutation-modifier-opt-set-setter-name-opt-code-block-
setter-name → (-identifier-)-
getter-setter-keyword-block → {-getter-keyword-clause-setter-keyword-clause-opt-}-
getter-setter-keyword-block → {-setter-keyword-clause-getter-keyword-clause-}-
getter-keyword-clause → attributes-opt-mutation-modifier-opt-get-
setter-keyword-clause → attributes-opt-mutation-modifier-opt-set-
willSet-didSet-block → {-willSet-clause-didSet-clause-opt-}-
willSet-didSet-block → {-didSet-clause-willSet-clause-opt-}-
willSet-clause → attributes-opt-willSet-setter-name-opt-code-block-
didSet-clause → attributes-opt-didSet-setter-name-opt-code-block-
类型别名声明
类型别名声明将现有类型的命名别名引入到程序中。 类型别名声明使用typealias关键字声明并具有以下形式:
typealiasname = existing type
在协议声明中,类型别名可以为经常使用的类型提供更短,更方便的名称。 例如:
在声明类型别名之后,可以使用别名来代替程序中的任何地方的现有类型。 现有类型可以是命名类型或复合类型。 类型别名不会创建新类型; 他们只是允许一个名称来引用一个现有的类型。
类型别名声明可以使用泛型参数为现有泛型类型提供名称。 类型别名可以为现有类型的部分或全部通用参数提供具体类型。 例如:
typealiasStringDictionary<Value> = Dictionary<String, Value>
// The following dictionaries have the same type.
vardictionary1: StringDictionary<Int> = [:]
vardictionary2: Dictionary<String, Int> = [:]
当使用泛型参数声明类型别名时,这些参数的约束必须完全匹配现有类型的泛型参数上的约束。 例如:
typealiasDictionaryOfInts<Key: Hashable> = Dictionary<Key, Int>
因为类型别名和现有类型可以互换使用,所以类型别名不能引入其他通用约束。
-> protocolSequence {
associatedtypeIterator: IteratorProtocol
typealiasElement = Iterator.Element
}
---
-> funcsum<T: Sequence>(_sequence: T) -> IntwhereT.Element == Int {
// ...
>> return9000
}
如果没有这个类型的别名,sum函数将不得不将相关类型引用为T.Iterator.Element而不是T.Element。
另见协议相关类型声明
GRAMMAR OF A TYPE ALIAS DECLARATION
typealias-declaration → attributes-opt-access-level-modifier-opt-typealias-typealias-name-generic-parameter-clause-opt-typealias-assignment-
typealias-name → identifier-
typealias-assignment → =-type-
函数声明
函数声明在程序中引入了一个函数或方法。 在类,结构,枚举或协议的上下文中声明的函数称为方法。 函数声明使用func关键字声明并具有以下形式:
func function name(parameters) -> return type {
statements
}
如果函数的返回类型为Void,则可以省略返回类型,如下所示:
func function name(parameters) {
statements
}
函数可以使用元组类型返回多个值作为函数的返回类型。
函数定义可以出现在另一个函数声明中。这种函数被称为嵌套函数。
如果嵌套函数捕获一个保证永远不会转义的值(例如输入输出参数),或者将其作为非逸出函数参数传递,则嵌套函数是非逸出的。否则,嵌套函数是一个转义函数。
有关嵌套函数的讨论,请参阅嵌套函数。
参数名称
每个参数的类型都必须包含 - 无法推断。如果在参数类型前面写入inout,则可以在该函数的范围内修改该参数。输入输出参数在下面的输入输出参数中详细讨论。
函数参数是一个以逗号分隔的列表,其中每个参数都有几种形式之一。函数调用中参数的顺序必须与函数声明中的参数顺序相匹配。参数列表中最简单的条目具有以下形式:
parameter name: parameter type
一个参数有一个在函数体内使用的名称,以及一个参数标签,它在调用函数或方法时使用。 默认情况下,参数名称也用作参数标签。 例如:
func f(x: Int, y: Int) -> Int { returnx + y }
f(x: 1, y: 2) // both x and y are labeled
您可以使用以下某种形式覆盖参数标签的默认行为:
argument labelparameter name: parameter type
_parameter name: parameter type
输入输出参数如下传递:
当函数被调用时,参数的值被复制。
在函数的主体中,副本被修改。
当函数返回时,副本的值被分配给原始参数。
这种行为称为拷入拷入或按值结果调用。例如,当一个计算属性或带有观察者的属性作为输入参数传递时,它的getter被作为函数调用的一部分进行调用,并且其setter被作为函数返回的一部分进行调用。
作为优化,当参数是存储在内存中物理地址的值时,函数体内部和外部都会使用相同的内存位置。优化的行为被称为参考调用;它可以满足复制拷贝模型的所有要求,同时消除复制的开销。使用copy-in copy-out给出的模型编写代码,而不依赖于逐个引用的优化,以便在有或没有优化的情况下它的行为正确。
在一个函数中,即使原始值在当前作用域中可用,也不要访问作为入出参数传递的值。访问原始文件是同时访问该值,这违反了Swift的记忆排他性保证。出于同样的原因,您无法将相同的值传递给多个输入输出参数。
有关内存安全性和内存独占性的更多信息,请参阅内存安全性。
// Make a local copy and manually copy it back.
var localX = x
defer { x = localX }
// Operate on localX asynchronously, then wait before returning.
queue.async { someMutatingOperation(&localX) }
queue.sync {}
特殊参数
参数名称前面的名称为参数提供了明确的参数标签,该标签可以与参数名称不同。相应的参数必须在函数或方法调用中使用给定的参数标签。
参数名称前面的下划线(_)可以取消参数标签。 相应的参数在函数或方法调用中必须没有标签。
func repeatGreeting(_greeting: String, countn: Int) { /* Greet n times */ }
repeat Greeting("Hello, world!", count: 2) // count is labeled, greeting is not
Inout参数
捕获输入输出参数的闭包函数或嵌套函数必须是非空格。如果您需要捕获输入输出参数而不改变它或观察其他代码所做的更改,请使用捕获列表以不可变的方式明确捕获参数。
func someFunction(a: inoutInt) -> () -> Int {
return { [a] inreturna + 1 }
}
如果您需要捕获并更改输入输出参数,请使用显式本地副本,例如在多线程代码中确保在函数返回之前所有变量都已完成。
func multithreadedFunction(queue: DispatchQueue, x: inoutInt) {
}
有关输入输出参数的更多讨论和示例,请参阅输入输出参数。
可以忽略参数,获取可变数量的值,并使用以下形式提供默认值:
_ : parameter type
parameter name: parameter type...
parameter name: parameter type = default argument value
基本类型名称后跟三个点(...)的参数被理解为可变参数。 一个函数最多可以有一个可变参数。 可变参数被视为包含基本类型名称元素的数组。 例如,可变参数Int ...被视为[Int]。 有关使用可变参数的示例,请参阅可变参数。
枚举方法或修改自我的结构必须使用mutating声明修饰符进行标记。
重写超类方法的方法必须使用override声明修饰符进行标记。覆盖没有覆盖修饰符的方法或在不覆盖超类方法的方法上使用覆盖修饰符时,会出现编译时错误。
与一个类型相关的方法而不是一个类型的实例必须用枚举和结构的静态声明修饰符或类或静态或类声明修饰符来标记。用类声明修饰符标记的类类型方法可以被子类实现覆盖;用静态标记的类类型方法不能被覆盖。
throws功能和方法
statements
throws关键字是函数类型的一部分,nonthrowing函数是抛出函数的子类型。因此,你可以在同一个地方使用非抛出函数作为抛出函数。
您不能仅基于函数是否可以抛出错误来重载函数。也就是说,你可以根据函数参数是否可以抛出错误来重载函数。
抛出方法不能重写非抛出方法,抛出方法不能满足非抛出方法的协议要求。也就是说,非抛出方法可以重写抛出方法,非抛出方法可以满足抛出方法的协议要求。
rethrows功能和方法
Never功能
Swift定义了一个Never类型,它表示函数或方法不会返回给它的调用者。 Never返回类型的函数和方法称为nonreturning。非归零函数和方法要么导致无法恢复的错误,要么开始一系列无限期延续的工作。这意味着在调用之后立即运行的代码从不执行。抛出和重新抛出函数可以将程序控制转移到适当的catch块,即使它们不返回。
可以调用非返回函数或方法来结束guard语句的else子句,如Guard语句中所讨论的。
枚举声明将一个指定的枚举类型引入到您的程序中。
枚举声明有两种基本形式,并使用enum关键字声明。使用任一形式声明的枚举的主体包含零个或多个值(称为枚举个案)以及任意数量的声明,包括计算属性,实例方法,类型方法,初始化方法,类型别名,甚至其他枚举,结构和类声明。枚举声明不能包含deinitializer或协议声明。
枚举类型可以采用任意数量的协议,但不能从类,结构或其他枚举继承。
与类和结构不同,枚举类型不具有隐式提供的默认初始化程序;所有初始化必须明确声明。初始化器可以委托给枚举中的其他初始化器,但初始化过程只有在初始化器将其中一个枚举案例分配给self后才能完成。
类似于结构,但与类不同,枚举是值类型;枚举实例在分配给变量或常量时,或作为参数传递给函数调用时被复制。有关值类型的信息,请参阅结构和枚举是值类型。
您可以扩展具有扩展声明的枚举类型的行为,如扩展声明中所述。
枚举任何类型的案例
以下表单声明了一个枚举类型,其中包含任何类型的枚举个案:
下划线(_)参数被明确忽略,不能在函数体内访问。
具有等号(=)的参数和类型后的表达式被理解为具有给定表达式的默认值。 给定的表达式在函数被调用时被评估。 如果在调用函数时忽略该参数,则使用默认值。
func f(x: Int = 42) -> Int { returnx }
f() // Valid, uses default value
f(x: 7) // Valid, uses the value provided
f(7) // Invalid, missing argument label
特殊的方法
可以抛出错误的函数和方法必须用throws关键字标记。这些函数和方法被称为抛出函数和抛出方法。他们有以下形式:
func function name(parameters) throws -> return type {
}
调用抛出函数或方法必须包装在一个try或try!表达式(即,在try或try!运算符的范围内)。
可以使用rethrows关键字声明一个函数或方法,以指示只有在其某个函数参数抛出错误时才会抛出错误。这些功能和方法被称为重新抛出功能和重新抛出方法。重新生成函数和方法必须至少有一个抛出函数参数。
func someFunction(callback: () throws -> Void) rethrows {
trycallback()
}
重新抛出函数或方法只能在catch子句中包含throw语句。 这使您可以调用do-catch块内的throwing函数,并通过抛出不同的错误来处理catch子句中的错误。 另外,catch子句只能处理由重投函数的抛出参数之一抛出的错误。 例如,以下内容无效,因为catch子句将处理alwaysThrows()抛出的错误。
func alwaysThrows() throws {
throw SomeError.error
}
func someFunction(callback: () throws -> Void) rethrows {
do {
try callback()
try alwaysThrows() // Invalid, alwaysThrows() isn't a throwing parameter
} catch {
throw AnotherError.error
}
}
抛出方法不能覆盖重新抛出方法,并且抛出方法不能满足重新抛出方法的协议要求。也就是说,重新抛出方法可以重写抛出方法,并且重新抛出方法可以满足抛出方法的协议要求。
您可以重写非返回方法,但新方法必须保留其返回类型和非返回行为。
GRAMMAR OF A FUNCTION DECLARATION
function-declaration → function-head-function-name-generic-parameter-clause-opt-function-signature-generic-where-clause-opt-function-body-opt-
function-head → attributes-opt-declaration-modifiers-opt-func-
function-name → identifier-operator-
function-signature → parameter-clause-throws-opt-function-result-opt-
function-signature → parameter-clause-rethrows-function-result-opt-
function-result → ->-attributes-opt-type-
function-body → code-block-
parameter-clause → (-)-(-parameter-list-)-
parameter-list → parameter-parameter-,-parameter-list-
parameter → external-parameter-name-opt-local-parameter-name-type-annotation-default-argument-clause-opt-
parameter → external-parameter-name-opt-local-parameter-name-type-annotation-
parameter → external-parameter-name-opt-local-parameter-name-type-annotation-...-
external-parameter-name → identifier-
local-parameter-name → identifier-
default-argument-clause → =-expression-
枚举声明
enum enumeration name: adopted protocols {
case enumeration case 1
case enumeration case 2(associated value types)
}
在这种形式中,每个大小写块由case关键字组成,后面跟着一个或多个枚举用例,用逗号分隔。 每个案件的名称必须是唯一的。 每种情况也可以指定它存储给定类型的值。 这些类型在关联的值类型元组中指定,紧跟在案例的名称后面。
case integer(Int)
case real(Double)
}
let f = Number.integer
// f is a function of type (Int) -> Number
// Apply f to create an array of Number instances with integer values
枚举与间接
枚举可以具有递归结构,也就是说,它们可以具有作为枚举类型本身的实例的关联值的情况。 但是,枚举类型的实例具有值语义,这意味着它们在内存中具有固定布局。 为了支持递归,编译器必须插入一个间接层。
用间接修饰符标记的枚举可以包含具有关联值的案例和不具有关联值的案例的混合。 也就是说,它不能包含任何也用间接修饰符标记的情况。
枚举与原始值类型的情况
在这种形式下声明的枚举有时被称为其他编程语言中的歧视联合。
存储关联值的枚举个案可用作创建具有指定关联值的枚举实例的函数。 就像函数一样,你可以获得一个枚举案例的引用,并在以后的代码中使用它。
enum Number {
letevenInts: [Number] = [0, 2, 4, 6].map(f)
有关更多信息以及查看具有关联值类型的案例示例,请参阅关联值。
要为特定的枚举类型启用间接寻址,请使用间接声明修饰符标记它。 间接案件必须具有关联价值。
enum Tree<T> {
case empty
indirectcasenode(value: T, left: Tree, right: Tree)
}
要为所有具有关联值的枚举情况启用间接寻址,请使用间接修饰符标记整个枚举 - 枚举中包含许多需要使用间接修饰符标记的情况时,这很方便。
下面的表单声明了一个包含相同基本类型的枚举个案的枚举类型:
enum enumeration name: raw-value type, adopted protocols {
case enumeration case 1 = raw value 1
case enumeration case 2 = raw value 2
}
具有原始值类型的枚举隐式地符合Swift标准库中定义的RawRepresentable协议。因此,它们具有rawValue属性和具有签名init?(rawValue:RawValue)的failable初始值设定项。您可以使用rawValue属性来访问枚举大小写的原始值,如ExampleEnum.b.rawValue中所示。如ExampleEnum(rawValue:5)中所示,您也可以使用原始值通过调用枚举的failable初始化程序来查找相应的案例(如果有的话),该案例返回可选案例。有关更多信息和查看使用原始值类型的案例示例,请参阅原始值。
访问枚举案例
要引用枚举类型的情况,请使用点(。)语法,如EnumerationType.enumerationCase中所示。如果枚举类型可以从上下文中推断出来,则可以省略它(点仍然是必需的),如枚举语法和隐式成员表达式中所述。
在这种形式中,每个大小写块由case关键字组成,后面跟着一个或多个用逗号分隔的枚举个案。与第一种形式的案例不同,每种案例都有一个基础值,称为原始值,具有相同的基本类型。这些值的类型在原始值类型中指定,并且必须表示整数,浮点数,字符串或单个字符。特别地,原始值类型必须符合Equatable协议和以下协议之一:ExpressibleByIntegerLiteral为整数常量,ExpressibleByFloatLiteral浮点文字,ExpressibleByStringLiteral对于包含任何数量的字符的字符串文字,和ExpressibleByUnicodeScalarLiteral或ExpressibleByExtendedGraphemeClusterLiteral字符串只包含单个字符的文字。每个案件必须有一个唯一的名称,并被分配一个独特的原始价值。
如果原始值类型被指定为Int,并且您没有明确地将值分配给案例,则它们被隐式分配值0,1,2等等。 Int类型的每个未分配的情况都隐含地分配了一个原始值,该值从前一个案例的原始值自动增加。
enum ExampleEnum: Int {
case a, b, c = 5, d
}
在上例中,ExampleEnum.a的原始值为0,ExampleEnum.b的值为1.并且由于ExampleEnum.c的值显式设置为5,因此ExampleEnum.d的值自动从5增加, 因此是6。
如果原始值类型被指定为字符串,并且您没有明确地将值分配给案例,则每个未分配的案例都会隐式分配一个与该案例名称相同文本的字符串。
enum GamePlayMode: String {
case cooperative, individual, competitive
}
在上例中,GamePlayMode.cooperative的原始值为“cooperative”,GamePlayMode.individual的原始值为“individual”。并且GamePlayMode.competitive的原始价值是“competitive”。
要检查枚举个案的值,请使用switch语句,如使用Switch语句匹配枚举值所示。枚举类型与switch语句的大小写块中的枚举大小写模式进行了模式匹配,如枚举大小写模式中所述。
GRAMMAR OF AN ENUMERATION DECLARATION
enum-declaration → attributes-opt-access-level-modifier-opt-union-style-enum-
enum-declaration → attributes-opt-access-level-modifier-opt-raw-value-style-enum-
union-style-enum → indirect-opt-enum-enum-name-generic-parameter-clause-opt-type-inheritance-clause-opt-generic-where-clause-opt-{-union-style-enum-members-opt-}-
union-style-enum-members → union-style-enum-member-union-style-enum-members-opt-
union-style-enum-member → declaration-union-style-enum-case-clause-compiler-control-statement-
union-style-enum-case-clause → attributes-opt-indirect-opt-case-union-style-enum-case-list-
union-style-enum-case-list → union-style-enum-case-union-style-enum-case-,-union-style-enum-case-list-
union-style-enum-case → enum-case-name-tuple-type-opt-
enum-name → identifier-
enum-case-name → identifier-
raw-value-style-enum → enum-enum-name-generic-parameter-clause-opt-type-inheritance-clause-generic-where-clause-opt-{-raw-value-style-enum-members-}-
raw-value-style-enum-members → raw-value-style-enum-member-raw-value-style-enum-members-opt-
raw-value-style-enum-member → declaration-raw-value-style-enum-case-clause-compiler-control-statement-
raw-value-style-enum-case-clause → attributes-opt-case-raw-value-style-enum-case-list-
raw-value-style-enum-case-list → raw-value-style-enum-case-raw-value-style-enum-case-,-raw-value-style-enum-case-list-
raw-value-style-enum-case → enum-case-name-raw-value-assignment-opt-
raw-value-assignment → =-raw-value-literal-
raw-value-literal → numeric-literal-static-string-literal-boolean-literal-
结构声明
结构声明将一个命名结构类型引入到您的程序中。 结构声明使用struct关键字声明并具有以下形式:
struct structure name: adopted protocols {
declarations
}
结构类型可以采用任意数量的协议,但不能从类,枚举或其他结构继承。
有三种方法可以创建一个先前声明的结构的实例:
调用结构体中声明的初始化程序之一,如初始化程序中所述。
如果没有声明初始化函数,则调用结构的成员初始值设定项,如构造类型的成员初始化函数中所述。
如果没有声明初始化方法,并且结构声明的所有属性都被赋予初始值,则调用结构的默认初始化方法,如默认初始化程序中所述。
初始化中描述了初始化结构的声明属性的过程。
可以使用点(。)语法访问结构实例的属性,如访问属性中所述。
结构是值类型;结构实例在分配给变量或常量时,或作为参数传递给函数调用时被复制。有关值类型的信息,请参阅结构和枚举是值类型。
结构体包含零个或多个声明。这些声明可以包括存储和计算属性,类型属性,实例方法,类型方法,初始化器,下标,类型别名,甚至包括其他结构,类和枚举声明。结构声明不能包含deinitializer或协议声明。有关各种声明的讨论和结构示例,请参阅类和结构。
您可以使用扩展声明扩展结构类型的行为,如扩展声明中所述
GRAMMAR OF A STRUCTURE DECLARATION
struct-declaration → attributes-opt-access-level-modifier-opt-struct-struct-name-generic-parameter-clause-opt-type-inheritance-clause-opt-generic-where-clause-opt-struct-body-
struct-name → identifier-
struct-body → {-struct-members-opt-}-
struct-members → struct-member-struct-members-opt-
struct-member → declaration-compiler-control-statement-
类声明
类声明将一个命名类类型引入到您的程序中。 类声明使用class关键字声明并具有以下形式:
class class name: superclass, adopted protocols {
declarations
}
一个类类型只能从一个父类,它的父类继承,但可以采用任意数量的协议。超类首先出现在类名和冒号后面,然后是任何采用的协议。泛型类可以继承其他泛型类和非泛型类,但非泛型类只能从其他非泛型类继承。当您在冒号后面编写通用超类的名称时,必须包含该通用类的全名,包括其通用参数子句。
正如初始化器声明中所讨论的,类可以具有指定的和便利的初始化器。类的指定初始化器必须初始化所有类的声明属性,并且必须在调用它的任何超类的指定初始化器之前进行初始化。
一个类可以覆盖它的超类的属性,方法,下标和初始值设定项。重写的属性,方法,下标和指定的初始化符必须用override声明修饰符标记。
为了要求这个子类实现一个超类的初始值设定项,使用所需的声明修饰符标记超类的初始值设定项。该初始化器的子类的实现也必须使用所需的声明修饰符进行标记。
虽然超类中声明的属性和方法由当前类继承,但超类中声明的指定初始化方法仅在子类满足自动初始化方法继承中描述的条件时才被继承。 Swift类不会从通用基类继承。
有两种方法可以创建一个先前声明的类的实例:
调用类中声明的初始化器之一,如初始化器中所述。
如果没有声明初始化方法,并且类声明的所有属性都被赋予初始值,则调用该类的默认初始化方法,如默认初始化程序中所述。
使用点(。)语法访问类实例的属性,如访问属性中所述。
类是引用类型;一个类的实例被引用,而不是被复制,当被赋值给变量或常量时,或者当作为参数传递给函数调用时。有关引用类型的信息,请参阅结构和枚举是值类型。
一个类的主体包含零个或多个声明。这些声明可以包括存储和计算属性,实例方法,类型方法,初始化方法,单个去初始化程序,下标,类型别名,甚至包括其他类,结构和枚举声明。类声明不能包含协议声明。有关包含各种声明的类的讨论和几个示例,请参见类和结构。
您可以使用扩展声明来扩展类类型的行为,如扩展声明中所述。
GRAMMAR OF A CLASS DECLARATION
class-declaration → attributes-opt-access-level-modifier-opt-final-opt-class-class-name-generic-parameter-clause-opt-type-inheritance-clause-opt-generic-where-clause-opt-class-body-
class-declaration → attributes-opt-final-access-level-modifier-opt-class-class-name-generic-parameter-clause-opt-type-inheritance-clause-opt-generic-where-clause-opt-class-body-
class-name → identifier-
class-body → {-class-members-opt-}-
class-members → class-member-class-members-opt-
class-member → declaration-compiler-control-statement-
协议声明
协议声明将一个命名的协议类型引入到您的程序中。 协议声明使用协议关键字在全局范围声明,并具有以下形式:
protocol protocol name: inherited protocols {
protocol member declarations
}
协议类型可以从任何其他协议继承。当协议类型从其他协议继承时,来自这些其他协议的一组要求被聚合,并且从当前协议继承的任何类型必须符合所有这些要求。有关如何使用协议继承的示例,请参阅协议继承。
注意
您还可以使用协议组合类型来聚合多个协议的一致性要求,如协议组合类型和协议组合中所述。
通过在该类型的扩展声明中采用协议,可以将协议一致性添加到先前声明的类型。在扩展中,您必须实现所有采用的协议的要求。如果该类型已经实现了所有需求,则可以将扩展声明的主体留空。
默认情况下,符合协议的类型必须实现协议中声明的所有属性,方法和下标。也就是说,您可以使用可选的声明修饰符标记这些协议成员声明,以指定它们的符合类型的实现是可选的。可选修饰符只能应用于标有objc属性的成员,并且只能应用于标记有objc属性的协议成员。因此,只有类别类型可以采用并符合包含可选成员要求的协议。有关如何使用可选声明修饰符的更多信息,以及有关如何访问可选协议成员的指导(例如,当您不确定某个符合类型是否实现它们时),请参阅可选协议要求。
注意
如果一个协议被标记为objc属性,则AnyObject要求会隐式应用于该协议; 没有必要明确地用AnyObject要求标记协议。
协议是命名类型,因此它们可以与其他命名类型一样出现在代码中的所有相同位置,如协议类型中所述。 但是,不能构建协议的实例,因为协议实际上并未提供它们指定的要求的实现。
协议主体包含零个或多个协议成员声明,它们描述了采用该协议的任何类型必须满足的一致性要求。具体来说,协议可以声明符合类型必须实现某些属性,方法,初始化器和下标。协议还可以声明特殊类型的别名,称为关联类型,可以指定协议各种声明之间的关系。协议声明不能包含类,结构,枚举或其他协议声明。协议成员声明在下面详细讨论。
要将协议仅限于类类型,请在继承协议列表中包含冒号后面的AnyObject协议。例如,以下协议只能由类类型采用:
protocol SomeProtocol: AnyObject {
/* Protocol members go here */
}
从AnyObject需求标记的协议继承的任何协议同样可以仅由类类型采用。
您可以使用协议来声明类或结构的委托应实现哪些方法,如委派中所述
GRAMMAR OF A PROTOCOL DECLARATION
protocol-declaration → attributes-opt-access-level-modifier-opt-protocol-protocol-name-type-inheritance-clause-opt-generic-where-clause-opt-protocol-body-
protocol-name → identifier-
protocol-body → {-protocol-members-opt-}-
protocol-members → protocol-member-protocol-members-opt-
protocol-member → protocol-member-declaration-compiler-control-statement-
protocol-member-declaration → protocol-property-declaration-
protocol-member-declaration → protocol-method-declaration-
protocol-member-declaration → protocol-initializer-declaration-
protocol-member-declaration → protocol-subscript-declaration-
protocol-member-declaration → protocol-associated-type-declaration-
protocol-member-declaration → typealias-declaration-
协议属性声明
协议声明符合类型必须通过在协议声明的主体中包含协议属性声明来实现属性。 协议属性声明具有变量声明的特殊形式:
var property name: type { getset }
如果属性声明同时包含get和set关键字,则符合类型可以使用存储的变量属性或可读写的计算属性(也就是实现getter和setter的属性)来实现它。 但是,该属性声明不能作为一个常量属性或一个只读计算属性来实现。 如果属性声明仅包含get关键字,则可以将其实现为任何类型的属性。 有关实现协议属性要求的符合类型的示例,请参阅属性要求。
协议声明符合类型必须通过在协议声明的主体中包含一个协议方法声明来实现一个方法。协议方法声明与函数声明具有相同的形式,但有两个例外:它们不包含函数体,并且不能提供任何默认参数值作为函数声明的一部分。有关实现协议方法要求的符合类型的示例,请参阅方法要求。
要在协议声明中声明类或静态方法要求,请使用静态声明修饰符标记方法声明。实现此方法的类使用类修饰符声明方法。实现它的结构必须用静态声明修饰符声明方法。如果要在扩展中实现该方法,请在扩展类时使用类修饰符,如果扩展了结构,请使用静态修饰符。
协议声明一致性类型必须通过在协议声明的主体中包含协议初始化程序声明来实现初始化程序。 协议初始化符声明与初始化符声明具有相同的形式,除非它们不包含初始化符的主体。
一致性类型可以通过实现一个不可破坏的初始化程序或一个init来满足不可破坏的协议初始值设定项要求! failable初始化程序。 一致性类型可以通过实现任何类型的初始化器来满足failable协议初始值设定器的要求。
当一个类实现一个初始值设定项来满足一个协议的初始值设定项要求时,如果这个类尚未用最后的声明修饰符标记,初始值设定项必须用所需的声明修饰符标记。
与其他协议成员声明一样,这些属性声明仅声明符合协议的类型的getter和setter要求。 因此,您不要在声明它的协议中直接实现getter或setter。
另请参阅变量声明
protocol-property-declaration →variable-declaration-head-variable-name-type-annotation-getter-setter-keyword-block-
协议方法声明
另请参阅函数声明
protocol-method-declaration → function-head-function-name-generic-parameter-clause-opt-function-signature-generic-where-clause-opt-
协议初始化程序声明
另请参阅初始化程序声明
protocol-initializer-declaration → initializer-head-generic-parameter-clause-opt-parameter-clause-throws-opt-generic-where-clause-opt-
protocol-initializer-declaration → initializer-head-generic-parameter-clause-opt-parameter-clause-rethrows-generic-where-clause-opt-
协议下标声明
协议声明符合类型必须通过在协议声明的主体中包含协议下标声明来实现下标。 协议下标声明有一个特殊的下标声明形式:
subscript (parameters) -> return type { getset }
协议使用associatedtype关键字声明关联的类型。 关联类型为用作协议声明一部分的类型提供别名。 关联类型与泛型参数子句中的类型参数类似,但它们在声明它们的协议中与Self关联。 在这种情况下,Self指的是符合协议的最终类型。有关更多信息和示例,请参阅关联类型。
associatedtype SomeType
}
protocol SubProtocolA: SomeProtocol {
// This syntax produces a warning.
associatedtype SomeType: Equatable
}
// This syntax is preferred.
初始化程序声明将类,结构或枚举的初始化程序引入到程序中。 初始化器声明使用init关键字声明,并有两种基本形式。
结构,枚举和类类型可以有任意数量的初始化器,但类初始化器的规则和相关行为是不同的。 与结构和枚举不同,类有两种初始化程序:指定初始化程序和便捷初始化程序,如初始化中所述。
下标声明只声明符合协议的类型的最小getter和setter实现要求。 如果下标声明包含get和set关键字,则符合类型必须同时实现getter和setter子句。 如果下标声明仅包含get关键字,则符合类型必须至少实现一个getter子句,并可以选择性地实现setter子句。
另见下标声明。
protocol-subscript-declaration → subscript-head-subscript-result-generic-where-clause-opt-getter-setter-keyword-block-
协议关联类型声明
在协议声明中使用泛型where子句将约束添加到从另一个协议继承的关联类型,而不重新声明关联的类型。 例如,下面的SubProtocol的声明是等价的:
protocol SomeProtocol {
protocolSubProtocolB: SomeProtocolwhereSomeType: Equatable {}
另请参见类型别名声明
protocol-associated-type-declaration → attributes-opt-access-level-modifier-opt-associatedtype-typealias-name-type-inheritance-clause-opt-typealias-assignment-opt-generic-where-clause-opt-
初始化程序声明
下面的表单声明了类的结构,枚举和指定初始值设定项的初始值设定项:
init(parameters) {
statements
}
指定的初始值设定项只能在类声明的上下文中声明,因此不能使用扩展声明添加到类中。
结构和枚举中的初始化器可以调用其他声明的初始化器来委托部分或全部初始化过程。
类的指定初始化器直接初始化所有类的属性。 它不能调用同一类的任何其他初始化器,并且如果该类有超类,则它必须调用超类的指定初始化器之一。 如果类从其父类继承任何属性,则必须先调用其中一个超类的指定初始化方法,然后才能在当前类中设置或修改任何这些属性。
要声明类的便利初始值设定项,请使用便捷声明修饰符标记初始值设定项声明。
convenience init(parameters) {
statements
}
您可以使用所需的声明修饰符标记指定的和便捷初始值设定项,以要求每个子类都实现初始值设定项。该初始化器的子类的实现也必须使用所需的声明修饰符进行标记。
默认情况下,在超类中声明的初始化程序不会被子类继承。也就是说,如果一个子类使用默认值初始化其存储的所有属性,并且没有定义它自己的任何初始化器,它将继承所有超类的初始化器。如果子类覆盖了所有超类的指定初始值设定项,它将继承超类的便捷初始值设定项。
与方法,属性和下标一样,您需要使用override声明修饰符标记覆盖指定的初始化方式。
注意
如果使用必需的声明修饰符标记初始化程序,则当您在子类中重写所需的初始化程序时,您也不会使用override修饰符标记初始化程序。
就像函数和方法一样,初始化器可以抛出或重新抛出错误。就像函数和方法一样,在初始化器的参数之后使用throws或rethrows关键字来表示适当的行为。
要查看各种类型声明中初始化函数的示例,请参阅初始化。
Failable初始化器
failable初始化程序是一种初始化程序,它产生一个可选实例或一个隐式展开的可选实例,该初始化程序声明的类型是可选的。结果,可失败的初始化程序可以返回nil来指示初始化失败。
可分区的初始化程序可以委托给任何类型的初始化程序。一个不可破解的初始化程序可以委托给另一个不可破解的初始化程序或一个init! failable初始化程序。一个不可破解的初始化器可以委托给一个init?通过强制解开超类初始化程序的结果来破坏初始化程序 - 例如,通过编写super.init()!.
初始化失败通过初始化器委派传播。特别是,如果一个可分区的初始化程序委托给一个初始化程序失败并返回nil,那么委托的初始化程序也失败并隐式返回nil。如果一个不可破解的初始化程序委托给一个init! failable初始化程序失败并返回nil,则会引发运行时错误(就好像您使用!运算符打开了一个具有零值的可选项)。
可由任何指定的初始化程序在子类中重写可失败的指定初始化程序。一个不可破坏的指定初始值设定项只能由一个非破坏指定的初始值设定项在子类中重写。
便利初始值设定项可以将初始化过程委派给另一个便利初始值设定项或委托给该类的指定初始值设定项之一。也就是说,初始化过程必须以对最终初始化类的属性的指定初始化程序的调用结束。便利初始化程序不能调用超类的初始化程序。
要声明一个可生成可选实例的failable初始化程序,请在初始化程序声明(init?)中的init关键字后附加一个问号。要声明一个生成隐式解包的可选实例的failable初始化程序,请附加一个感叹号(init!)。下面的例子显示了一个init? failable初始化器生成一个结构的可选实例。
struct SomeStruct {
let property: String
// produces an optional instance of 'SomeStruct'
init?(input: String) {
if input.isEmpty {
// discard 'self' and return 'nil'
return nil
}
property = input
}
}
你叫一个初始化? failable初始化器的方式与调用非破坏初始化器的方式相同,只是必须处理结果的可选性。
if let actualInstance = SomeStruct(input: "Hello") {
// do something with the instance of 'SomeStruct'
} else {
// initialization of 'SomeStruct' failed and the initializer returned 'nil'
}
可以初始化的初始化程序可以在初始化程序的主体实现中的任何位置返回nil。
有关更多信息和查看failable初始化程序的示例,请参阅Failable Initializers
initializer-declaration → initializer-head-generic-parameter-clause-opt-parameter-clause-throws-opt-generic-where-clause-opt-initializer-body-
initializer-declaration → initializer-head-generic-parameter-clause-opt-parameter-clause-rethrows-generic-where-clause-opt-initializer-body-
initializer-head → attributes-opt-declaration-modifiers-opt-init-
initializer-head → attributes-opt-declaration-modifiers-opt-init-?-
initializer-head → attributes-opt-declaration-modifiers-opt-init-!-
initializer-body → code-block-
Deinitializer声明
deinitializer声明为类类型声明了一个deinitializer。 取消初始化程序不采用参数并具有以下形式:
deinit {
statements
}
一个子类继承它的超类的deinitializer,它在子类对象被释放之前隐式调用。 子类对象不会被释放,直到其继承链中的所有去初始化器完成执行。
去初始化程序不直接调用。
当不再有任何对类对象的引用时,即在类对象被释放之前,会自动调用取消初始化器。 去初始化器只能在类声明的主体中声明 - 但不能在类的扩展中声明 - 每个类最多只能有一个。
有关如何在类声明中使用deinitializer的示例,请参阅Deinitialization
deinitializer-declaration → attributes-opt-deinit-code-block-
扩展声明
扩展声明允许您扩展现有类型的行为。 扩展声明使用扩展关键字声明并具有以下形式:
extension type namewhererequirements {
declarations
}
如果类型名称是类,结构或枚举类型,则扩展名将扩展该类型。如果类型名称是协议类型,则扩展扩展符合该协议的所有类型。协议扩展的正文中的声明不能被标记为最终。
扩展泛型类型或协议以及关联类型的扩展声明可以包括需求。如果扩展类型的实例或符合扩展协议的类型的实例满足要求,则实例将获得声明中指定的行为。
扩展声明可以包含初始化声明。也就是说,如果要扩展的类型在另一个模块中定义,则初始化程序声明必须委托给已在该模块中定义的初始化程序,以确保该类型的成员已正确初始化。
现有类型的属性,方法和初始值设定项不能在该类型的扩展名中重写。
扩展声明的主体包含零个或多个声明。这些声明可以包括计算属性,计算类型属性,实例方法,类型方法,初始化程序,下标声明,甚至是类,结构和枚举声明。扩展声明不能包含deinitializer或协议声明,存储的属性,属性观察器或其他扩展声明。有关包含各种声明的扩展的讨论和几个示例,请参阅扩展。
扩展声明可以通过指定采用的协议将协议一致性添加到现有的类,结构或枚举类型中:
extension type name: adopted protocolswhererequirements {
declarations
}
如果添加协议一致性的扩展也包括需求,则只有符合要求的扩展类型的实例才能获得一致性。
扩展声明不能将类继承添加到现有类,因此您可以在类型名称和冒号后指定一个协议列表
extension-declaration → attributes-opt-access-level-modifier-opt-extension-type-identifier-type-inheritance-clause-opt-generic-where-clause-opt-extension-body-
extension-body → {-extension-members-opt-}-
extension-members → extension-member-extension-members-opt-
extension-member → declaration-compiler-control-statement-
下标声明
下标声明允许您为特定类型的对象添加下标支持,并且通常用于为访问集合,列表或序列中的元素提供方便的语法。 下标声明使用下标关键字声明并具有以下形式:
subscript (parameters) -> return type {
get {
statements
}
set(setter name) {
statements
}
}
这些参数指定用于访问下标表达式中相应类型元素的一个或多个索引(例如,表达式对象[i]中的i)。尽管用于访问元素的索引可以是任何类型,但每个参数都必须包含一个类型注释以指定每个索引的类型。返回类型指定正在访问的元素的类型。
与计算属性一样,下标声明支持读取和写入访问元素的值。 getter用于读取值,setter用于写入值。 setter子句是可选的,当只需要一个getter时,可以省略两个子句并直接返回所需的值。也就是说,如果你提供了一个setter子句,你还必须提供一个getter子句。
设置者名称和括号是可选的。如果您提供了一个设置者名称,它将用作设置者的参数名称。如果您不提供setter名称,则setter的默认参数名称为value。设置者名称的类型必须与返回类型相同。
只要参数或返回类型与您所重载的参数或返回类型不同,就可以在声明的类型中重载下标声明。您也可以覆盖从超类继承的下标声明。当你这样做时,你必须用重写声明修饰符标记重写的下标声明。
默认情况下,下标中使用的参数不具有参数标签,与函数,方法和初始化程序不同。但是,您可以使用函数,方法和初始化器使用的相同语法提供显式参数标签。
您也可以在协议声明的上下文中声明下标,如协议下标声明中所述。
运算符声明在程序中引入了新的中缀,前缀或后缀运算符,并使用运算符关键字声明。
您可以声明三种不同固定的运算符:中缀,前缀和后缀。 运算符的固定性指定运算符与其操作数的相对位置。
运营商声明有三种基本形式,每种固定性都有一种形式。 运算符的固定性是通过在运算符关键字前用中缀,前缀或后缀声明修饰符标记运算符声明来指定的。 在每个表格中,操作员的名称只能包含操作员中定义的操作员字符。
下标声明只能出现在类,结构,枚举,扩展或协议声明的上下文中。
有关下标和查看下标声明示例的更多信息,请参阅下标。
subscript-declaration → subscript-head-subscript-result-generic-where-clause-opt-code-block-
subscript-declaration → subscript-head-subscript-result-generic-where-clause-opt-getter-setter-block-
subscript-declaration → subscript-head-subscript-result-generic-where-clause-opt-getter-setter-keyword-block-
subscript-head → attributes-opt-declaration-modifiers-opt-subscript-generic-parameter-clause-opt-parameter-clause-
subscript-result → ->-attributes-opt-type-
运算符声明
以下表格声明了一个新的中缀操作符:
infixoperatoroperator name: precedence group
Infix操作员可以选择指定一个优先组。 如果省略运算符的优先级组,Swift将使用默认优先级组DefaultPrecedence,该优先级指定的优先级高于TernaryPrecedence。 有关更多信息,请参阅优先组声明。
中缀运算符是在其两个操作数之间写入的二元运算符,例如表达式1 + 2中熟悉的加法运算符(+)。
以下表单声明了一个新的前缀运算符:
prefixoperatoroperator name
前缀运算符声明不指定优先级。 前缀运算符是非关联的。
前缀运算符是在其操作数之前立即写入的一元运算符,例如表达式!a中的前缀逻辑NOT运算符(!)。
以下表单声明了一个新的后缀运算符:
postfixoperatoroperator name
与前缀运算符一样,后缀运算符声明不指定优先级。 Postfix运算符是非关联的。
优先级组声明为您的程序中的中缀运算符优先级引入了一个新分组。 运算符的优先级指定运算符与其操作数绑定的程度,如果没有分组括号。
后缀运算符是一个一元运算符,它在其操作数之后立即写入,如表达式a!中的后缀forced-unwrap运算符(!)。
声明一个新的操作符后,通过声明一个与操作符名称相同的静态方法来实现它。 静态方法是运算符将其值作为参数的值之一的类型的成员,例如,将Double乘以Int的运算符作为Double或Int结构中的静态方法实现。 如果要实现前缀或后缀运算符,则还必须使用相应的前缀或后缀声明修饰符标记该方法声明。 要查看如何创建和实现新操作符的示例,请参阅自定义操作符
operator-declaration → prefix-operator-declaration-postfix-operator-declaration-infix-operator-declaration-
prefix-operator-declaration → prefix-operator-operator-
postfix-operator-declaration → postfix-operator-operator-
infix-operator-declaration → infix-operator-operator-infix-operator-group-opt-
infix-operator-group → :-precedence-group-name-
优先组声明
优先组声明具有以下形式:
precedencegroupprecedence group name {
higherThan: lower group names
lowerThan: higher group names
associativity: associativity
assignment: assignment
}
注意
使用较低组名称和较高组名称相互关联的优先组必须适合单个关系层次结构,但它们不必形成线性层次结构。这意味着有可能使优先级组具有未定义的相对优先级。来自这些优先级组的运算符不能彼此相邻使用,而无需对括号进行分组。
Swift定义了许多优先组以配合标准库提供的运算符。例如,加法(+)和减法( - )运算符属于AdditionPrecedence组,乘法(*)和除法运算符(/)属于MultiplicationPrecedence组。有关Swift标准库提供的优先组的完整列表,请参阅运算符声明。
运算符的关联性指定在没有分组括号的情况下,具有相同优先级的运算符序列如何组合在一起。您可以通过编写一个上下文相关关键字left,right或none来指定运算符的关联性 - 如果您省略关联性,则默认值为none。左联合组的操作员从左到右。例如,减法运算符( - )是左关联的,所以表达式4-5-6被分组为(4-5)-6并且计算为-7。右关联组从右到左的运算符以及没有关联的运算符根本不关联。具有相同优先级的非关联运算符不能彼此相邻。例如,<运算符具有none的关联性,这意味着1 <2 <3不是有效的表达式。
声明修饰符是修饰声明行为或含义的关键字或上下文相关关键字。通过在声明的属性(如果有)和引入声明的关键字之间写入适当的关键字或上下文相关的关键字来指定声明修饰符。
较低的组名称和较高的组名称列表指定新的优先组与现有优先组的关系。 lowerThan precedence组属性只能用于引用在当前模块外声明的优先级组。当两个运算符相互竞争其操作数时,例如在表达式2 + 3 * 5中,具有较高相对优先级的运算符与其操作数更紧密地绑定。
优先级组的分配指定在包含可选链接的操作中使用的运算符的优先级。设置为true时,相应优先级组中的运算符在可选链中使用与标准库中的赋值运算符相同的分组规则。否则,当设置为false或省略时,优先级组中的运算符遵循与不执行赋值的运算符相同的可选链接规则。
precedence-group-declaration → precedencegroup-precedence-group-name-{-precedence-group-attributes-opt-}-
precedence-group-attributes → precedence-group-attribute-precedence-group-attributes-opt-
precedence-group-attribute → precedence-group-relation-
precedence-group-attribute → precedence-group-assignment-
precedence-group-attribute → precedence-group-associativity-
precedence-group-relation → higherThan-:-precedence-group-names-
precedence-group-relation → lowerThan-:-precedence-group-names-
precedence-group-assignment → assignment-:-boolean-literal-
precedence-group-associativity → associativity-:-left-
precedence-group-associativity → associativity-:-right-
precedence-group-associativity → associativity-:-none-
precedence-group-names → precedence-group-name-precedence-group-name-,-precedence-group-names-
precedence-group-name → identifier-
声明修饰符
dynamic
将此修饰符应用于可由Objective-C表示的类的任何成员。当您使用动态修饰符标记成员声明时,对该成员的访问总是使用Objective-C运行时动态分派。对该成员的访问不会由编译器内联或虚拟化。
因为用动态修饰符标记的声明是使用Objective-C运行时分派的,所以它们必须用objc属性标记。
final
将此修饰符应用于类或类的属性,方法或下标成员。它被应用于一个类来表明该类不能被子类化。它应用于类的属性,方法或下标,以指示类成员不能在任何子类中重写。有关如何使用最终属性的示例,请参阅防止覆盖。
lazy
将该修饰符应用于类或结构的存储变量属性,以指示该属性的初始值最初是计算并存储一次,当该属性第一次访问时。有关如何使用lazy修饰符的示例,请参阅Lazy Stored Properties。
optional
将此修饰符应用于协议的属性,方法或下标成员,以指示不需要符合类型来实现这些成员。
您可以将可选修饰符仅应用于使用objc属性标记的协议。因此,只有类别类型可以采用并符合包含可选成员要求的协议。有关如何使用可选修饰符的更多信息,以及有关如何访问可选协议成员的指导(例如,当您不确定某个符合类型是否实现它们时),请参阅可选协议要求。
required
将此修饰符应用于类的指定或便利初始值设定项,以指示每个子类都必须实现该初始化项。该初始化器的子类的实现也必须用所需的修饰符标记。
unowned
将此修饰符应用于存储的变量,常量或存储属性,以指示该变量或属性具有对作为其值存储的对象的无主引用。如果尝试在解除分配对象后访问变量或属性,则会引发运行时错误。像弱引用一样,属性或值的类型必须是类类型;不像弱参考,这种类型是非选择性的。有关无主修饰符的示例和更多信息,请参阅无主参考。
unowned(safe)
无主的明确拼写。
unowned(unsafe)
将此修饰符应用于存储的变量,常量或存储属性,以指示该变量或属性具有对作为其值存储的对象的无主引用。如果在对象释放后尝试访问变量或属性,则会在对象过去存在的位置访问内存,这是内存不安全的操作。像弱引用一样,属性或值的类型必须是类类型;不像弱参考,这种类型是非选择性的。有关无主修饰符的示例和更多信息,请参阅无主参考。
weak
Swift提供了五个访问控制级别:开放,公共,内部,文件私有和私有。您可以用下面的访问级别修饰符之一标记一个声明来指定声明的访问级别。访问控制在访问控制中进行了详细讨论。
将此修饰符应用于存储的变量或存储的变量属性,以指示变量或属性对作为其值存储的对象具有弱引用。变量或属性的类型必须是可选的类类型。如果在对象被释放后访问变量或属性,则其值为零。有关弱修饰符的示例和更多信息,请参阅弱引用。
访问控制级别
open
将此修饰符应用于声明,以指示声明可以通过与声明相同的模块中的代码进行访问和分类。使用开放访问级别修饰符标记的声明也可以通过导入包含该声明的模块的模块中的代码进行访问和分类。
public
将此修饰符应用于声明,以指示声明可以通过与声明相同的模块中的代码进行访问和分类。使用公共访问级别修饰符标记的声明也可以通过导入包含该声明的模块的模块中的代码访问(但不是子类别)。
internal
将此修饰符应用于声明以指示声明只能由与声明相同的模块中的代码访问。默认情况下,大多数声明都使用内部访问级别修饰符隐式标记。
fileprivate
将此修饰符应用于声明以指示声明只能由与声明相同的源文件中的代码访问。
private
将此修饰符应用于声明以指示声明只能由声明的直接封闭范围内的代码访问。
为了进行访问控制,对同一文件中相同类型的扩展共享一个访问控制范围。如果它们扩展的类型也在同一个文件中,则它们共享该类型的访问控制范围。在类型声明中声明的私有成员可以从扩展中访问,并且可以从其他扩展和类型声明中访问在一个扩展中声明的私有成员。
上面的每个访问级别修饰符都可选择接受一个参数,该参数由括在圆括号中的set关键字(例如private(set))组成。如果要为变量或下标的设置者指定访问级别小于或等于变量或下标本身的访问级别,请使用此形式的访问级别修饰符,如Getters and Setters中所述
GRAMMAR OF A DECLARATION MODIFIER
declaration-modifier → class-convenience-dynamic-final-infix-lazy-optional-override-postfix-prefix-required-static-unowned-unowned-(-safe-)-unowned-(-unsafe-)-weak-
declaration-modifier → access-level-modifier-
declaration-modifier → mutation-modifier-
declaration-modifiers → declaration-modifier-declaration-modifiers-opt-
access-level-modifier → private-private-(-set-)-
access-level-modifier → fileprivate-fileprivate-(-set-)-
access-level-modifier → internal-internal-(-set-)-
access-level-modifier → public-public-(-set-)-
access-level-modifier → open-open-(-set-)-
mutation-modifier → mutating-nonmutating-