编程iOS开发者,中级/高级

在Swift中,opaque types (some)是什么,它们有什么用,何时应将其应用于protocol types而不是protocol existentials?

用 Hintsage AI 助手通过面试

回答。

问题背景: Opaque types (some) 于Swift 5.1推出,开启了一种新的方式来抽象函数或属性的返回值,当类型对编译器已知但对用户隐藏时。这是protocol existentials (any Protocol) 的替代方案,但严格绑定于函数内部的特定类型。

问题: 当一个函数返回一个带有associatedtype的协议(例如,Sequence)时,无法直接编写:

func makeNumberSequence() -> Sequence { ... } // 错误

Protocol existentials允许返回任何实现,但不会保证每次调用时返回相同的类型:

func foo() -> any View { ... }

这导致不可预测性和弱类型安全。

解决方案: 使用opaque result type:

func makeNumbers() -> some Sequence { [1, 2, 3] }

现在编译器确切知道实际返回的类型,但它在外部是隐藏的。这提供了性能优化、安全性,允许使用SwiftUI DSL,简化模块间的类型交换。

关键特性:

  • opaque类型内部的具体类型始终是一个
  • 不支持在使用位置的associatedtype,而不是在定义中
  • 用于构建声明式API(例如SwiftUI)

带有陷阱的问题。

opaque types可以用于存储值(作为类的属性)吗?

不可以。Opaque types仅用于函数的返回值或计算属性。存储值或值数组时使用existentials (any Protocol)。

同一个函数的不同分支可以返回不同类型的some吗?

不可以。编译器要求所有分支返回相同的具体类型,否则会报错:

func foo(flag: Bool) -> some Sequence { if flag { return [1, 2, 3] } else { return ["a", "b", "c"] // 错误 } }

可以使用some来识别多个函数之间的返回类型吗?

不可以。每个带有some的函数返回其唯一的隐式类型,即使实际上是同一个数组。如果两个函数使用some,并且其协议或隐式类型不同,则不能将一个函数的结果用作另一个函数的参数。

常见错误与反模式

  • 试图通过some返回不同类型(编译错误)
  • 在需要protocol existentials的地方使用some(例如,用于存储状态)
  • 放弃优化,继续使用旧式protocol types

生活实例

负面案例

在项目中,一切都通过any Protocol返回,集合失去类型化,出现类型转换问题,编译时优化变慢。

优点:

  • 灵活的架构,能够组合不同类型

缺点:

  • 类型安全丧失,效率降低,类型转换问题

正面案例

在SwiftUI设计中,组件返回some View,每个模块清楚地定义内部类型。包的大小减少,构建速度加快。

优点:

  • 性能提高,类型安全增加

缺点:

  • 在需要动态存储时某种程度的灵活性丧失