编程高级iOS开发者

什么是Swift中的'opaque types' (some),何时以及为什么使用它们,它们与具有associated type的protocol有什么区别?

用 Hintsage AI 助手通过面试

答案。

问题的背景:

在Swift 5之前,返回一个符合具有associated type的protocol的值时,常常会遇到限制——类型不能直接作为返回类型使用,需要进行类型擦除。为了提高可读性和性能,引入了opaque types——通过关键字some返回的值,允许在公共接口中描述抽象。

问题:

当需要隐藏实际返回值的类型,同时通过protocol保持抽象,但又要避免动态调度和类型擦除的开销时。例如,返回集合、序列、视图组件。

解决方案:

Opaque types允许返回符合protocol的类型,同时隐藏其具体实现。编译器知道实际类型,但调用方不知道。

示例:

protocol Shape { func area() -> Double } struct Circle: Shape { var radius: Double func area() -> Double { Double.pi * radius * radius } } func makeCircle() -> some Shape { return Circle(radius: 3) } let s = makeCircle() print(s.area()) // 有效

关键特点:

  • Opaque type——始终是一个与protocol对应的相同类型,通过some声明在返回值中
  • 比类型擦除更快、更严格,允许在protocol中处理associated type
  • 不允许在不同的分支返回不同的类型(所有的return必须是相同的类型)

反向问题。

opaque type (some Protocol)与返回类型Protocol有什么区别?

Opaque type在编译时具有具体实现(虽然对外隐藏)。返回Protocol时使用动态调度,如果存在associated type则没有类型安全。

能否在同一个函数中使用some Protocol返回不同类型?

不能。所有的return必须返回相同的实际类型:

func maker(flag: Bool) -> some Shape { if flag { return Circle(radius: 3) } else { return Square(size: 2) // 错误:返回类型不一致 } }

protocol中的associatedtype能否通过some Protocol使用?

可以。正因为这个原因(首先)需要opaque type:

protocol View { associatedtype Body } func makeView() -> some View { /* ... */ }

常见错误与反模式

  • 混淆some Protocol与Protocol——不同的案例和限制
  • 违反返回类型在所有分支上的同质性规则
  • 在更简单的protocol或typealias可以解决的地方使用some

实际案例

负面案例

函数返回没有opaque的protocol,无法使用associatedtype的方法,需要复杂的类型擦除,代码无法编译或工作不优化。

优点:

  • 在抽象中的灵活性,例如AnySequence

缺点:

  • 失去类型安全,低性能
  • 不能使用associated types

积极案例

SwiftUI中的ViewBuilder使用some View,隐藏细节,提高类型安全,提升编译和运行速度。

优点:

  • 可读的API,类型安全,没有动态调度
  • 维护简单

缺点:

  • 在同一个函数中无法返回不同类型