不透明类型
具有不透明返回类型的函数或方法会隐藏返回值的类型信息。此时,函数不再提供具体的类型作为返回类型,而是根据它支持的协议来描述返回值。
在处理模块和调用代码之间关系时,异常类型信息非常有用,此时返回的底层数据类型仍然可以保持私有。而且不同于返回协议类型,不透明类型可以保证类型一致性--表一起能获取到类型信息,但是模块使用者却不能获取到
不透明类型解决的问题
返回不透明类型
可以认为不透明类型和泛型相反。
泛型允许调用一个方法,为这个方法的形参和返回值制定一个与实现无关的类型,例如
func max<T>(_ x: T, _ y: T) -> T where T: Comparable { ... }
类型T由调用方法的代码决定,函数内部也要通过通用的方式写代码,才能应对调用者传入的各种类型
返回不透明类型函数中,不透明类型允许函数实现时,选择一个与调用代码无关的返回类型,比如:下面例子返回了一个梯形,但是却没直接输出梯形的底层类型
struct Square: Shape {
var size: Int
func draw() -> String {
let line = String(repeating: "*", count: size)
let result = Array<String>(repeating: line, count: size)
return result.joined(separator: "\n")
}
}
func makeTrapezoid() -> some Shape {
let top = Triangle(size: 2)
let middle = Square(size: 2)
let bottom = FlippedShape(shape: top)
let trapezoid = JoinedShape(
top: top,
bottom: JoinedShape(top: middle, bottom: bottom)
)
return trapezoid
}
let trapezoid = makeTrapezoid()
print(trapezoid.draw())
返回值定义为some Shape
;因此,该函数返回遵循Shape
协议的给定类型,而不需要指定任何具体类型。
这样写makeTrapezoid()
函数可以表明它公共接口的基本性质 --返回的是一个几何图形 -- 而不是部分的公共接口生成的特殊类型。
makeTrapezoid()
函数可以返回任意它需要的类型,只要其遵循Shape
协议即可,类似泛型函数的实现代码,其调用代码需要采用通用的方式,使其返回的任何Shape
类型值都能被正常使用
//泛型和不透明类型相结合
func flip<T: Shape>(_ shape: T) -> some Shape {
return FlippedShape(shape: shape)
}
注意:
如果函数内部有多个地方返回不透明类型,那么需要保证返回的均为同一类型,否则会报错
不透明类型和协议类型区别
虽然使用不透明类型作为函数返回值,看起来和返回协议类型非常相似,但这两者有一个主要区别,就在于是否需要保证类型一致性。
一个不透明类型只能对应一个具体的类型,即使函数调用者并不知道是哪种类型。
协议类型可以对应多个类型,只要他们遵循同一协议
不透明类型则比啊留了底层类型的唯一性,swift能够推断出来关联类型
protocol Container {
associatedtype Item
var count: Int { get }
subscript(i: Int) -> Item { get }
}
extension Array: Container { }
// 错误:有关联类型的协议不能作为返回类型。
func makeProtocolContainer<T>(item: T) -> Container {
return [item]
}
// 错误:没有足够多的信息来推断 C 的类型。
func makeProtocolContainer<T, C: Container>(item: T) -> C {
return [item]
}
func makeOpaqueContainer<T>(item: T) -> some Container {
return [item]
}
let opaqueContainer = makeOpaqueContainer(item: 12)
let twelve = opaqueContainer[0]
print(type(of: twelve))
// 输出 "Int"