前言介绍
Swift中已经有some
和any
两个关键字用于配合协议使用,来约束使用者的类型必须符合协议
some
some
关键字是在Swift5.1中引入,用于配合协议使用,创建不透明类型。在编译器看来,传入的参数依然是具体的类型,编译器使用了一个唯一的隐式类型接收这个类型,只不过编译器保证这个类型是遵循了这个协议的。它和使用泛型约束语法没任何区别
所以以下三种代码是等价的
// 使用简短泛型语法,约束T遵守协议
func wash<T: Vehicle>(_ vehicle: T) {}
// 使用完整泛型语法,约束T遵守协议
func wash<T>(_ vehicle: T) where T: Vehicle {}
// 使用some关键字,约束 匿名的不透明类型 遵守协议
// 因为这个类型在编译器看来和普通泛型类型的实参没任何区别。所以叫不透明类型
func wash(_ vehicle: some Vehicle) {}
some关键字可以用于函数参数、变量类型、类型修饰、函数返回值等
// 函数参数使用some
func funcUseSome(_ v: some Vehicle) {}
// 变量使用some
let varUseSome: some Vehicle = Car()
// 函数返回值类型使用some
func createSomeVehicle() -> some Vehicle {
return Car()
}
// 数组使用some
let arrUseSome: [some Vehicle] = []
不透明类型表示的隐式类型每次都是唯一的。下面的代码,一旦car变量就是一个隐式的不透明类型。
即使第二行代码又给car赋值了Car实例,这个Car类型也和上面生成的不透明类型不一样,会报错
var car: some Vehicle = Car()
// Cannot assign value of type 'Car' to type 'some Vehicle'
car = Car()
any
any是Swift5.6引入的,也是配合协议使用。它和some不同的是,它会将类型包装成existantial container。并且针对值类型和引用类型分别包装成不同容器类型
let myCar: any Vehicle = Car()
func wash(_ vehicle: any Vehicle) {}
any不会和some一样生成不透明的唯一类型,它最终就是Container类型,可以重复的赋值,只要符合协议就行
var myCar: any Vehicle = Car()
// 这里还可以继续赋值
myCar = Bus()
myCar = Car()
// 函数的if分支里,只要是符合协议的类型,都可以返回
func createAnyVehicle(isPublicTransport: Bool) -> any Vehicle {
if isPublicTransport {
return Bus()
} else {
return Car()
}
}
param pack
some 可以在保证在编译期就知道具体的类型,效率高。但它无法在集合中存放不同的符合协议的类型
any 把所有类型包装成existential container,极大的提高了灵活性,但同时也降低了效率
param pack 在Swift5.9引入,可以看作是对some的增强。这样我们在写接收不确定数量的泛型的函数时,就不用写多个重载函数了,用一个就可以搞定
没有param pack之前, 我们如果要支持元组的比较,要写N多个重载函数
func == (lhs: (), rhs: ()) -> Bool
func == <A, B>(lhs: (A, B), rhs: (A, B)) -> Bool where A: Equatable, B: Equatable
func == <A, B, C>(lhs: (A, B, C), rhs: (A, B, C)) -> Bool where A: Equatable, B: Equatable, C: Equatable
// and so on, up to 6-element tuples
有了param pack之后,我们只需要写下面的一个函数就可以了。并且在Swift6.0 param pack支持了for in 让代码更加简洁
// 使用 each Element引入不同的泛型形参
func == <each Element: Equatable>(lhs: (repeat each Element),
rhs: (repeat each Element)) -> Bool {
// 使用repeat each Element重复不同数量的泛型形参
// 编译器会保证lhs和rhs中泛型形参的数量是相等的
for (left, right) in repeat (each lhs, each rhs) {
guard left == right else { return false }
}
return true
}
一个应用实例
我们有一个协议,可以产生任意类型的值
protocol ValueProducer {
associatedtype Value: Codable
func evaluate() -> Value
}
我们有任意个可能产生值或产生错误的生产者,我们想要获取所有产生的值,将错误过滤
func evaluateAll<each V: ValueProducer, each E: Error>(result: repeat Result<each V, each E>) -> [any Codable] {
var evaluated: [any Codable] = []
for case .success(let valueProducer) in repeat each result {
evaluated.append(valueProducer.evaluate())
}
return evaluated
}
使用的时候可以这样
// 产生整数类型的值
struct IntProducer: ValueProducer {
let contained: Int
init(_ contained: Int) {
self.contained = contained
}
func evaluate() -> Int {
return self.contained
}
}
// 产生bool类型的值
struct BoolProducer: ValueProducer {
let contained: Bool
init(_ contained: Bool) {
self.contained = contained
}
func evaluate() -> Bool {
return self.contained
}
}
struct SomeError: Error {}
print(evaluateAll(result:
Result<SomeValueProducer,SomeError>.success(SomeValueProducer(5)),
Result<SomeValueProducer, SomeError>.failure(SomeError()),
Result<BoolProducer, SomeError>.success(BoolProducer(true))))
// [5, true]