问题的背景:
在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()) // 有效
关键特点:
some声明在返回值中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 { /* ... */ }
函数返回没有opaque的protocol,无法使用associatedtype的方法,需要复杂的类型擦除,代码无法编译或工作不优化。
优点:
缺点:
SwiftUI中的ViewBuilder使用some View,隐藏细节,提高类型安全,提升编译和运行速度。
优点:
缺点: