Swift 的元组是精简版结构体?骗你的啦

449 查看

对于 Objective-C 的开发者,元组 类型是个有些陌生的概念。开发者可能会把元组和结构或类产生混淆。但事实上,元组比前面两者更加基础。我喜欢把元组看做一个微型结构体或 精简版结构体,该说法只用在极小的领域内,并随后被丢弃。

根据 Apple 关于 Swift Types 的官方文档:

元组类型是用括号包围,由一个逗号分隔的零个或多个类型的列表。

它是由其它类型组合而成的类型。元组可能包含零或多个类型,比如 字符串、整数、字符、布尔以及其它元组。同时请注意,元组是 *值传递,而不是引用。

上面是一个包含三个独立元素的元组,如果需要用较冗长的方式声明,也可以把上面的元组定义为:

如果我们想访问来自这些来自元组的单独元素,我们可能不得不使用类似数组的索引来访问:

但愿/现在你能很好理解该元组的语法。下面将以混合方式创建包含其他元组的新元组。初期样式:

这绝对疯狂,该点语法起初开起来像申请某物的某种语法版本。其原因是其在索引号为 1 的值类型是另一个元组,因此它使用了自己的存取器。

但是,为了避免使用索引,这必须记住与索引对应的值/,最好的做法是尽可能地给你的元组属性命名。

何处该用元组

当你想让一个函数能够返回多种类型时,这是元组的最佳使用场景。/如果你是 Objective-C 开发者,这个概念听起来有些奇怪,甚至是不可能的。我们之前为了解决这种问题,我们通常不得不返回一个 NSArray 或 NSDictionary。尽管如此,这种解决方案是危险的。因为一旦我们把 NSArray 转换成错误类型的对象,并调用指定方法会发生什么?这会导致在应用在运行时的闪退。

因此,让我们看看我们如何写出一个返回多种/类型的函数,而不是为了应对 Objective-C 的局限性而想出一个麻烦的方案。

为了提高代码可读性,我使用 typealias 预定义构造元组,也许你也该这样写,这样防止了在代码中意外地错误修改它的结构。

但是,正如你在上面看到的,这里有个叫做 getMultipleValues() 函数,它返回了一个包含三个独立的和不同数据类型的元组,每一个都有自己对应的变量名。

结构实例(不建议使用)

更新:Swift 3.0 将会废弃 建议:SE-0029 从函数应用里移除元组隐式转换

在文章的开头,我戏称元组为 精简版结构体。一个鲜为人知的事实是,元组可以用来实例化结构体。

结构体 CGSize 有两个元素,其中任何一个都可以是 CGFloat、Double 或 Int。 但是,如果我们传递一个元组进入这个函数会发生什么?

是不是很酷?但是有一点需要注意,元组的值必须和结构的构造函数的参数完全匹配,更改顺序、参数名称、数量,都会导致阻止Xcode继续编译。

“不能通过‘(height: CGFloat, width:CGFloat)’参数列表调用 ‘CGSize’ 类型的初始化”——Xcode

你看我做了什么?我通过交换高度和宽度的位置,改变了参数的布局。尽管它们都叫做 CGFloats ,编译器不能推断出你想要做什么。

有趣的事实

如果曾使用过 Swift,你很可能在不知道的情况下已经使用元组作为返回类型了。下面,我们有三个单独的函数,它们都做了相同的事情(就是不做任何事),并且没有返回任何值。

doNothingA() 用了更前卫的方式定义了一个空函数;而 doNothingB() 用了原始方式声明了空函数;doNothingC() 则使用缺省方式声明空函数明,省略了返回值。

最有趣的是,“Void”是“()”的 typealias,也就是空元组。如果你记得这篇文章的开头,你就会想到我提到过元组可以包含 或多个类型。

选择你的发音

人们如何发出单词 “tuple” 的音有很大的争议。 有些人喜欢说 “Two-pull”,而其他人说:“Tup-pl”,而最疯狂的人说:“Choo-pl”。 无论您喜欢哪一种,准备把它说出来吧。


本文的 示例代码 可以在 GitHub 上找到。这只是一个简单的 Playgrounds 文件,来帮助你加强对元组理解。