Swift4.2语言参考(六) 声明
一个声明引入了一个新的名称或构建到你的程序。例如,您使用声明来引入函数和方法,引入变量和常量,以及定义枚举,结构,类和协议类型。您还可以使用声明来扩展现有命名类型的行为,并将符号导入到其他地方声明的程序中。 在Swift中,大多数声明也是定义,因为它们是在声明它们的同时实现或初始化的。也就是说,由于协议不实现其成员,因此大多数协议成员仅是声明。为方便起见,因为在Swift中区别并不重要,术语声明包括声明和定义。 1 GRAMMAR OF A DECLARATION 2 3 declaration → import-declaration 4 5 declaration → constant-declaration 6 7 declaration → variable-declaration 8 9 declaration → typealias-declaration 10 11 declaration → function-declaration 12 13 declaration → enum-declaration 14 15 declaration → struct-declaration 16 17 declaration → class-declaration 18 19 declaration → protocol-declaration 20 21 declaration → initializer-declaration 22 23 declaration → deinitializer-declaration 24 25 declaration → extension-declaration 26 27 declaration → subscript-declaration 28 29 declaration → operator-declaration 30 31 declaration → precedence-group-declaration 32 33 declarations → declaration declarations opt 顶级代码Swift源文件中的顶级代码由零个或多个语句,声明和表达式组成。默认情况下,属于同一模块的每个源文件中的代码都可以访问在源文件顶层声明的变量,常量和其他命名声明。您可以通过使用访问级别修饰符标记声明来覆盖此默认行为,如访问控制级别中所述。 1 GRAMMAR OF A TOP-LEVEL DECLARATION 2 3 top-level-declaration → statements opt 代码块甲码块是由各种声明和控制结构,以组语句一起使用。它具有以下形式: 1 { 2 statements 3 } 代码块中的语句包括声明,表达式和其他类型的语句,并按照它们在源代码中的出现顺序执行。 1 GRAMMAR OF A CODE BLOCK 2 3 code-block → { statements opt } 引用声明一个引用声明,您可以访问声明当前文件之外的符号。基本表单导入整个模块;?它由 import module 提供更多详细信息限制导入的符号 - 您可以在模块或子模块中指定特定子模块或特定声明。使用此详细表单时,只有导入的符号(而不是声明它的模块)在当前作用域中可用。 1 import import kind module.symbol name 2 import module.submodule 1 GRAMMAR OF AN IMPORT DECLARATION 2 3 import-declaration → attributes opt import import-kind opt import-path 4 5 import-kind → typealias | struct | class | enum | protocol | let | var | func 6 7 import-path → import-path-identifier | import-path-identifier . import-path 8 9 import-path-identifier → identifier | operator 常数声明一个常数声明引入了一个名为常量的值到你的程序。使用 let constant name: type = expression 常量声明定义常量名称和初始化表达式值之间的不可变绑定;?设置常量值后,不能更改。也就是说,如果使用类对象初始化常量,则对象本身可以更改,但常量名称与其引用的对象之间的绑定不能。 当在全局范围内声明常量时,必须使用值初始化它。当在函数或方法的上下文中发生常量声明时,可以稍后对其进行初始化,只要保证在第一次读取其值之前设置值即可。当在类或结构声明的上下文中发生常量声明时,它被视为常量属性。常量声明不是计算属性,因此没有getter或setter。 如果常量声明的常量名称是元组模式,则元组中每个项的名称将绑定到初始化表达式中的相应值。 let (firstNumber,secondNumber) = (10,42) 在此示例中, 1 print("The first number is (firstNumber).") 2 // Prints "The first number is 10." 3 print("The second number is (secondNumber).") 4 // Prints "The second number is 42." 当可以推断常量名称的类型时,类型注释( 要声明常量类型属性,请使用 有关常量的更多信息以及有关何时使用它们的指导,请参阅常量和变量以及存储的属性。 1 GRAMMAR OF A CONSTANT DECLARATION 2 3 constant-declaration → attributes opt declaration-modifiers opt let pattern-initializer-list 4 5 pattern-initializer-list → pattern-initializer | pattern-initializer,pattern-initializer-list 6 7 pattern-initializer → pattern initializer opt 8 9 initializer → = expression 变量声明一个变量声明引入了一个名为变量值到你的程序,并使用声明 变量声明有几种形式,用于声明不同类型的命名可变值,包括存储和计算变量和属性,存储变量和属性观察器以及静态变量属性。要使用的适当形式取决于声明变量的范围以及要声明的变量类型。 注意 您还可以在协议声明的上下文中声明属性,如协议属性声明中所述。 您可以通过使用 存储变量和存储变量属性以下表单声明存储变量或存储变量属性: var variable name: type = expression
您可以在全局范围,函数的本地范围或类或结构声明的上下文中定义此形式的变量声明。当在全局范围或函数的局部范围声明此表单的变量声明时,它被称为存储变量。当它在类或结构声明的上下文中声明时,它被称为存储变量属性。 初始化表达式不能出现在协议声明中,但在所有其他上下文中,初始化表达式是可选的。也就是说,如果不存在初始化表达式,则变量声明必须包含显式类型注释( 与常量声明一样,如果变量名是元组模式,则元组中每个项的名称将绑定到初始化表达式中的相应值。 顾名思义,存储变量或存储变量属性的值存储在内存中。 计算变量和计算属性以下表单声明计算变量或计算属性: 1 var variable name: type { 2 get { 3 statements 4 } 5 set(setter name) { 6 statements 7 } 8 } 您可以在全局范围,函数的本地范围或类,结构,枚举或扩展声明的上下文中定义此形式的变量声明。当在全局范围或函数的局部范围声明此形式的变量声明时,它被称为计算变量。当它在类,结构或扩展声明的上下文中声明时,它被称为计算属性。 getter用于读取值,setter用于写入值。setter子句是可选的,当只需要一个getter时,你可以省略这两个子句,直接返回所请求的值,如Read-Only Computed Properties中所述。但是,如果提供setter子句,则还必须提供getter子句。 该二传手名称和圆括号是可选的。如果提供setter名称,则将其用作setter参数的名称。如果未提供setter名称,则seter的默认参数名称为 与存储的命名值和存储的变量属性不同,计算的命名值或计算属性的值不存储在内存中。 有关更多信息以及查看计算属性的示例,请参阅计算属性。 存储变量观察器和属性观察器您还可以使用 1 var variable name: type = expression { 2 willSet(setter name) { 3 statements 4 } 5 didSet(setter name) { 6 statements 7 } 8 } 您可以在全局范围,函数的本地范围或类或结构声明的上下文中定义此形式的变量声明。当在全局范围或函数的局部范围声明此表单的变量声明时,观察器称为存储变量观察器。当在类或结构声明的上下文中声明它时,观察器被称为属性观察器。 您可以将属性观察器添加到任何存储的属性。您还可以通过覆盖子类中的属性将属性观察器添加到任何继承的属性(无论是存储还是计算),如覆盖属性观察器中所述。 初始化表达式在类或结构声明的上下文中是可选的,但在其他地方需要。该类型的注释是可选的,当类型可以从初始化推断表达。 的 一个 和子句中的setter名称和括号是可选的。如果提供setter名称,它们将用作和观察器的参数名称。如果不提供setter名称,则观察器的默认参数名称为观察器的默认参数名称。
有关更多信息以及查看如何使用属性观察器的示例,请参阅Property Observers。 变量属性要声明类型变量属性,请使用 注意 在类声明中, 1 GRAMMAR OF A VARIABLE DECLARATION 2 3 variable-declaration → variable-declaration-head pattern-initializer-list 4 5 variable-declaration → variable-declaration-head variable-name type-annotation code-block 6 7 variable-declaration → variable-declaration-head variable-name type-annotation getter-setter-block 8 9 variable-declaration → variable-declaration-head variable-name type-annotation getter-setter-keyword-block 10 11 variable-declaration → variable-declaration-head variable-name initializer willSet-didSet-block 12 13 variable-declaration → variable-declaration-head variable-name type-annotation initializer opt willSet-didSet-block 14 15 variable-declaration-head → attributes opt declaration-modifiers opt var 16 17 variable-name → identifier 18 19 getter-setter-block → code-block 20 21 getter-setter-block → { getter-clause setter-clause opt } 22 23 getter-setter-block → { setter-clause getter-clause } 24 25 getter-clause → attributes opt mutation-modifier opt get code-block 26 27 setter-clause → attributes opt mutation-modifier opt set setter-name opt code-block 28 29 setter-name → ( identifier ) 30 31 getter-setter-keyword-block → { getter-keyword-clause setter-keyword-clause opt } 32 33 getter-setter-keyword-block → { setter-keyword-clause getter-keyword-clause } 34 35 getter-keyword-clause → attributes opt mutation-modifier opt get 36 37 setter-keyword-clause → attributes opt mutation-modifier opt set 38 39 willSet-didSet-block → { willSet-clause didSet-clause opt } 40 41 willSet-didSet-block → { didSet-clause willSet-clause opt } 42 43 willSet-clause → attributes opt willSet setter-name opt code-block 44 45 didSet-clause → attributes opt didSet setter-name opt code-block 类型别名声明一个类型别名声明介绍了现有类型的命名别名到你的程序。类型别名声明使用 typealias name = existing type 一个类型别名声明后,别名名称可以代替的现有类型无处不在你的程序。在现有类型可以是命名型或复合型。类型别名不会创建新类型;?它们只是允许名称引用现有类型。 类型别名声明可以使用泛型参数为现有泛型类型指定名称。类型别名可以为现有类型的部分或全部通用参数提供具体类型。例如: 1 typealias StringDictionary<Value> = Dictionary<String,Value> 2 3 // The following dictionaries have the same type. 4 var dictionary1: StringDictionary<Int> = [:] 5 var dictionary2: Dictionary<String,Int> = [:] 当使用泛型参数声明类型别名时,对这些参数的约束必须与现有类型的泛型参数的约束完全匹配。例如: typealias DictionaryOfInts<Key: Hashable> = Dictionary<Key,Int> 由于类型别名和现有类型可以互换使用,因此类型别名不能引入其他通用约束。 类型别名可以通过省略声明中的所有泛型参数来转发现有类型的泛型参数。例如, typealias Diccionario = Dictionary 在协议声明中,类型别名可以为经常使用的类型提供更简单,更方便的名称。例如: 1 protocol Sequence { 2 associatedtype Iterator: IteratorProtocol 3 typealias Element = Iterator.Element 4 } 5 6 func sum<T: Sequence>(_ sequence: T) -> Int where T.Element == Int { 7 // ... 8 } 如果没有此类别别名,则该 另请参阅协议关联类型声明。 1 GRAMMAR OF A TYPE ALIAS DECLARATION 2 3 typealias-declaration → attributes opt access-level-modifier opt typealias typealias-name generic-parameter-clause opt typealias-assignment 4 5 typealias-name → identifier 6 7 typealias-assignment → = type 函数声明一个函数声明引入了一个函数或方法到你的程序。在类,结构,枚举或协议的上下文中声明的函数称为方法。函数声明使用 1 func function name(parameters) -> return type { 2 statements 3 } 如果函数的返回类型为 1 func function name(parameters) { 2 statements 3 } 必须包含每个参数的类型 - 无法推断。如果 函数可以使用元组类型作为函数的返回类型返回多个值。 函数定义可以出现在另一个函数声明中。这种函数称为嵌套函数。 如果嵌套函数捕获保证永不转义的值(例如输入输出参数)或作为非转义函数参数传递,则嵌套函数是非转义的。否则,嵌套函数是一个转义函数。 有关嵌套函数的讨论,请参阅嵌套函数。 参数名称函数参数是以逗号分隔的列表,其中每个参数都具有多种形式之一。函数调用中参数的顺序必须与函数声明中的参数顺序相匹配。参数列表中最简单的条目具有以下形式: parameter name: parameter type 参数具有在函数体中使用的名称,以及在调用函数或方法时使用的参数标签。默认情况下,参数名称也用作参数标签。例如: 1 func f(x: Int,y: Int) -> Int { return x + y } 2 f(x: 1,y: 2) // both x and y are labeled 您可以使用以下格式之一覆盖参数标签的默认行为: 1 argument label parameter name: parameter type 2 _ parameter name: parameter type 参数名称前面的名称为参数提供显式参数标签,该标签可以与参数名称不同。相应的参数必须在函数或方法调用中使用给定的参数标签。
1 func repeatGreeting(_ greeting: String,count n: Int) { /* Greet n times */ } 2 repeatGreeting("Hello,world!",count: 2) // count is labeled,greeting is not 输入输出参数输入输出参数传递如下:
此行为称为copy-in copy-out或按值调用结果。例如,当计算属性或具有观察器的属性作为输入输出参数传递时,其getter作为函数调用的一部分被调用,其setter作为函数返回的一部分被调用。 作为优化,当参数是存储在存储器中的物理地址处的值时,在函数体内部和外部使用相同的存储器位置。优化的行为称为引用调用?;?它满足了拷入式拷贝模型的所有要求,同时消除了复制的开销。使用copy-in copy-out给出的模型编写代码,而不依赖于逐个引用的优化,以便在有或没有优化的情况下它的行为正确。 在函数内,不要访问作为输入输出参数传递的值,即使原始值在当前范围内可用。访问原始版本是同时访问该值,这违反了Swift的内存独占性保证。出于同样的原因,您无法将相同的值传递给多个输入输出参数。 有关内存安全性和内存独占性的更多信息,请参阅内存安全性。 捕获输入输出参数的闭包或嵌套函数必须是非溢出的。如果您需要捕获in-out参数而不进行变更或观察其他代码所做的更改,请使用捕获列表以不可变的方式显式捕获参数。 1 func someFunction(a: inout Int) -> () -> Int { 2 return { [a] in return a + 1 } 3 } 如果需要捕获并改变输入输出参数,请使用显式本地副本,例如在多线程代码中确保在函数返回之前完成所有变异。 1 func multithreadedFunction(queue: DispatchQueue,x: inout Int) { 2 // Make a local copy and manually copy it back. 3 var localX = x 4 defer { x = localX } 5 6 // Operate on localX asynchronously,then wait before returning. 7 queue.async { someMutatingOperation(&localX) } 8 queue.sync {} 9 } 有关输入输出参数的更多讨论和示例,请参阅输入输出参数。 特殊参数可以忽略参数,获取可变数量的值,并使用以下形式提供默认值: 1 _ : parameter type 2 parameter name: parameter type... 3 parameter name: parameter type = default argument value 下划线( 具有基本类型名称后面紧跟三个点( 具有等号( 1 func f(x: Int = 42) -> Int { return x } 2 f() // Valid,uses default value 3 f(x: 7) // Valid,uses the value provided 4 f(7) // Invalid,missing argument label 特殊方法枚举或修改的结构上的方法 必须使用 与类型而不是类型实例关联的方法必须使用 投掷功能和方法必须使用 1 func function name(parameters) throws -> return type { 2 statements 3 } 对throw函数或方法的调用必须包装在一个 该 您不能仅基于函数是否可以引发错误来重载函数。也就是说,您可以根据函数参数是否可以引发错误来重载函数。 投掷方法不能覆盖非投掷方法,投掷方法不能满足非投掷方法的协议要求。也就是说,非投掷方法可以覆盖投掷方法,非投掷方法可以满足投掷方法的协议要求。 重新定义函数和方法可以使用 1 func someFunction(callback: () throws -> Void) rethrows { 2 try callback() 3 } 重新抛出函数或方法 1 func alwaysThrows() throws { 2 throw SomeError.error 3 } 4 func someFunction(callback: () throws -> Void) rethrows { 5 do { 6 try callback() 7 try alwaysThrows() // Invalid,alwaysThrows() isn‘t a throwing parameter 8 } catch { 9 throw AnotherError.error 10 } 11 } 投掷方法不能覆盖重新抛出方法,投掷方法不能满足重新抛出方法的协议要求。也就是说,重新抛出方法可以覆盖投掷方法,并且重新抛出方法可以满足投掷方法的协议要求。 非返回的功能Swift定义了一个 可以调用非返回函数或方法来结束 您可以覆盖非返回方法,但新方法必须保留其返回类型和非返回行为。 1 GRAMMAR OF A FUNCTION DECLARATION 2 3 function-declaration → function-head function-name generic-parameter-clause opt function-signature generic-where-clause opt function-body opt 4 5 function-head → attributes opt declaration-modifiers opt func 6 7 function-name → identifier | operator 8 9 function-signature → parameter-clause throwsopt function-result opt 10 11 function-signature → parameter-clause rethrows function-result opt 12 13 function-result → -> attributes opt type 14 15 function-body → code-block 16 17 parameter-clause → ( ) | ( parameter-list ) 18 19 parameter-list → parameter | parameter,parameter-list 20 21 parameter → external-parameter-name opt local-parameter-name type-annotation default-argument-clause opt 22 23 parameter → external-parameter-name opt local-parameter-name type-annotation 24 25 parameter → external-parameter-name opt local-parameter-name type-annotation ... 26 27 external-parameter-name → identifier 28 29 local-parameter-name → identifier 30 31 default-argument-clause → = expression 枚举声明一个枚举声明引入了一个名为枚举类型到你的程序。 枚举声明有两种基本形式,并使用 枚举类型可以采用任意数量的协议,但不能从类,结构或其他枚举继承。 与类和结构不同,枚举类型没有隐式提供的默认初始值设定项;?必须明确声明所有初始值设定项。初始化程序可以委托给枚举中的其他初始化程序,但只有在初始化程序将其中一个枚举情况分配给初始化程序后,初始化过程才会完成 与结构类似,但与类不同,枚举是值类型;?枚举的实例在分配给变量或常量时复制,或者作为参数传递给函数调用时复制。有关值类型的信息,请参阅结构和枚举是值类型。 您可以使用扩展声明扩展枚举类型的行为,如扩展声明中所述。 任何类型的枚举个案以下表单声明了一个枚举类型,其中包含任何类型的枚举个案: 1 enum enumeration name: adopted protocols { 2 case enumeration case 1 3 case enumeration case 2(associated value types) 4 } 以这种形式声明的枚举有时被称为其他编程语言中的歧视联合。 在此表单中,每个案例块由 存储关联值的枚举情况可用作创建具有指定关联值的枚举实例的函数。就像函数一样,您可以获得对枚举案例的引用,并在稍后的代码中应用它。 1 enum Number { 2 case integer(Int) 3 case real(Double) 4 } 5 let f = Number.integer 6 // f is a function of type (Int) -> Number 7 8 // Apply f to create an array of Number instances with integer values 9 let evenInts: [Number] = [0,2,4,6].map(f) 有关更多信息以及查看具有关联值类型的案例示例,请参阅关联值。 具有间接的枚举枚举可以具有递归结构,也就是说,它们可以具有关联值的案例,这些值是枚举类型本身的实例。但是,枚举类型的实例具有值语义,这意味着它们在内存中具有固定的布局。为了支持递归,编译器必须插入一个间接层。 要为特定枚举情况启用间接寻址,请使用 1 enum Tree<T> { 2 case empty 3 indirect case node(value: T,left: Tree,right: Tree) 4 } 要为具有关联值的枚举的所有情况启用间接,请使用 使用 有关原始价值案件的查点以下表单声明一个枚举类型,其中包含相同基本类型的枚举个案: 1 enum enumeration name: raw-value type,adopted protocols { 2 case enumeration case 1 = raw value 1 3 case enumeration case 2 = raw value 2 4 } 在此表单中,每个案例块由 如果原始值的类型被指定为 1 enum ExampleEnum: Int { 2 case a,b,c = 5,d 3 } 在上面的例子中,原始值 如果将原始值类型指定为 1 enum GamePlayMode: String { 2 case cooperative,individual,competitive 3 } 在上面的例子中,原始值 具有原始值类型的枚举隐式地符合 访问枚举案例要引用枚举类型的大小写,请使用dot( 要检查枚举情况的值,请使用 1 GRAMMAR OF AN ENUMERATION DECLARATION 2 3 enum-declaration → attributes opt access-level-modifier opt union-style-enum 4 5 enum-declaration → attributes opt access-level-modifier opt raw-value-style-enum 6 7 union-style-enum → indirectopt enum enum-name generic-parameter-clause opt type-inheritance-clause opt generic-where-clause opt { union-style-enum-members opt } 8 9 union-style-enum-members → union-style-enum-member union-style-enum-members opt 10 11 union-style-enum-member → declaration | union-style-enum-case-clause | compiler-control-statement 12 13 union-style-enum-case-clause → attributes opt indirectopt case union-style-enum-case-list 14 15 union-style-enum-case-list → union-style-enum-case | union-style-enum-case,union-style-enum-case-list 16 17 union-style-enum-case → enum-case-name tuple-type opt 18 19 enum-name → identifier 20 21 enum-case-name → identifier 22 23 raw-value-style-enum → enum enum-name generic-parameter-clause opt type-inheritance-clause generic-where-clause opt { raw-value-style-enum-members } 24 25 raw-value-style-enum-members → raw-value-style-enum-member raw-value-style-enum-members opt 26 27 raw-value-style-enum-member → declaration | raw-value-style-enum-case-clause | compiler-control-statement 28 29 raw-value-style-enum-case-clause → attributes opt case raw-value-style-enum-case-list 30 31 raw-value-style-enum-case-list → raw-value-style-enum-case | raw-value-style-enum-case,raw-value-style-enum-case-list 32 33 raw-value-style-enum-case → enum-case-name raw-value-assignment opt 34 35 raw-value-assignment → = raw-value-literal 36 37 raw-value-literal → numeric-literal | static-string-literal | boolean-literal 结构声明一个结构声明引入了一个名为结构类型到你的程序。结构声明使用 1 struct structure name: adopted protocols { 2 declarations 3 } 结构体包含零个或多个声明。这些声明可以包括存储和计算属性,类型属性,实例方法,类型方法,初始化器,下标,类型别名,甚至其他结构,类和枚举声明。结构声明不能包含deinitializer或协议声明。有关包含各种声明的结构的讨论和几个示例,请参阅结构和类。 结构类型可以采用任意数量的协议,但不能从类,枚举或其他结构继承。 有三种方法可以创建先前声明的结构的实例:
初始化中描述了初始化结构声明的属性的过程。 可以使用dot( 结构是价值类型;?在分配给变量或常量时,或者作为参数传递给函数调用时,将复制结构的实例。有关值类型的信息,请参阅结构和枚举是值类型。 您可以使用扩展声明扩展结构类型的行为,如扩展声明中所述。 1 GRAMMAR OF A STRUCTURE DECLARATION 2 3 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 4 5 struct-name → identifier 6 7 struct-body → { struct-members opt } 8 9 struct-members → struct-member struct-members opt 10 11 struct-member → declaration | compiler-control-statement 类声明一类声明引入了一个名为类类型到你的程序。类声明使用 1 class class name: superclass,adopted protocols { 2 declarations 3 } 类的主体包含零个或多个声明。这些声明可以包括存储和计算属性,实例方法,类型方法,初始化器,单个取消初始化器,下标,类型别名,甚至其他类,结构和枚举声明。类声明不能包含协议声明。有关包含各种声明的类的讨论和几个示例,请参阅结构和类。 类类型只能从一个父类(超类)继承,但可以采用任意数量的协议。该超后第一次出现的类名和冒号,跟任何通过的议定书。泛型类可以继承自其他泛型和非泛型类,但非泛型类只能从其他非泛型类继承。在冒号后写入通用超类的名称时,必须包含该泛型类的全名,包括其泛型参数子句。 正如初始化程序声明中所讨论的,类可以具有指定和便利初始化程序。类的指定初始值设定项必须初始化所有类的声明属性,并且必须在调用其任何超类的指定初始值设定项之前执行此操作。 类可以覆盖其超类的属性,方法,下标和初始值设定项。必须使用 要求子类实现超类的初始值设定项,请使用 虽然在超类中声明的属性和方法是由当前类继承的,但是当子类满足自动初始化程序继承中描述的条件时,才会继承在超类中声明的指定初始值设定项。Swift类不从通用基类继承。 有两种方法可以创建先前声明的类的实例:
使用dot( 类是引用类型;?当分配给变量或常量时,或者作为参数传递给函数调用时,类的实例被引用而不是复制。有关引用类型的信息,请参阅结构和枚举是值类型。 您可以使用扩展声明扩展类类型的行为,如扩展声明中所述。 1 GRAMMAR OF A CLASS DECLARATION 2 3 class-declaration → attributes opt access-level-modifier opt finalopt class class-name generic-parameter-clause opt type-inheritance-clause opt generic-where-clause opt class-body 4 5 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 6 7 class-name → identifier 8 9 class-body → { class-members opt } 10 11 class-members → class-member class-members opt 12 13 class-member → declaration | compiler-control-statement 协议声明一个协议声明引入了一个名为协议类型到你的程序。协议声明使用 1 protocol protocol name: inherited protocols { 2 protocol member declarations 3 } 协议主体包含零个或多个协议成员声明,这些声明描述了采用协议的任何类型必须满足的一致性要求。特别是,协议可以声明符合类型必须实现某些属性,方法,初始值设定项和下标。协议还可以声明特殊类型的别名,称为关联类型,可以指定协议的各种声明之间的关系。协议声明不能包含类,结构,枚举或其他协议声明。该协议的成员声明在下面详细讨论。 协议类型可以从任何数量的其他协议继承。当协议类型继承自其他协议时,会聚合来自其他协议的一组要求,并且从当前协议继承的任何类型都必须符合所有这些要求。有关如何使用协议继承的示例,请参阅协议继承。 注意 您还可以使用协议组合类型聚合多个协议的一致性要求,如协议组合类型和协议组合中所述。 您可以通过在该类型的扩展声明中采用协议,将协议一致性添加到先前声明的类型。在扩展中,您必须实现所有采用的协议要求。如果类型已经实现了所有要求,则可以将扩展声明的主体留空。 默认情况下,符合协议的类型必须实现协议中声明的所有属性,方法和下标。也就是说,您可以使用 要仅将协议的采用限制为类类型,请在冒号后的继承协议列表中包含该 1 protocol SomeProtocol: AnyObject { 2 /* Protocol members go here */ 3 } 从标记有 注意 如果协议标记有该 协议是命名类型,因此它们可以出现在代码中与其他命名类型相同的位置,如Protocols as Types中所述。但是,您无法构造协议的实例,因为协议实际上并不提供它们指定的要求的实现。 您可以使用协议,声明其方法的类或结构的代表应该执行,如在代表团。 1 GRAMMAR OF A PROTOCOL DECLARATION 2 3 protocol-declaration → attributes opt access-level-modifier opt protocol protocol-name type-inheritance-clause opt generic-where-clause opt protocol-body 4 5 protocol-name → identifier 6 7 protocol-body → { protocol-members opt } 8 9 protocol-members → protocol-member protocol-members opt 10 11 protocol-member → protocol-member-declaration | compiler-control-statement 12 13 protocol-member-declaration → protocol-property-declaration 14 15 protocol-member-declaration → protocol-method-declaration 16 17 protocol-member-declaration → protocol-initializer-declaration 18 19 protocol-member-declaration → protocol-subscript-declaration 20 21 protocol-member-declaration → protocol-associated-type-declaration 22 23 protocol-member-declaration → typealias-declaration 协议声明协议声明符合类型必须通过在协议声明的主体中包含协议属性声明来实现属性。协议属性声明具有特殊形式的变量声明: var property name: type { get set } 与其他协议成员声明一样,这些属性声明仅声明符合协议的类型的getter和setter要求。因此,您不会直接在声明它的协议中实现getter或setter。 getter和setter要求可以通过各种方式的符合类型来满足。如果属性声明包含 另见变量声明。 1 GRAMMAR OF A PROTOCOL PROPERTY DECLARATION 2 3 protocol-property-declaration → variable-declaration-head variable-name type-annotation getter-setter-keyword-block 协议方法声明协议声明符合类型必须通过在协议声明的主体中包含协议方法声明来实现方法。协议方法声明与函数声明具有相同的形式,但有两个例外:它们不包含函数体,并且您不能提供任何默认参数值作为函数声明的一部分。有关实现协议方法要求的符合类型的示例,请参阅方法要求。 要在协议声明中声明类或静态方法要求,请使用 另请参见功能声明。 1 GRAMMAR OF A PROTOCOL METHOD DECLARATION 2 3 protocol-method-declaration → function-head function-name generic-parameter-clause opt function-signature generic-where-clause opt 协议初始化程序声明协议声明符合类型必须通过在协议声明的主体中包含协议初始化程序声明来实现初始化程序。协议初始化程序声明与初始化程序声明具有相同的形式,但它们不包括初始化程序的主体。 符合类型可以通过实现不可用的初始化器或 当类实现初始化程序以满足协议的初始化程序要求时, 另请参见初始化程序声明。 1 GRAMMAR OF A PROTOCOL INITIALIZER DECLARATION 2 3 protocol-initializer-declaration → initializer-head generic-parameter-clause opt parameter-clause throwsopt generic-where-clause opt 4 5 protocol-initializer-declaration → initializer-head generic-parameter-clause opt parameter-clause rethrows generic-where-clause opt 协议下标声明协议声明符合类型必须通过在协议声明的主体中包含协议下标声明来实现下标。协议下标声明具有特殊形式的下标声明: subscript (parameters) -> return type { get set } 下标声明仅声明符合协议的类型的最小getter和setter实现要求。如果下标声明包含 另见下标声明。 1 GRAMMAR OF A PROTOCOL SUBSCRIPT DECLARATION 2 3 protocol-subscript-declaration → subscript-head subscript-result generic-where-clause opt getter-setter-keyword-block 关联类型声明协议使用 您可以 1 protocol SomeProtocol { 2 associatedtype SomeType 3 } 4 5 protocol SubProtocolA: SomeProtocol { 6 // This syntax produces a warning. 7 associatedtype SomeType: Equatable 8 } 9 10 // This syntax is preferred. 11 protocol SubProtocolB: SomeProtocol where SomeType: Equatable { } 另请参见类型别名声明。 1 GRAMMAR OF A PROTOCOL ASSOCIATED TYPE DECLARATION 2 3 protocol-associated-type-declaration → attributes opt access-level-modifier opt associatedtype typealias-name type-inheritance-clause opt typealias-assignment opt generic-where-clause opt 初始化器声明一个初始化声明引入了一个初始化的一类,结构或枚举到你的程序。初始化器声明使用 结构,枚举和类类型可以包含任意数量的初始值设定项,但类初始值设定项的规则和关联行为是不同的。不同于结构和枚举,类有两种初始化的:指定的初始值和方便初始化,如在初始化。 以下表单声明了类的结构,枚举和指定初始值设定项的初始值设定项: 1 init(parameters) { 2 statements 3 } 类的指定初始值设定项直接初始化所有类的属性。它不能调用同一个类的任何其他初始值设定项,如果该类有一个超类,它必须调用一个超类的指定初始值设定项。如果类从其超类继承任何属性,则必须先调用其中一个超类的指定初始值设定项,然后才能在当前类中设置或修改任何这些属性。 指定的初始值设定项只能在类声明的上下文中声明,因此不能使用扩展声明添加到类中。 结构和枚举中的初始化程序可以调用其他声明的初始化程序来委派部分或全部初始化过程。 要为类声明便捷初始值设定项,请使用 1 convenience init(parameters) { 2 statements 3 } 便捷初始化程序可以将初始化过程委托给另一个便利初始化程序或该类的指定初始化程序之一。也就是说,初始化过程必须以对最终初始化类属性的指定初始化程序的调用结束。便捷初始化程序无法调用超类的初始值设定项。 您可以使用 默认情况下,超类中声明的初始值设定项不会被子类继承。也就是说,如果子类使用默认值初始化其所有存储的属性,并且没有定义自己的任何初始化器,则它继承所有超类的初始值设定项。如果子类重写了所有超类的指定初始值设定项,它将继承超类的便捷初始值设定项。 与方法,属性和下标一样,您需要使用 注意 如果使用 就像函数和方法一样,初始值设定项可以抛出或重新抛出错误。就像函数和方法一样,在初始化程序的参数之后使用 要查看各种类型声明中的初始值设定项示例,请参阅初始化。 可用的初始化程序甲failable初始化是一个类型初始的产生一个可选实例或初始化被声明的类型的隐式展开可选实例。因此,可以使用可用的初始值设定项 要声明一个可生成可选实例的可用初始值设定项,请 1 struct SomeStruct { 2 let property: String 3 // produces an optional instance of ‘SomeStruct‘ 4 init?(input: String) { 5 if input.isEmpty { 6 // discard ‘self‘ and return ‘nil‘ 7 return nil 8 } 9 property = input 10 } 11 } 您可以像调用不可 1 if let actualInstance = SomeStruct(input: "Hello") { 2 // do something with the instance of ‘SomeStruct‘ 3 } else { 4 // initialization of ‘SomeStruct‘ failed and the initializer returned ‘nil‘ 5 } 可执行的初始化程序可以 可用的初始化程序可以委托给任何类型的初始化程序。不可用的初始化程序可以委托给另一个不可用的初始化程序或一个 初始化失败通过初始化程序委派传播。具体来说,如果可用的初始化程序委托给失败并返回 可以通过任何类型的指定初始化程序在子类中覆盖可用的指定初始值设定项。不可用的指定初始值设定项只能由不可用的指定初始化程序在子类中重写。 有关更多信息以及有关可用初始化程序的示例,请参阅Failable Initializers。 1 GRAMMAR OF AN INITIALIZER DECLARATION 2 3 initializer-declaration → initializer-head generic-parameter-clause opt parameter-clause throwsopt generic-where-clause opt initializer-body 4 5 initializer-declaration → initializer-head generic-parameter-clause opt parameter-clause rethrows generic-where-clause opt initializer-body 6 7 initializer-head → attributes opt declaration-modifiers opt init 8 9 initializer-head → attributes opt declaration-modifiers opt init ? 10 11 initializer-head → attributes opt declaration-modifiers opt init ! 12 13 initializer-body → code-block deinitializer声明一个deinitializer声明声明一个类类型deinitializer。Deinitializers没有参数,具有以下形式: 1 deinit { 2 statements 3 } 在取消分配类对象之前,当不再引用类对象时,会自动调用deinitializer。deinitializer只能在类声明的主体中声明 - 但不能在类的扩展中声明 - 每个类最多只能有一个。 子类继承其超类的deinitializer,它在子类对象被释放之前被隐式调用。在继承链中的所有deinitializer完成执行之前,不会释放子类对象。 Deinitializers不是直接调用的。 有关如何在类声明中使用deinitializer的示例,请参阅取消初始化。 1 GRAMMAR OF A DEINITIALIZER DECLARATION 2 3 deinitializer-declaration → attributes opt deinit code-block 扩展声明一个扩展声明可以扩展现有类型的行为。扩展声明使用 1 extension type name where requirements { 2 declarations 3 } 扩展声明的主体包含零个或多个声明。这些声明可以包括计算属性,计算类型属性,实例方法,类型方法,初始化器,下标声明,甚至类,结构和枚举声明。扩展声明不能包含deinitializer或协议声明,存储属性,属性观察器或其他扩展声明。无法标记协议扩展中的声明 如果类型名称是类,结构或枚举类型,则扩展名会扩展该类型。如果类型名称是协议类型,则扩展名将扩展符合该协议的所有类型。 扩展泛型类型或具有关联类型的协议的扩展声明可以包括要求。如果扩展类型或符合扩展协议的类型的实例满足要求,则实例将获得声明中指定的行为。 扩展声明可以包含初始化程序声明。也就是说,如果您扩展的类型是在另一个模块中定义的,则初始化程序声明必须委托给已在该模块中定义的初始化程序,以确保该类型的成员已正确初始化。 无法在该类型的扩展中覆盖现有类型的属性,方法和初始值设定项。 扩展声明可以通过指定采用的协议将协议一致性添加到现有的类,结构或枚举类型: 1 extension type name: adopted protocols where requirements { 2 declarations 3 } 扩展声明不能将类继承添加到现有类,因此您只能在类型名称和冒号后指定协议列表。 条件一致性您可以扩展泛型类型以有条件地符合协议,以便只有满足某些要求时,该类型的实例才符合协议。通过在扩展声明中包含需求,可以向协议添加条件一致性。 在某些通用上下文中不使用重写的要求在一些通用上下文中,从条件一致性到协议获得行为的类型并不总是使用该协议要求的专门实现。为了说明此行为,以下示例定义了两个协议和一个有条件地符合这两种协议的泛型类型。 1 protocol Loggable { 2 func log() 3 } 4 extension Loggable { 5 func log() { 6 print(self) 7 } 8 } 9 10 protocol TitledLoggable: Loggable { 11 static var logTitle: String { get } 12 } 13 extension TitledLoggable { 14 func log() { 15 print("(Self.logTitle): (self)") 16 } 17 } 18 19 struct Pair<T>: CustomStringConvertible { 20 let first: T 21 let second: T 22 var description: String { 23 return "((first),(second))" 24 } 25 } 26 27 extension Pair: Loggable where T: Loggable { } 28 extension Pair: TitledLoggable where T: TitledLoggable { 29 static var logTitle: String { 30 return "Pair of ‘(T.logTitle)‘" 31 } 32 } 33 34 extension String: TitledLoggable { 35 static var logTitle: String { 36 return "String" 37 } 38 } 该 1 let oneAndTwo = Pair(first: "one",second: "two") 2 oneAndTwo.log() 3 // Prints "Pair of ‘String‘: (one,two)" 但是,当 1 func doSomething<T: Loggable>(with x: T) { 2 x.log() 3 } 4 doSomething(with: oneAndTwo) 5 // Prints "(one,two)" 在 协议一致性不能冗余具体类型只能符合特定协议一次。Swift将冗余协议一致性标记为错误。在两种情况下,您可能会遇到这种错误。第一种情况是您多次明确地遵循相同的协议,但具有不同的要求。第二种情况是您多次隐式地从同一协议继承。这些情况将在下面的部分中讨论。 解决显式冗余问题即使扩展的要求是互斥的,具体类型上的多个扩展也不能添加对同一协议的一致性。以下示例演示了此限制。两个扩展声明尝试向 1 protocol Serializable { 2 func serialize() -> Any 3 } 4 5 extension Array: Serializable where Element == Int { 6 func serialize() -> Any { 7 // implementation 8 } 9 } 10 extension Array: Serializable where Element == String { 11 func serialize() -> Any { 12 // implementation 13 } 14 } 15 // Error: redundant conformance of ‘Array<Element>‘ to protocol ‘Serializable‘ 如果需要基于多个具体类型添加条件一致性,请创建一个新类型的协议,并在声明条件一致性时将该协议用作要求。 1 protocol SerializableInArray { } 2 extension Int: SerializableInArray { } 3 extension String: SerializableInArray { } 4 5 extension Array: Serializable where Element: SerializableInArray { 6 func serialize() -> Any { 7 // implementation 8 } 9 } 解决隐式冗余问题当具体类型有条件地符合协议时,该类型隐式地符合具有相同要求的任何父协议。 如果您需要一个类型来有条件地遵循从单个父级继承的两个协议,则显式声明与父协议的一致性。这避免了以不同的要求两次隐式地符合父协议。 以下示例显式声明了条件一致性, 1 protocol MarkedLoggable: Loggable { 2 func markAndLog() 3 } 4 5 extension MarkedLoggable { 6 func markAndLog() { 7 print("----------") 8 log() 9 } 10 } 11 12 extension Array: Loggable where Element: Loggable { } 13 extension Array: TitledLoggable where Element: TitledLoggable { 14 static var logTitle: String { 15 return "Array of ‘(Element.logTitle)‘" 16 } 17 } 18 extension Array: MarkedLoggable where Element: MarkedLoggable { } ? 如果没有显式声明条件一致性 1 extension Array: Loggable where Element: TitledLoggable { } 2 extension Array: Loggable where Element: MarkedLoggable { } 3 // Error: redundant conformance of ‘Array<Element>‘ to protocol ‘Loggable‘ 1 GRAMMAR OF AN EXTENSION DECLARATION 2 3 extension-declaration → attributes opt access-level-modifier opt extension type-identifier type-inheritance-clause opt generic-where-clause opt extension-body 4 5 extension-body → { extension-members opt } 6 7 extension-members → extension-member extension-members opt 8 9 extension-member → declaration | compiler-control-statement 下标声明甲标声明允许你添加下标支持用于特定类型的对象和通常用于访问在一个集合,列表或序列中的元素提供了一个方便的语法。使用 1 subscript (parameters) -> return type { 2 get { 3 statements 4 } 5 set(setter name) { 6 statements 7 } 8 } 下标声明只能出现在类,结构,枚举,扩展或协议声明的上下文中。 所述参数指定用于访问相应的类型的元件在一个下标表达一个或多个索引(例如, 与计算属性一样,下标声明支持读取和写入所访问元素的值。getter用于读取值,setter用于写入值。setter子句是可选的,当只需要一个getter时,你可以省略这两个子句并直接返回所请求的值。也就是说,如果你提供了一个setter子句,你还必须提供一个getter子句。 该二传手名称和圆括号是可选的。如果提供setter名称,则将其用作setter参数的名称。如果未提供setter名称,则setter的默认参数名称为 只要参数或返回类型与您正在重载的参数或返回类型不同,您就可以在声明它的类型中重载下标声明。您还可以覆盖从超类继承的下标声明。执行此操作时,必须使用 默认情况下,与函数,方法和初始值设定项不同,下标中使用的参数没有参数标签。但是,您可以使用函数,方法和初始化程序使用的相同语法提供显式参数标签。 您还可以在协议声明的上下文中声明下标,如协议下标声明中所述。 有关下标和查看下标声明示例的更多信息,请参阅下标。 1 GRAMMAR OF A SUBSCRIPT DECLARATION 2 3 subscript-declaration → subscript-head subscript-result generic-where-clause opt code-block 4 5 subscript-declaration → subscript-head subscript-result generic-where-clause opt getter-setter-block 6 7 subscript-declaration → subscript-head subscript-result generic-where-clause opt getter-setter-keyword-block 8 9 subscript-head → attributes opt declaration-modifiers opt subscript generic-parameter-clause opt parameter-clause 10 11 subscript-result → -> attributes opt type 运算符声明一个操作符声明引入了一个新的中缀,前缀或者后缀运营商到您的程序,并使用声明 您可以声明三种不同固定的运算符:中缀,前缀和后缀。运算符的固定性指定运算符与其操作数的相对位置。 运算符声明有三种基本形式,每种固定方式一种。操作者的固定性通过标记与操作者声明中指定 以下表单声明了一个新的中缀运算符: infix operator operator name: precedence group 中缀运算符是在其两个操作数之间写入的二元运算符,例如 中缀运营商可以选择指定优先级组。如果省略运算符的优先级组,则Swift使用默认优先级组 以下表单声明了一个新的前缀运算符: prefix operator operator name 甲前缀操作者是其操作数之前立即写入,如前缀逻辑非运算符(一元运算符 前缀运算符声明不指定优先级。前缀运算符是非关联的。 以下表单声明了一个新的后缀运算符: postfix operator operator name 甲后缀运算符是它的操作数后立即写入一元运算符,如后缀强制解包运算符( 与前缀运算符一样,后缀运算符声明不指定优先级。后缀运算符是非关联的。 声明一个新运算符后,通过声明一个与运算符同名的静态方法来实现它。静态方法是其值的运算符采用作为参数-例如所述类型中的一种的一个成员,即乘以操作者 1 GRAMMAR OF AN OPERATOR DECLARATION 2 3 operator-declaration → prefix-operator-declaration | postfix-operator-declaration | infix-operator-declaration 4 5 prefix-operator-declaration → prefix operator operator 6 7 postfix-operator-declaration → postfix operator operator 8 9 infix-operator-declaration → infix operator operator infix-operator-group opt 10 11 infix-operator-group → : precedence-group-name 优先组声明一个优先级组声明介绍了中缀运算符优先级一个新的分组到你的程序。运算符的优先级指定运算符在没有分组括号的情况下与其操作数绑定的紧密程度。 优先组声明具有以下形式: 1 precedencegroup precedence group name { 2 higherThan: lower group names 3 lowerThan: higher group names 4 associativity: associativity 5 assignment: assignment 6 } 在较低的组名和更高组名称列表指定新的优先级组的关系,以现有的优先级组。所述 注意 使用较低组名称和较高组名称相互关联的优先级组必须适合单个关系层次结构,但它们不必形成线性层次结构。这意味着可以使用具有未定义的相对优先级的优先级组。如果没有分组括号,那么这些优先级组中的运算符不能彼此相邻使用。 Swift定义了许多优先级组,以与标准库提供的运算符一起使用。例如,addition( 运算符的关联性指定在没有分组括号的情况下,具有相同优先级的运算符序列如何组合在一起。通过编写上下文敏感的关键字之一指定的经营者的关联 在包含可选链接的操作中使用时,优先级组的分配指定运算符的优先级。设置 1 GRAMMAR OF A PRECEDENCE GROUP DECLARATION 2 3 precedence-group-declaration → precedencegroup precedence-group-name { precedence-group-attributes opt } 4 5 precedence-group-attributes → precedence-group-attribute precedence-group-attributes opt 6 7 precedence-group-attribute → precedence-group-relation 8 9 precedence-group-attribute → precedence-group-assignment 10 11 precedence-group-attribute → precedence-group-associativity 12 13 precedence-group-relation → higherThan : precedence-group-names 14 15 precedence-group-relation → lowerThan : precedence-group-names 16 17 precedence-group-assignment → assignment : boolean-literal 18 19 precedence-group-associativity → associativity : left 20 21 precedence-group-associativity → associativity : right 22 23 precedence-group-associativity → associativity : none 24 25 precedence-group-names → precedence-group-name | precedence-group-name,precedence-group-names 26 27 precedence-group-name → identifier 声明修饰符声明修饰符是关键字或上下文相关关键字,用于修改声明的行为或含义。您可以通过在声明的属性(如果有)和引入声明的关键字之间写入适当的关键字或上下文相关关键字来指定声明修饰符。
访问控制级别Swift提供五个级别的访问控制:开放,公共,内部,文件私有和私有。您可以使用以下访问级别修饰符之一标记声明,以指定声明的访问级别。访问控制进行了详细讨论访问控制。
出于访问控制的目的,同一文件中相同类型的扩展共享访问控制范围。如果它们扩展的类型也在同一个文件中,则它们共享类型的访问控制范围。可以从扩展访问在类型声明中声明的私有成员,并且可以从其他扩展和类型声明访问在一个扩展中声明的私有成员。 上面的每个访问级别修饰符都可以选择接受单个参数,该参数由 1 GRAMMAR OF A DECLARATION MODIFIER 2 3 declaration-modifier → class | convenience | dynamic | final | infix | lazy | optional | override | postfix | prefix | required | static | unowned | unowned ( safe ) | unowned ( unsafe ) | weak 4 5 declaration-modifier → access-level-modifier 6 7 declaration-modifier → mutation-modifier 8 9 declaration-modifiers → declaration-modifier declaration-modifiers opt 10 11 access-level-modifier → private | private ( set ) 12 13 access-level-modifier → fileprivate | fileprivate ( set ) 14 15 access-level-modifier → internal | internal ( set ) 16 17 access-level-modifier → public | public ( set ) 18 19 access-level-modifier → open | open ( set ) 20 21 mutation-modifier → mutating | nonmutating (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |