★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公众号:山青咏芝(shanqingyongzhi)
➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/)
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:https://www.cnblogs.com/strengthen/p/9740135.html
➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。
➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
在Swift中,有两种类型:命名类型和复合类型。一个名为类型是当它的定义可以给出一个特定名称的类型。命名类型包括类,结构,枚举和协议。例如,名为的用户定义类的实例MyClass
具有该类型MyClass
。除了用户定义的命名类型之外,Swift标准库还定义了许多常用的命名类型,包括表示数组,字典和可选值的类型。
通常在其他语言中被认为是基本或原始的数据类型(例如表示数字,字符和字符串的类型)实际上是命名类型,使用结构在Swift标准库中定义和实现。因为它们是命名类型,所以您可以使用“ 扩展和扩展声明”中讨论的扩展声明来扩展其行为以满足程序的需要。
阿化合物类型是没有名字的类型,在夫特语言本身定义的。有两种复合类型:函数类型和元组类型。复合类型可以包含命名类型和其他复合类型。例如,元组类型包含两个元素:第一个是命名类型,第二个是另一个复合类型。(Int, (Int, Int))
Int
(Int, Int)
您可以在命名类型或复合类型周围放置括号。但是,在类型周围添加括号不会产生任何影响。例如,(Int)
相当于Int
。
本章讨论Swift语言本身定义的类型,并描述Swift的类型推断行为。
一种类型的语法
1 GRAMMAR OF A TYPE 2 3 type → array-type 4 5 type → dictionary-type 6 7 type → function-type 8 9 type → type-identifier 10 11 type → tuple-type 12 13 type → optional-type 14 15 type → implicitly-unwrapped-optional-type 16 17 type → protocol-composition-type 18 19 type → metatype-type 20 21 type → Any 22 23 type → Self 24 25 type → ( type )
类型注释
类型标注明确指定的变量或表达式的类型。类型注释以冒号(:
)开头,以类型结尾,如以下示例所示:
1 let someTuple: (Double, Double) = (3.14159, 2.71828) 2 func someFunction(a: Int) { /* ... */ }
在第一个示例中,表达式someTuple
被指定为具有元组类型。在第二个示例中,函数的参数被指定为具有类型。(Double, Double)
a
someFunction
Int
类型注释可以在类型之前包含类型属性的可选列表。
1 GRAMMAR OF A TYPE ANNOTATION 2 3 type-annotation → : attributes opt inoutopt type
类型标识符
类型标识符指的是命名类型或命名类型或复合类型的类型别名。
大多数情况下,类型标识符直接引用与标识符同名的命名类型。例如,Int
是直接引用命名类型Int
的类型标识符,类型标识符直接引用命名类型。Dictionary<String, Int>
Dictionary<String, Int>
有两种情况,类型标识符不引用具有相同名称的类型。在第一种情况下,类型标识符指的是命名或复合类型的类型别名。例如,在下面的示例中,Point
类型注释中的使用是指元组类型。(Int, Int)
1 typealias Point = (Int, Int) 2 let origin: Point = (0, 0)
在第二种情况下,类型标识符使用dot(.
)语法来引用在其他模块中声明或嵌套在其他类型中的命名类型。例如,以下代码中的类型标识符引用模块中MyType
声明的命名类型ExampleModule
。
var someValue: ExampleModule.MyType
类型标识符的语法
1 type-identifier → type-name generic-argument-clause opt | type-name generic-argument-clause opt . type-identifier 2 3 type-name → identifier
元组类型
元组类型是以逗号分隔的类型列表,括在括号中。
您可以使用元组类型作为函数的返回类型,以使函数能够返回包含多个值的单个元组。您还可以命名元组类型的元素,并使用这些名称来引用各个元素的值。元素名称由标识符后跟冒号(:)组成。有关演示这两个功能的示例,请参阅具有多个返回值的函数。
当元组类型的元素具有名称时,该名称是该类型的一部分。
1 var someTuple = (top: 10, bottom: 12) // someTuple is of type (top: Int, bottom: Int) 2 someTuple = (top: 4, bottom: 42) // OK: names match 3 someTuple = (9, 99) // OK: names are inferred 4 someTuple = (left: 5, right: 5) // Error: names don't match
所有元组类型都包含两种或更多类型,除了Void
它是空元组类型的类型别名,()
。
1 GRAMMAR OF A TUPLE TYPE 2 3 tuple-type → ( ) | ( tuple-type-element , tuple-type-element-list ) 4 5 tuple-type-element-list → tuple-type-element | tuple-type-element , tuple-type-element-list 6 7 tuple-type-element → element-name type-annotation | type 8 9 element-name → identifier
功能类型
函数类型表示函数,方法或闭包的类型,由一个参数和由箭头(->
)分隔的返回类型组成:
(parameter type) -> return type
的参数类型是类型逗号分隔的列表。因为返回类型可以是元组类型,所以函数类型支持返回多个值的函数和方法。
函数类型的参数(其中是任何类型)可以应用该属性在其调用站点隐式创建闭包。这提供了一种语法上方便的方法来推迟表达式的求值,而无需在调用函数时编写显式闭包。有关autoclosure函数类型参数的示例,请参见Autoclosures。() -> T
T
autoclosure
函数类型可以在其一个可变参数参数参数类型。从语法上讲,一个可变参数包含一个基本类型名称,后面紧跟三个点(...
),如下所示Int...
。可变参数被视为包含基本类型名称元素的数组。例如,可变参数Int...
被视为[Int]
。有关使用可变参数的示例,请参阅可变参数。
要指定输入输出参数,请在参数类型前加上inout
关键字前缀。您无法使用inout
关键字标记可变参数或返回类型。在输出参数在讨论的In-Out参数。
如果函数类型只有一个参数且该参数的类型是元组类型,则在编写函数类型时必须将元组类型括起来。例如,是一个函数的类型,它接受元组类型的单个参数,并且不返回任何值。相反,没有括号,是一个带有两个参数但不返回任何值的函数的类型。同样,因为是类型别名,函数类型与-a函数相同,该函数接受一个空元组的参数。这些类型与-a不带参数的函数不同。((Int, Int)) -> Void
(Int, Int)
(Int, Int) -> Void
Int
Void
()
(Void) -> Void
(()) -> ()
() -> ()
函数和方法中的参数名称不是相应函数类型的一部分。例如:
1 func someFunction(left: Int, right: Int) {} 2 func anotherFunction(left: Int, right: Int) {} 3 func functionWithDifferentLabels(top: Int, bottom: Int) {} 4 5 var f = someFunction // The type of f is (Int, Int) -> Void, not (left: Int, right: Int) -> Void. 6 f = anotherFunction // OK 7 f = functionWithDifferentLabels // OK 8 9 func functionWithDifferentArgumentTypes(left: Int, right: String) {} 10 f = functionWithDifferentArgumentTypes // Error 11 12 func functionWithDifferentNumberOfArguments(left: Int, right: Int, top: Int) {} 13 f = functionWithDifferentNumberOfArguments // Error
因为参数标签不是函数类型的一部分,所以在编写函数类型时省略它们。
1 var operation: (lhs: Int, rhs: Int) -> Int // Error 2 var operation: (_ lhs: Int, _ rhs: Int) -> Int // OK 3 var operation: (Int, Int) -> Int // OK
如果函数类型包含多个箭头(->
),则函数类型从右到左分组。例如,函数类型被理解为- 也就是说,一个函数接受并返回另一个获取并返回的函数。(Int) -> (Int) -> Int
(Int) -> ((Int) -> Int)
Int
Int
必须使用throws
关键字标记可以抛出错误的函数类型,并且必须使用rethrows
关键字标记可以重新抛出错误的函数类型。该throws
关键字是一个函数的类型的一部分,和nonthrowing函数是投掷功能的亚型。因此,您可以在与抛出函数相同的位置使用非抛出函数。投掷和重新抛出功能被描述在投掷函数和方法以及重新抛出函数和方法。
Nonescaping闭包的限制
作为非出错函数的参数不能存储在属性,变量或类型常量中Any
,因为这可能允许值转义。
作为非出错函数的参数不能作为参数传递给另一个非出错函数参数。此限制有助于Swift在编译时而不是在运行时执行更多的检查以查看对内存的冲突访问。例如:
1 let external: (() -> Void) -> Void = { _ in () } 2 func takesTwoFunctions(first: (() -> Void) -> Void, second: (() -> Void) -> Void) { 3 first { first {} } // Error 4 second { second {} } // Error 5 6 first { second {} } // Error 7 second { first {} } // Error 8 9 first { external {} } // OK 10 external { first {} } // OK 11 }
在上面的代码中,两个参数takesTwoFunctions(first:second:)
都是函数。两个参数都没有标记@escaping
,因此它们都是非脱节的。
上例中标记为“Error”的四个函数调用会导致编译器错误。因为first
和second
参数是非脱节函数,所以它们不能作为参数传递给另一个非脱节函数参数。相反,标记为“OK”的两个函数调用不会导致编译器错误。这些函数调用不违反限制,因为external
它不是参数之一takesTwoFunctions(first:second:)
。
如果需要避免此限制,请将其中一个参数标记为转义,或者使用该withoutActuallyEscaping(_:do:)
函数将其中一个非转义函数参数临时转换为转义函数。有关避免对内存的冲突访问的信息,请参阅内存安全。
1 GRAMMAR OF A FUNCTION TYPE 2 3 function-type → attributes opt function-type-argument-clause throwsopt -> type 4 5 function-type → attributes opt function-type-argument-clause rethrows -> type 6 7 function-type-argument-clause → ( ) 8 9 function-type-argument-clause → ( function-type-argument-list ...opt ) 10 11 function-type-argument-list → function-type-argument | function-type-argument , function-type-argument-list 12 13 function-type-argument → attributes opt inoutopt type | argument-label type-annotation 14 15 argument-label → identifier
数组类型
Swift语言为Swift标准库Array<Element>
类型提供以下语法糖:
[type]
换句话说,以下两个声明是等效的:
1 let someArray: Array<String> = ["Alex", "Brian", "Dave"] 2 let someArray: [String] = ["Alex", "Brian", "Dave"]
在这两种情况下,常量someArray
都声明为字符串数组。可以通过在方括号中指定有效索引值来通过下标来访问数组的元素:someArray[0]
引用索引0处的元素"Alex"
。
您可以通过嵌套方括号对来创建多维数组,其中元素的基本类型的名称包含在最里面的方括号对中。例如,您可以使用三组方括号创建三维整数数组:
var array3D: [[[Int]]] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
在访问多维数组中的元素时,最左侧的下标索引引用最外层数组中该索引处的元素。右边的下一个下标索引是指数组中嵌套一个级别的索引处的元素。依此类推。这意味着在上面的示例中,array3D[0]
引用,引用和引用值4。[[1, 2], [3, 4]]
array3D[0][1]
[3, 4]
array3D[0][1][1]
有关Swift标准库Array
类型的详细讨论,请参阅数组。
1 GRAMMAR OF AN ARRAY TYPE 2 3 array-type → [ type ]
字典类型
Swift语言为Swift标准库类型提供以下语法糖:Dictionary<Key, Value>
[key type: value type]
换句话说,以下两个声明是等效的:
1 let someDictionary: [String: Int] = ["Alex": 31, "Paul": 39] 2 let someDictionary: Dictionary<String, Int> = ["Alex": 31, "Paul": 39]
在这两种情况下,常量都someDictionary
被声明为字典,其中字符串为键,整数为值。
可以通过在方括号中指定相应的键来通过下标来访问字典的值:someDictionary["Alex"]
引用与键相关联的值"Alex"
。下标返回字典值类型的可选值。如果指定的键未包含在字典中,则下标返回nil
。
字典的键类型必须符合Swift标准库Hashable
协议。
有关Swift标准库Dictionary
类型的详细讨论,请参阅字典。
1 GRAMMAR OF A DICTIONARY TYPE 2 3 dictionary-type → [ type : type ]
可选类型
Swift语言将后缀定义?
为命名类型的语法糖,该类型Optional<Wrapped>
在Swift标准库中定义。换句话说,以下两个声明是等效的:
1 var optionalInteger: Int? 2 var optionalInteger: Optional<Int>
在这两种情况下,变量optionalInteger
都声明为具有可选整数的类型。请注意,类型和类型之间不能出现空格?
。
该类型Optional<Wrapped>
是具有两种情况的枚举,none
并且some(Wrapped)
用于表示可能存在或不存在的值。任何类型都可以显式声明为(或隐式转换为)可选类型。如果在声明可选变量或属性时未提供初始值,则其值自动默认为nil
。
如果可选类型的实例包含值,则可以使用后缀运算符访问该值!
,如下所示:
1 optionalInteger = 42 2 optionalInteger! // 42
使用!
运算符解包具有nil
运行时错误结果值的可选项。
您还可以使用可选链接和可选绑定来有条件地对可选表达式执行操作。如果值为nil
,则不执行任何操作,因此不会产生运行时错误。
有关更多信息以及查看显示如何使用可选类型的示例,请参阅Optionals。
1 GRAMMAR OF AN OPTIONAL TYPE 2 3 optional-type → type ?
隐式展开的可选类型
Swift语言将后缀定义!
为命名类型的语法糖,该类型Optional<Wrapped>
在Swift标准库中定义,并具有在访问时自动解包的附加行为。如果您尝试使用具有值的隐式展开的可选项,nil
则会出现运行时错误。除了隐式解包行为外,以下两个声明是等效的:
1 var implicitlyUnwrappedString: String! 2 var explicitlyUnwrappedString: Optional<String>
请注意,类型和类型之间不能出现空格!
。
因为隐式解包会更改包含该类型的声明的含义,所以嵌套在元组类型或泛型类型中的可选类型(如字典或数组的元素类型)不能标记为隐式解包。例如:
1 let tupleOfImplicitlyUnwrappedElements: (Int!, Int!) // Error 2 let implicitlyUnwrappedTuple: (Int, Int)! // OK 3 4 let arrayOfImplicitlyUnwrappedElements: [Int!] // Error 5 let implicitlyUnwrappedArray: [Int]! // OK
由于隐式解包的选项Optional<Wrapped>
与可选值具有相同的类型,因此您可以在代码中可以使用选项的所有相同位置使用隐式解包的选项。例如,您可以将隐式解包的选项的值分配给变量,常量和选项的属性,反之亦然。
与optionals一样,如果在声明隐式展开的可选变量或属性时未提供初始值,则其值自动默认为nil
。
使用可选链接有条件地对隐式展开的可选表达式执行操作。如果值为nil
,则不执行任何操作,因此不会产生运行时错误。
有关隐式展开的可选类型的更多信息,请参阅隐式展开的可选项。
1 GRAMMAR OF AN IMPLICITLY UNWRAPPED OPTIONAL TYPE 2 3 implicitly-unwrapped-optional-type → type !
协议组成类型
协议组合类型定义符合指定协议列表中的每个协议的类型,或者是给定类的子类并且符合指定协议列表中的每个协议的类型。只有在类型注释,通用参数子句和通用where
子句中指定类型时,才可以使用协议组合类型。
协议组成类型具有以下形式:
Protocol 1 & Protocol 2
协议组合类型允许您指定其类型符合多个协议要求的值,而无需显式定义从您希望类型符合的每个协议继承的新的命名协议。例如,可以使用的协议的组合物类型,而不是声明一个新的协议,从继承,,和。同样,您可以使用而不是声明作为子类并符合的新协议。ProtocolA & ProtocolB & ProtocolC
ProtocolA
ProtocolB
ProtocolC
SuperClass & ProtocolA
SuperClass
ProtocolA
协议组合列表中的每个项目都是以下之一; 该列表最多可包含一个类:
- 一个类的名称
- 协议的名称
- 类型别名,其基础类型是协议组合类型,协议或类。
当协议组合类型包含类型别名时,同一协议可能在定义中出现多次 - 重复项被忽略。例如,PQR
下面代码中的定义等同于。P & Q & R
1 typealias PQ = P & Q 2 typealias PQR = PQ & Q & R
1 GRAMMAR OF A PROTOCOL COMPOSITION TYPE 2 3 protocol-composition-type → type-identifier & protocol-composition-continuation 4 5 protocol-composition-continuation → type-identifier | protocol-composition-type
元类型
元类型类型是指任何类型的类型,包括类类型,结构类型,枚举类型和协议类型。
类,结构或枚举类型的元类型是该类型的名称,后跟.Type
。协议类型的元类型 - 不是在运行时符合协议的具体类型 - 是后面的协议的名称.Protocol
。例如,类类型的元类型SomeClass
是SomeClass.Type
协议的元SomeProtocol
类型SomeProtocol.Protocol
。
您可以使用后缀self
表达式将类型作为值进行访问。例如,SomeClass.self
返回SomeClass
自身,而不是实例SomeClass
。并SomeProtocol.self
返回SomeProtocol
自身,而不是SomeProtocol
在运行时符合的类型的实例。您可以type(of:)
使用类型的实例调用该函数,以将该实例的动态,运行时类型作为值进行访问,如以下示例所示:
1 class SomeBaseClass { 2 class func printClassName() { 3 print("SomeBaseClass") 4 } 5 } 6 class SomeSubClass: SomeBaseClass { 7 override class func printClassName() { 8 print("SomeSubClass") 9 } 10 } 11 let someInstance: SomeBaseClass = SomeSubClass() 12 // The compile-time type of someInstance is SomeBaseClass, 13 // and the runtime type of someInstance is SomeSubClass 14 type(of: someInstance).printClassName() 15 // Prints "SomeSubClass"
有关更多信息,请参阅type(of:)
Swift标准库。
使用初始化表达式从该类型的元类型值构造类型的实例。对于类实例,调用的初始值设定项必须使用required
关键字标记,或者使用final
关键字标记整个类。
1 class AnotherSubClass: SomeBaseClass { 2 let string: String 3 required init(string: String) { 4 self.string = string 5 } 6 override class func printClassName() { 7 print("AnotherSubClass") 8 } 9 } 10 let metatype: AnotherSubClass.Type = AnotherSubClass.self 11 let anotherInstance = metatype.init(string: "some string")
1 GRAMMAR OF A METATYPE TYPE 2 3 metatype-type → type . Type | type . Protocol
类型继承子句
类型继承子句用于指定命名类型从哪个类继承以及命名类型符合哪些协议。类型继承子句以冒号(:
)开头,后跟类型标识符列表。
类类型可以从单个超类继承并符合任意数量的协议。定义类时,超类的名称必须首先出现在类型标识符列表中,然后是类必须符合的任意数量的协议。如果该类不从另一个类继承,则该列表可以以协议开头。有关类继承的扩展讨论和几个示例,请参阅继承。
其他命名类型只能继承或符合协议列表。协议类型可以从任何数量的其他协议继承。当协议类型继承自其他协议时,来自那些其他协议的要求集合在一起,并且从当前协议继承的任何类型必须符合所有这些要求。
枚举定义中的类型继承子句可以是协议列表,也可以是为其案例分配原始值的枚举,也可以是指定这些原始值类型的单个命名类型。有关使用类型继承子句指定其原始值类型的枚举定义的示例,请参阅原始值。
1 GRAMMAR OF A TYPE INHERITANCE CLAUSE 2 3 type-inheritance-clause → : type-inheritance-list 4 5 type-inheritance-list → type-identifier | type-identifier , type-inheritance-list
类型推断
Swift广泛使用类型推断,允许您省略代码中许多变量和表达式的类型或部分类型。例如,您可以编写,完全省略类型,而不是写入,编译器正确地推断出名称类型的值。类似地,当可以从上下文推断出完整类型时,可以省略部分类型。
例如,如果您编写,编译器会推断出具有该类型的编译器。
var x: Int = 0
var x = 0
x
Int
let dict: Dictionary = ["A": 1]
dict
Dictionary<String, Int>
在上面的两个例子中,类型信息从表达式树的叶子传递到它的根。也就是说,通过首先检查类型然后将此类型信息传递到根(变量)来推断x
in 的类型。var x: Int = 0
0
x
在Swift中,类型信息也可以以相反的方向流动 - 从根到叶。例如,在下面的示例中,常量上的显式类型注释()会导致数字文字具有推断类型而不是。: Float
eFloat
2.71828
Float
Double
1 let e = 2.71828 // The type of e is inferred to be Double. 2 let eFloat: Float = 2.71828 // The type of eFloat is Float.
Swift中的类型推断在单个表达式或语句的级别上运行。这意味着,必须可以通过对表达式或其子表达式之一进行类型检查来访问在表达式中推断省略类型或类型的一部分所需的所有信息。