不透明类型

具有不透明返回类型的函数或方法会隐藏返回值的类型信息。此时,函数不再提供具体的类型作为返回类型,而是根据它支持的协议来描述返回值。

在处理模块和调用代码之间关系时,异常类型信息非常有用,此时返回的底层数据类型仍然可以保持私有。而且不同于返回协议类型,不透明类型可以保证类型一致性--表一起能获取到类型信息,但是模块使用者却不能获取到

不透明类型解决的问题

返回不透明类型

可以认为不透明类型和泛型相反。

泛型允许调用一个方法,为这个方法的形参和返回值制定一个与实现无关的类型,例如

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"