Swift

不透明类型与装箱协议类型

本文详细介绍了 Swift 中的不透明类型(some)和装箱协议类型(any),解析了它们的区别、使用场景以及如何选择合适的类型。

在 Swift 中,someany 关键字分别代表了不透明类型(Opaque Types)和装箱协议类型(Boxed Protocol Types)。理解它们的区别对于编写高效、清晰的 Swift 代码至关重要。

不透明类型 (Opaque Types) - some

不透明类型允许函数返回一个遵循特定协议的类型,而无需暴露具体的底层类型。这通过 some 关键字实现。

特点

  • 类型一致性:返回的底层类型必须在所有代码路径中保持一致。
  • 性能优化:编译器知道具体的底层类型,因此可以进行静态分发和优化。
  • 隐藏实现细节:调用者只知道返回值遵循某个协议,而不知道具体是哪个类型。
protocol Shape {
    func draw() -> String
}

struct Triangle: Shape {
    func draw() -> String { "Triangle" }
}

struct Square: Shape {
    func draw() -> String { "Square" }
}

func makeShape() -> some Shape {
    return Triangle() // 必须始终返回相同的类型
}

装箱协议类型 (Boxed Protocol Types) - any

装箱协议类型(也称为存在类型 Existential Types)允许存储任何遵循特定协议的类型的值。这通过 any 关键字实现。

特点

  • 动态性:可以在运行时存储不同类型的实例,只要它们遵循相同的协议。
  • 类型擦除:底层类型被擦除,编译器只知道它遵循该协议。
  • 动态分发:方法调用通常通过动态分发进行,可能带来轻微的性能开销。
var shapes: [any Shape] = []
shapes.append(Triangle())
shapes.append(Square())

for shape in shapes {
    print(shape.draw())
}

some vs any:如何选择?

  1. 优先使用 some

    • 当你需要返回一个特定类型但想隐藏其具体实现时。
    • 当你在泛型代码中需要保证类型一致性时。
    • some 提供更好的性能和更强的类型保证。
  2. 在必要时使用 any

    • 当你需要在一个集合中存储不同类型的对象(异构集合)时。
    • 当你需要动态地改变变量所持有的具体类型时。

总结

  • 使用 some Protocol 来表示“一个特定的、遵循该协议的类型”。
  • 使用 any Protocol 来表示“任何遵循该协议的类型(盒子)”。

从 Swift 5.7 开始,建议默认使用 some,仅在确实需要动态类型擦除功能时才使用 any

在 GitHub 上编辑

上次更新于