Swift 声明
声明(declaration) 用以向程序里引入新的名字或者结构。举例来说,可以使用声明来引入函数和方法,变量和常量,或者定义新的具有命名的枚举、结构体、类和协议类型。还可以使用声明来扩展一个既有的具有命名的类型的行为,或者在程序里引入在其它地方声明的符号。
在 Swift 中,大多数声明在某种意义上讲也是定义,因为它们在声明时往往伴随着实现或初始化。由于协议并不提供实现,大多数协议成员仅仅只是声明而已。为了方便起见,也是因为这些区别在 Swift 中并不是很重要,“声明”这个术语同时包含了声明和定义两种含义。
声明语法
declaration
声 明 → 导入声明
声明 → 常量声明
声明 → 变量声明
声明 → 类型别名声明
声明 → 函数声明
声明 → 枚举声明
声明 → 结构体声明
声明 → 类声明
声明 → actor 声明
声明 → 协议声明
声明 → 构造器声明
声明 → 析构器声明
声明 → 扩展声明
声明 → 下标声明
声明 → 运算符声明
declarations
顶级代码
Swift 的源文件中的顶级代码(top-level code)由零个或多个语句、声明和表达式组成。默认情况下,在一个源文件的顶层声明的变量,常量和其他具有命名的声明可以被同模块中的每一个源文件中的代码访问。可以使用一个访问级别修饰符来 标记声明来覆盖这种默认行为,请参阅 访问控制级别。
顶级声明语法
顶级声明 → 多条语句可选
代码块
代码块(code block) 可以将一些声明和控制结构体组织在一起。它有如下的形式:
{
语句
}
代码块中的“语句”包括声明、表达式和各种其他类型的语句,它们按照在源码中的出现顺序被依次执行。
代码块语法
code-block
代码块 → { 多条语句可选 }
导入声明
导入声明(import declaration) 让你可以使用在其他文件中声明的内容。导入语句的基本形式是导入整个模块,它由 import
关键字和紧随其后的模块名组成:
import 模块
可以对导入操作提供更细致的控制,如指定一个特殊的子模块或者指定一个模块或子模块中的某个声明。提供了这些限制后,在当前作用域中,只有被导入的符号是可用的,而不是整个模块中的所有声明。
import 导入类型 模块.符号名
import 模块.子模块
grammer-of-an-import-declaration
导入声明语法
import-declaration
import-kind
导入类型 → typealias | struct | class | enum | protocol | let | var | func
import-path
import-path-identifier
常量声明
常量声明(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
声明修饰符实现允许或禁止被子类重写的目的。类型属性在 类型属性 中有介绍。
如果还想获得更多关于常量的信息或者想在使用中获得帮助,请参阅 常量和变量 和 存储属性。
grammer-of-a-constant-declaration
常量声明语法
constant-declaration
pattern-initializer-list
pattern-initializer
initializer
构造器 → = 表达式
变量声明
变量声明(variable declaration) 可以在程序中引入一个具有命名的变量,它以关键字 var
来声明。
变量声明有几种不同的形式,可以声明不同种类的命名值和可变值,如存储型和计算型变量和属性,属性观察器,以及静态变量属性。所使用的声明形式取决于变量声明的适用范围和打算声明的变量类型。
注意
也可以在协议声明中声明属性,详情请参阅 协议属性声明。
可以在子类中重写继承来的变量属性,使用 override
声明修饰符标记属性的声明即可,详情请参阅 重写。
存储型变量和存储型变量属性
使用如下形式声明一个存储型变量或存储型变量属性:
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
则是 可选的。
要获得更多信息以及查看如何使用属性观察器的例子,请参阅 属性观察器。
类型变量属性
要声明一个类型变量属性,用 static
声明修饰符标记该声明。类可以改用 class
声明修饰符标记类的类型计算型属性从而允许子类重写超类的实现。类型属性在 类型属性 章节有详细讨论。
grammer-of-a-variable-declaration
变量声明语法
variable-declaration
变量声明 → 变量声明头 变量名称 类型注解 getter-setter 代码块
变量声明 → 变量声明头 变量名称 类型注解 getter-setter 关键字代码块
变量声明 → 变量声明头 变量名称 构造器 willSet-didSet 代码块
变量声明 → 变量声明头 变量名称 类型注解 构造器可选 willSet-didSet 代码块
variable-declaration-head
variable-name
变量名称 → 标识符
getter-setter-block
getter-setter 代码块 → 代码块
getter-clause
setter-clause
setter-name
setter 名称 → ( 标识符 )
getter-setter-keyword-block
getter-setter 关键字代码块 → { getter 关键字子句 setter 关键字子句可选 }
getter-setter 关键字代码块 → { setter 关键字子句 getter 关键字子句 }
getter-keyword-clause
getter 关键字子句 → 特性列表可选 get
setter-keyword-clause
setter 关键字子句 → 特性列表可选 set
willSet-didSet-block
willSet-didSet 代码块 → { willSet 子句 didSet 子句可选 }
willSet-didSet 代码块 → { didSet 子句 willSet 子句可选 }
willSet-clause
didSet-clause
类型别名声明
类型别名(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