前言
在 Swift5.9 的版本中,经过 Apple 进行一系列的改动,可以允许开发者使用可变泛型参数。
上述改动解决了 Swift 中的一个很重要问题,就是泛型函数需要特定数量的类型参数。虽然这些函数仍然可以接受可变参数,但它们最终仍然必须使用相同的类型。
比如下面的例子,假设我们三个不同的结构体用来代表程序员不同的角色:
arduino
struct FrontEndDev {
var name: String
}
struct BackEndDev {
var name: String
}
struct FullStackDev {
var name: String
}
当然,在实际需求中这些结构体会有更多的属性用来实现需求,但此处仅是举例,所以只给结构体一个 name
的属性。
接着我们为每个结构体创建实例:
ini
let johnny = FrontEndDev(name: "Johnny Appleseed")
let jess = FrontEndDev(name: "Jessica Appleseed")
let kate = BackEndDev(name: "Kate Bell")
let kevin = BackEndDev(name: "Kevin Bell")
let derek = FullStackDev(name: "Derek Derekson")
然后,当涉及到实际工作时,我们可以使用一个简单的函数将开发人员匹配在一起,如下所示:
swift
func pairUp1<T, U>(firstPeople: T..., secondPeople: U...) -> ([(T, U)]) {
assert(firstPeople.count == secondPeople.count, "You must provide equal numbers of people to pair.")
var result = [(T, U)]()
for i in 0..<firstPeople.count {
result.append((firstPeople[i], secondPeople[i]))
}
return result
}
它使用两个可变参数来接收一组第一个人和一组第二个人,然后将它们作为数组返回。我们现在可以使用它来创建一个组程序员,改组需要处理某些前端和后端的工作。
ini
let result1 = pairUp1(firstPeople: johnny, jess, secondPeople: kate, kevin)
到目前为止,都是按照 Swift 5.9 之前的版本去写的代码。但有趣的是: Derek
是一名全栈开发人员,因此既可以担任后端开发人员,也可以担任前端开发人员。然而,如果我们尝试使用johnny
, derek
作为第一个参数,那么 Swift 编译器将会报错。因为它需要第一个人和第二个人的类型是相同的。
我们可以使用 any
关键字来抹除所有的类型信息,从而解决这个问题。但这样就失去了很多可以利用的 Swift 特性,我们可以用参数包来更优雅的解决这个问题。
参数包
乍一开始看到这个语法可能会有点懵,下面先写出完整代码,后面再一点点分解它:
sql
func pairUp2<each T, each U>(firstPeople: repeat each T, secondPeople: repeat each U) -> (repeat (first: each T, second: each U)) {
return (repeat (each firstPeople, each secondPeople))
}
让我们分解成四部分来理解它:
<each T, each U>
创建了两个类型参数包,T
和U
repeat each T
是一个包扩展,它将参数包扩展为实际值 - 它相当于T...
,但避免了与...
用作运算符造成混淆- 返回类型意味着我们要发回配对程序员的元组,
T
和U
各一个 return
关键字的作用:它使用包扩展表达式从T
中获取一个值,从U
中获取一个值,然后将它们组合在一起形成返回值
需要注意的是,返回类型会自动确保我们的 T
和 U
类型具有相同的形状------它们内部具有相同数量的元素。因此,如果我们试图传入两组不同大小的数据,Swift 会编译报错,而不是像在第一个函数中那样使用assert()
。
下面是新函数的调用示例:
ini
let result2 = pairUp2(firstPeople: johnny, derek, secondPeople: kate, kevin)