iOS Swift No.23 - 不透明类型2

第二十三章 不透明类型

3. Differences Between Opaque Types and Protocol Types ( 不透明类型和泛型类型的区别 )

返回一个不透明类型看起来和使用协议类型作为函数的返回类型是比较相似的。 但是两个种类的返回类型不的地方在于它们各自保留的返回类型标识。一个不透明类型引用的是一个特殊的类型,即便是函数的调用者也不能看到是哪一个类型,一个协议类型可以引用任何一个遵循协议的类型。简单来讲,协议类型给我们更多关于它存储值的潜在类型的灵活性。而不透明类型可以是我们更加确定潜在类型的类型。

下面是一个返回协议类型的flip(_:)版本,其替代了使用不透明类型作为返回类型

func protoFlip<T: Shape>(_ shape: T) -> Shape {
    return FlippedShape(shape: shape)
}

这个版本的函数protoFlip(_:)有一个和flip(:)相同的函数体,并且该函数体总是返回一个相同类型的值,而不像flip(_:)函数体,protoFlip(_:)并没有请求返回该函数的返回值。只是要遵循Shape协议,另一方面来讲protoFlip(_:)用它的调用者联系API要比flip(_:)要更加亲密。protoFlip(:)保留了多个类型返回值的灵活性。

func protoFlip<T: Shape>(_ shape: T) -> Shape {
    if shape is Square {
        return shape    
    }

    return FlippedShape(shape: shape)
}

修正过后的这个代码版本,它会基于传入的图形形状返回一个Square的实例或FlippedShape的实例。函数返回这两个翻转的图形形状可能是完全不同的类型。 函数的其他有效版本可能返回的是不同类型的值,当我们饭庄相同图案形状的多个实例的时候。protoFlip(_:)上的较少的返回类型信息意味着很多个操作基于在无效的返回值上,举个例子,不可以使用==操作符来对比函数返回值的结果。

let protoFlippedTriangle = protoFlip(smallTriangle)
let sameThing = protoFlip(smallTriangle)
protoFlippedTriangle == sameThing  // Error

最后一行代码出错有下面几个原因,最直接的问题就是Shape并没有把==看作是协议要求的一部分。如果说我们尝试在协议的要求里面加上这个==操作符,同样的又回出现另一个问题,是因为该==操作符需要知道左边和右边的参数类型。此类运算符通常采用类型为 Self 的参数,匹配和采用协议的任何具体类型,但向协议添加Self要求不允许在将协议用作类型时发生类型擦除。but adding a Self requirement to the protocol doesn’t allow for the type erasure that happens when you use the protocol as a type.

函数将协议类型当作返回类型使用会给我们采用该协议而返回的任何类型很多灵活性。然而,换取灵活性的代价就是有些操作在返回值上面是无法执行的。 这个例子就给我们说明了==操作符是无效的。 无效的原因就是它取决于不使用协议类型不保留的特定类型信息。

使用该方法的另一个问题就是这个图形转化并不会嵌套,翻转长方形的结构其实是一个Shape类型的值。函数protoFlip(_:)采用的是遵循Shape协议类型的一个参数,协议类型的值并不遵循协议。函数的返回值并不遵循Shape协议,这就意味着像protoFlip(protoFlip(smallTriange))这样的代码应用了很多个转换是无效的,是因为翻转形状对函数protoFlip(_:)来说是无效的参数。

比较起来,不透明类型保留了基础类型的标识,所以swift可以推断关联类型,这就会使我们可以在协议类型不能用作返回值的地方使用不透明返回值。举个例子下面的这个就是来自泛型中的Container协议的一个版本

protocol Container {
    associatedtype Item
    var count: Int { get }
    subscript(i: Int) -> Item { get }
}
extension Array: Container { }

之所以不能把Container当作函数的返回值来使用,是因为该协议有一个关联类型。同样的我们也不可以在泛型返回类型里把它(container协议)当作一个约束来使用。因为在函数体的外面没有跟多信息来让swift推断出该泛型类型的种类。

// Error: Protocol with associated types can't be used as a return type.
func makeProtocolContainer<T>(item: T) -> Container {
    return [item]
}

// Error: Not enough information to infer C.
func makeProtocolContainer<T, C: Container>(item: T) -> C {
    return [item]
}

使用不透明类型some Container来作为返回类型表达了API所需的协定。 - 函数返回容器 但是拒绝指明容器的类型。

func makeOpaqueContainer<T>(item: T) -> some Container {
    return [item]
}
let opaqueContainer = makeOpaqueContainer(item: 12)
let twelve = opaqueContainer[0]
print(type(of: twelve))
// Prints "Int

类型twelve可以被推断为Int,这恰恰恰说明了类型推断适用于不透明类型。在makeOpaqueContainer(item:)的实现中泛型容器的潜在类型是[T], T 是 Int 所以返回值是一个整数的数组,并且Item的关联类型可以被推断为Int。Container的下标返回的是Item,这就意味着twelve类型同样可以被推断为Int

猜你喜欢

转载自blog.csdn.net/weixin_45026183/article/details/107412715