余晖落尽暮晚霞,黄昏迟暮远山寻
本站
当前位置:网站首页 > 编程知识 > 正文

Swift 4.1 官方文档大全(第二十四章)类型(Types)

xiyangw 2022-12-03 11:50 18 浏览 0 评论

类型

在Swift中,有两种类型:命名类型和复合类型。命名类型是一种可以在定义时给定特定名称的类型。命名类型包括类,结构,枚举和协议。例如,名为MyClass的用户定义类的实例具有MyClass类型。除了用户定义的命名类型外,Swift标准库还定义了许多常用的命名类型,包括代表数组,字典和可选值的类型。

Swift 4.1 官方文档大全(第二十四章)类型(Types)

在其他语言中通常被认为是基本或基本的数据类型(例如表示数字,字符和字符串的类型)实际上是命名类型,在Swift标准库中使用结构定义和实现。因为它们是被命名的类型,所以可以使用扩展声明来扩展它们的行为以适应程序的需求,这在扩展和扩展声明中进行了讨论。

复合类型是一种没有名称的类型,在Swift语言中定义。有两种复合类型:函数类型和元组类型。化合物类型可能包含命名类型和其他化合物类型。例如,元组类型(Int,(Int,Int))包含两个元素:第一个是命名类型Int,第二个是另一个复合类型(Int,Int)。

您可以将圆括号放在指定类型或复合类型的周围。但是,在类型周围添加括号不会产生任何影响。例如,(Int)等同于Int。

本章讨论在Swift语言中定义的类型,并描述Swift的类型推断行为

type → array-type-

type → dictionary-type-

type → function-type-

type → type-identifier-

type → tuple-type-

type → optional-type-

type → implicitly-unwrapped-optional-type-

type → protocol-composition-type-

type → metatype-type-

type → Any-

type → Self-

type → (-type-)-

类型注释

类型注释显式指定变量或表达式的类型。 类型注释以冒号(:)开头并以类型结尾,如以下示例所示:

let someTuple: (Double, Double) = (3.14159, 2.71828)

func someFunction(a: Int) { /* ... */ }

在第一个示例中,someTuple表达式指定具有元组类型(Double,Double)。 在第二个示例中,函数someFunction的参数a被指定为Int类型。

类型注释可以在类型之前包含可选的类型属性列表。

type-annotation → :-attributes-opt-inout-opt-type-

类型标识符

类型标识符引用命名类型或命名或复合类型的类型别名。

大多数情况下,类型标识符直接引用与标识符同名的命名类型。 例如,Int是直接引用指定类型Int的类型标识符,类型标识符Dictionary <String,Int>直接引用指定类型Dictionary <String,Int>。

有两种情况,其中一个类型标识符不引用具有相同名称的类型。 在第一种情况下,类型标识符是指名称或复合类型的类型别名。 例如,在下面的例子中,在类型注释中使用Point是指元组类型(Int,Int)。

typealiasPoint = (Int, Int)

let origin: Point = (0, 0)

在第二种情况下,类型标识符使用点(。)语法来引用在其他模块中声明的或在其他类型中嵌套的命名类型。 例如,以下代码中的类型标识符会引用在ExampleModule模块中声明的已命名类型MyType。

var someValue:ExampleModule.MyType

type-identifier → type-name-generic-argument-clause-opt-type-name-generic-argument-clause-opt-.-type-identifier-

type-name → identifier-

元组类型

元组类型是逗号分隔的类型列表,用括号括起来。

您可以使用元组类型作为函数的返回类型,以使函数能够返回包含多个值的单个元组。 您也可以命名元组类型的元素并使用这些名称来引用各个元素的值。 元素名称由紧随冒号(:)的标识符组成。 有关演示这两个功能的示例,请参阅具有多个返回值的函数。

当元组类型的元素具有名称时,该名称是该类型的一部分。

var someTuple = (top: 10, bottom: 12) // someTuple is of type (top: Int, bottom: Int)

someTuple = (top: 4, bottom: 42) // OK: names match

someTuple = (9, 99) // OK: names are inferred

someTuple = (left: 5, right: 5) // Error: names don't match

所有的元组类型都包含两个或更多的类型,除了Void,它是空元组类型的类型别名()。

tuple-type → (-)-(-tuple-type-element-,-tuple-type-element-list-)-

tuple-type-element-list → tuple-type-element-tuple-type-element-,-tuple-type-element-list-

tuple-type-element → element-name-type-annotation-type-

element-name → identifier-

功能类型

函数类型表示函数,方法或闭包的类型,并由用箭头( - >)分隔的参数和返回类型组成:

  • (parameter type) -> return type

参数类型是用逗号分隔的类型列表。因为返回类型可以是元组类型,所以函数类型支持返回多个值的函数和方法。

函数type() - > T(其中T是任何类型)的参数可以应用autoclosure属性,以隐式地在其呼叫站点创建闭包。这提供了一种语法上方便的方式来推迟表达式的评估,而无需在调用函数时编写明确的闭包。有关自动闭合功能类型参数的示例,请参阅自动闭合。

函数类型可以在其参数类型中具有可变参数。在语法上,可变参数包含一个基本类型名称,后跟三个点(...),如Int ....可变参数被视为包含基本类型名称的元素的数组。例如,可变参数Int ...被视为[Int]。有关使用可变参数的示例,请参阅可变参数。

要指定输入输出参数,请使用inout关键字为参数类型添加前缀。您不能使用inout关键字标记可变参数或返回类型。输入输出参数中讨论输入输出参数。

如果一个函数类型只有一个参数,并且该参数的类型是一个元组类型,那么在写入该函数的类型时,元组类型必须加上括号。例如,((Int,Int)) - > Void是接受元组类型(Int,Int)的单个参数且不返回任何值的函数的类型。相反,没有括号,(Int,Int) - > Void是一个函数的类型,它接受两个Int参数并且不返回任何值。同样,由于Void是()的类型别名,因此函数类型(Void) - > Void与(()) - >()是相同的函数,该函数接受一个空元组的单个参数。这些类型不同于() - >() - 一个不带参数的函数。

函数和方法中的参数名称不是相应函数类型的一部分。例如:

func someFunction(left: Int, right: Int) {}

func anotherFunction(left: Int, right: Int) {}

func functionWithDifferentLabels(top: Int, bottom: Int) {}

var f = someFunction// The type of f is (Int, Int) -> Void, not (left: Int, right: Int) -> Void.

f = anotherFunction// OK

f = functionWithDifferentLabels// OK

func functionWithDifferentArgumentTypes(left: Int, right: String) {}

f = functionWithDifferentArgumentTypes// Error

func functionWithDifferentNumberOfArguments(left: Int, right: Int, top: Int) {}

f = functionWithDifferentNumberOfArguments// Error

由于参数标签不是函数类型的一部分,因此在编写函数类型时省略它们。

var operation: (lhs: Int, rhs: Int) -> Int// Error

var operation: (_lhs: Int, _rhs: Int) -> Int// OK

var operation: (Int, Int) -> Int// OK

如果函数类型包含多个单箭头( - >),则函数类型将从右向左分组。例如,函数类型(Int) - >(Int) - > Int被理解为(Int) - >((Int) - > Int) - 也就是说,一个函数接受一个Int并返回另一个函数,返回一个Int。

可以抛出错误的函数类型必须使用throws关键字标记,并且可以重新抛出错误的函数类型必须使用rethrows关键字标记。 throws关键字是函数类型的一部分,nonthrowing函数是抛出函数的子类型。因此,你可以在同一个地方使用非抛出函数作为抛出函数。投掷函数和方法以及重新函数和方法中描述了投掷和重投掷函数。

消除封锁的限制

一个参数是一个非观察函数,不能作为参数传递给另一个非观察函数参数。这个限制有助于Swift在编译时而不是在运行时执行更多的检查冲突访问内存。例如:

let external: (Any) -> Void = { _in () }

func takesTwoFunctions(first: (Any) -> Void, second: (Any) -> Void) {

first(first) // Error

second(second) // Error

first(second) // Error

second(first) // Error

first(external) // OK

external(first) // OK

}

在上面的代码中,takeTwoFunctions(first:second :)的两个参数都是函数。 这两个参数都没有被标记为@escaping,所以它们都作为结果不转义。

上述示例中标记为“错误”的四个函数调用会导致编译器错误。 因为第一个和第二个参数是非转义函数,所以它们不能作为参数传递给另一个非转义函数参数。 相反,标记为“OK”的两个函数调用不会导致编译器错误。 这些函数调用不会违反限制,因为外部函数不是takeTwoFunctions(first:second :)的参数之一。

如果您需要避免此限制,请将其中一个参数标记为转义,或者使用withoutActuallyEscaping(_:do :)函数临时将其中一个非转义函数参数转换为转义函数。 有关避免冲突访问内存的信息,请参阅内存安全。

函数类型的语法

function-type → attributes-opt-function-type-argument-clause-throws-opt-->-type-

function-type → attributes-opt-function-type-argument-clause-rethrows-->-type-

function-type-argument-clause → (-)-

function-type-argument-clause → (-function-type-argument-list-...-opt-)-

function-type-argument-list → function-type-argument-function-type-argument-,-function-type-argument-list-

function-type-argument → attributes-opt-inout-opt-type-argument-label-type-annotation-

argument-label → identifier-

数组类型

Swift语言为Swift标准库Array <Element>类型提供了以下语法糖:

  • [type]

换句话说,以下两个声明是等价的:

let someArray: Array<String> = ["Alex", "Brian", "Dave"]

let someArray: [String] = ["Alex", "Brian", "Dave"]

在这两种情况下,常量someArray都被声明为一个字符串数组。 通过在方括号中指定有效的索引值,可以通过下标来访问数组的元素:someArray [0]引用索引为0的元素“Alex”。

您可以通过嵌套方括号对创建多维数组,其中元素的基本类型的名称包含在最内侧的一对方括号中。 例如,您可以使用三组方括号创建一个三维整数数组

var array3D: [[[Int]]] = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]

当访问多维数组中的元素时,最左边的下标索引引用最外面数组中该索引处的元素。 右下角的下标索引指的是数组中嵌套一级的那个索引处的元素。依此类推。 这意味着在上面的例子中,array3D [0]引用[[1,2],[3,4]],array3D [0] [1]引用[3,4],array3D [0] [1 ] [1]是指数值4。

有关Swift标准库数组类型的详细讨论,请参阅数组。

array-type → [-type-]-

字典类型

Swift语言为Swift标准库Dictionary <Key,Value>类型提供以下语法糖:

  • [key type: value type]

换句话说,以下两个声明是等价的:

let someDictionary: [String: Int] = ["Alex": 31, "Paul": 39]

let someDictionary: Dictionary<String, Int> = ["Alex": 31, "Paul": 39]

在这两种情况下,常量someDictionary被声明为一个字典,其中字符串作为键和整数作为值。

通过在方括号中指定相应的键,可以通过下标来访问字典的值:someDictionary [“Alex”]指与键“Alex”相关联的值。 下标返回字典值类型的可选值。 如果指定的键不包含在字典中,则下标返回nil。

字典的键类型必须符合Swift标准库Hashable协议。

有关Swift标准库字典类型的详细讨论,请参阅字典。

dictionary-type → [-type-:-type-]-

可选类型

Swift语言定义了后缀? 作为命名类型可选的<Wrapped>的语法糖,它是在Swift标准库中定义的。 换句话说,以下两个声明是等价的:

var optionalInteger: Int?

var optionalInteger: Optional<Int>

在这两种情况下,变量optionalInteger被声明为具有可选整数的类型。 请注意,类型和?之间不会出现空格。

类型可选的<Wrapped>是一个枚举,有两种情况,无和一些(Wrapped),它们用于表示可能存在或可能不存在的值。 任何类型都可以显式声明为(或隐式转换为)可选类型。 如果您在声明可选变量或属性时未提供初始值,则其值自动默认为零。

如果可选类型的实例包含值,则可以使用postfix运算符!访问该值,如下所示:

optionalInteger = 42

optionalInteger! // 42

使用 ! 运算符打开一个值为零的可选结果导致运行时错误。

您还可以使用可选链和可选绑定来有条件地对可选表达式执行操作。 如果值为零,则不执行任何操作,因此不会产生运行时错误

有关更多信息并查看显示如何使用可选类型的示例,请参阅可选项。

optional-type → type-?-

隐式解包可选类型

Swift语言定义了后缀! 作为在Swift标准库中定义的命名类型可选的<Wrapped>的语法糖,以及在访问它时自动解包的附加行为。 如果您尝试使用值为nil的隐式解包可选项,则会出现运行时错误。 除了隐式展开行为之外,以下两个声明是等价的:

var implicitlyUnwrappedString: String!

var explicitlyUnwrappedString: Optional<String>

请注意,类型和!之间不会出现空格。

因为隐式展开会更改包含该类型的声明的含义,所以嵌套在元组类型或泛型类型内的可选类型(例如字典或数组的元素类型)不能标记为隐式展开。 例如:

let tupleOfImplicitlyUnwrappedElements: (Int!, Int!) // Error

let implicitlyUnwrappedTuple: (Int, Int)! // OK

let arrayOfImplicitlyUnwrappedElements: [Int!] // Error

let implicitlyUnwrappedArray: [Int]! // OK

因为隐式解包选项与可选值具有相同的可选<Wrapped>类型,所以可以在代码中的所有相同位置使用隐式解包选项,以便使用可选项。 例如,您可以将隐式解包选项的值分配给变量,常量和可选属性,反之亦然。

与可选项一样,如果您在声明隐式解包的可选变量或属性时未提供初始值,则其值将自动默认为零。

使用可选链接有条件地对隐式解包的可选表达式执行操作。 如果值为零,则不执行任何操作,因此不会产生运行时错误。

有关隐式展开的可选类型的更多信息,请参见隐式展开的可选项

implicitly-unwrapped-optional-type → type-!-

协议组合类型

协议组合类型定义了一种符合指定协议列表中的每个协议的类型,或者是一个给定类的子类的类型,并且符合指定协议列表中的每个协议。 只有在类型注释中指定类型,泛型参数子句和泛型where子句时,才可以使用协议组合类型。

协议组合类型具有以下形式:

  • Protocol 1 & Protocol 2

协议组合类型允许您指定一个类型符合多个协议要求的值,而不明确定义一个新的,命名的协议,该协议从您希望类型符合的每个协议继承。例如,您可以使用协议组合类型ProtocolA&ProtocolB&ProtocolC而不是声明从ProtocolA,ProtocolB和ProtocolC继承的新协议。同样,您可以使用SuperClass&ProtocolA而不是声明一个新的协议,该协议是SuperClass的一个子类,并且符合ProtocolA。

协议组成列表中的每个项目都是以下之一;该列表最多可以包含一个类:

班级的名称

协议的名称

基础类型是协议组合类型,协议或类的类型别名。

当协议组合类型包含类型别名时,同一协议可能在定义中出现多次 - 重复项将被忽略。例如,下面代码中PQR的定义等同于P&Q&R。

typealias PQ = P&Q

typealias PQR = PQ&Q&R

协议组成类型的语法

protocol-composition-type → type-identifier-&-protocol-composition-continuation-

protocol-composition-continuation → type-identifier-protocol-composition-type-

元类型

元类型是指任何类型的类型,包括类类型,结构类型,枚举类型和协议类型。

类,结构或枚举类型的元类型是该类型的名称,后跟.Type。 协议类型的元类型(不是符合运行时协议的具体类型)是该协议的名称,后跟.Protocol。 例如,类类型SomeClass的元类型是SomeClass.Type,协议SomeProtocol的元类型是SomeProtocol.Protocol

您可以使用postfix自我表达式来访问作为值的类型。 例如,SomeClass.self本身返回SomeClass,而不是SomeClass的实例。 SomeProtocol.self自身返回SomeProtocol,而不是在运行时符合SomeProtocol类型的实例。 您可以使用某个类型的实例调用类型(of :)函数,以便将该实例的动态运行时类型作为值进行访问,如以下示例所示:

class SomeBaseClass {

class funcprintClassName() {

print("SomeBaseClass")

}

}

class SomeSubClass: SomeBaseClass {

override class funcprintClassName() {

print("SomeSubClass")

}

}

let someInstance: SomeBaseClass = SomeSubClass()

// The compile-time type of someInstance is SomeBaseClass,

// and the runtime type of someInstance is SomeSubClass

type(of: someInstance).printClassName()

// Prints "SomeSubClass"

有关更多信息,请参阅Swift标准库中的(of :)类型。

使用初始化表达式从该类型的元类型值构造一个类型的实例。 对于类实例,所调用的初始化程序必须标记为必需的关键字或标有final关键字的整个类。

class AnotherSubClass: SomeBaseClass {

let string: String

required init (string: String) {

self.string = string

}

override class func printClassName() {

print("AnotherSubClass")

}

}

let metatype: AnotherSubClass.Type = AnotherSubClass.self

let anotherInstance = metatype.init(string: "some string”)

metatype-type → type-.-Type-type-.-Protocol-

类型继承子句

类型继承子句用于指定指定类型继承的类以及指定类型符合的协议。类型继承子句以冒号(:)开头,后跟类型标识符列表。

类类型可以从一个超类继承,并符合任何数量的协议。定义一个类时,超类的名称必须首先出现在类型标识符列表中,然后是类必须符合的任意数量的协议。如果该类不从另一个类继承,则该列表可以以协议开始。有关扩展的讨论和类继承的几个示例,请参阅继承。

其他命名类型只能继承或符合协议列表。协议类型可以从任何其他协议继承。当协议类型从其他协议继承时,来自这些其他协议的一组要求聚合在一起,并且从当前协议继承的任何类型都必须符合所有这些要求。

枚举定义中的类型继承子句可以是协议列表,也可以是将枚举原始值分配给其情况的枚举,也可以是指定这些原始值类型的单个命名类型。有关使用类型继承子句指定其原始值类型的枚举定义的示例,请参阅原始值。

一种遗传条款的语法

type-inheritance-clause → :-type-inheritance-list-

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>。

在上面的两个例子中,类型信息都从表达式树的叶子传递到它的根。也就是说,通过首先检查0的类型,然后将该类型信息传递给根(变量x),推断var x:Int = 0中x的类型。

在Swift中,类型信息也可以以相反的方向流动 - 从根到叶。在下面的示例中,例如,常量eFloat上的显式类型注释(:Float)会导致数字文字2.71828具有Float而不是Double的推断类型。

let e = 2.71828// The type of e is inferred to be Double.

let eFloat: Float = 2.71828// The type of eFloat is Float.

Swift中的类型推断在单个表达式或语句的级别上运行。 这意味着推断表达式中的省略类型或部分类型所需的所有信息必须可以通过类型检查表达式或其子表达式之一来访问。

相关推荐

spring利用spring.handlers解析自定义配置(spring validation 自定义)

一、问题我们在spring的xml配置文件里经常定义各种各样的配置(tx、bean、mvc、bean等等)。以及集成第三方框架时,也会看到一些spring之外的配置,例如dubbo的配置、securi...

「Spring源码分析」AOP源码解析(上篇)(spring源码深度解析(第2版))

前言前面写了六篇文章详细地分析了SpringBean加载流程,这部分完了之后就要进入一个比较困难的部分了,就是AOP的实现原理分析。为了探究AOP实现原理,首先定义几个类,一个Dao接口:1&nbs...

Spring 解析注册BeanDefinition这一篇就Over
Spring 解析注册BeanDefinition这一篇就Over

一、简介:学习过Spring框架的人一定都会听过Spring的IoC(控制反转)、DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC、...

2023-03-20 14:53 xiyangw

域、模块、空间、闭包,你真的懂了吗?(模块控制域与作用域的关系)

Javascript有一个特性叫做域。尽管对于初学者来说理解域是有难度的,但我会尽力用最简单的方式让你理解域。理解域能让你的代码更优秀,减少错误,及有助于你做出更强大的模式设计。什么是域域是在运行时,...

这一次搞懂Spring自定义标签以及注解解析原理
这一次搞懂Spring自定义标签以及注解解析原理

前言在上一篇文章中分析了Spring是如何解析默认标签的,并封装为BeanDefinition注册到缓存中,这一篇就来看看对于像context这种自定义标签是如...

2023-03-20 14:53 xiyangw

前端基础进阶(七)-前端工程师最容易出错的问题-this关键字
前端基础进阶(七)-前端工程师最容易出错的问题-this关键字

我们在学习JavaScript的时候,因为对一些概念不是很清楚,但是又会通过一些简洁的方式把它给记下来,那么这样自己记下来的概念和真正的概念产生了很强的偏差.当...

2023-03-20 14:52 xiyangw

深入K8s:守护进程DaemonSet及其源码分析(k8s 进程)
深入K8s:守护进程DaemonSet及其源码分析(k8s 进程)

建议学习:膜拜!阿里内部都在强推的K8S(kubernetes)学习指南,不能再详细了最近也一直在加班,处理项目中的事情,发现问题越多越是感觉自己的能力不足,...

2023-03-20 14:52 xiyangw

Spring 是如何解析 bean 标签的?(spring beans标签)
Spring 是如何解析 bean 标签的?(spring beans标签)

前情回顾上回「SpringIoC容器初始化(2)」说到了Spring如何解析我们定义的<bean>标签,代码跟进了一层又一层,跋山涉水,...

2023-03-20 14:52 xiyangw

快速了解JavaScript文本框操作(javascript文本框代码)
快速了解JavaScript文本框操作(javascript文本框代码)

HTML中使用<input>元素表示单行输入框和<textarea>元素表示多行文本框。HTML中使用的<input&...

2023-03-20 14:51 xiyangw

荐读|30道JavaOOP面试题,可以和面试官扯皮了
荐读|30道JavaOOP面试题,可以和面试官扯皮了

面试是我们每个人都要经历的事情,大部分人且不止一次,今天给大家准备了30道JavaOOP面试题,希望能够帮助到对Java感兴趣的同学,让大家在找工作的时候能够...

2023-03-20 14:51 xiyangw

源码系列——mybatis源码刨析总结,下(mybatis源码分析)
源码系列——mybatis源码刨析总结,下(mybatis源码分析)

接上文简答题一.1.Mybatis动态sql是做什么的?1.动态sql就是根据条件标签动态的拼接sql,包括判空,循环,拼接等2.哪些动态sql?动态sql大...

2023-03-20 14:50 xiyangw

Java面试题(第二弹)(java面试题及答案整理)
Java面试题(第二弹)(java面试题及答案整理)

1.抽象类和接口的区别?接口可以被多重implements,抽象类只能被单一extends接口只有定义,抽象类可以有定义和实现接口的字段定义默认为:public...

2023-03-20 14:50 xiyangw

mybatis3 源码深度解析-动态 sql 实现原理(sql数据库基础知识)
mybatis3 源码深度解析-动态 sql 实现原理(sql数据库基础知识)

大纲动态sql使用示例SqlSource和BoundSql以及实现类LanguageDriver以及实现类SqlNode以及实现类动态sql解...

2023-03-20 14:50 xiyangw

第43节 Text、Comment及CDATASection(第43节 Text、Comment及CDATASection)
第43节 Text、Comment及CDATASection(第43节 Text、Comment及CDATASection)

本内容是《Web前端开发之Javascript视频》的课件,请配合大师哥《Javascript》视频课程学习。文本节点用Text类型表示,包含的是可以按字面解释...

2023-03-20 14:49 xiyangw

Qt读写三种文件(qt读取文件数据并赋值给变量)

第一种INI配置文件.ini文件是InitializationFile的缩写,即初始化文件。除了windows现在很多其他操作系统下面的应用软件也有.ini文件,用来配置应用软件以实现不同用户的要...

取消回复欢迎 发表评论: