Opaque return types 是Swift 5.1新的语言属性。它可以用于函数、方法和属性返回一些值,无需向调用API的客户端揭示该值的具体类型。返回的类型将是一些实现了协议的类型。使用此解决方案,模块API不必公开泄漏该方法的基本内部返回类型,只需使用some关键字返回协议的隐晦类型即可。在SwiftUI中,该解决方案通常用于View中返回body属性中的某些View 。
以下是Opaque return types 提供的一些基本内容,可在我们想使用Swift创建API时使用:
- 提供一个特定类型的协议而无需暴露具体的类型,实现更好的API封装
- 由于不会返回具体类型,因此使用者不用担心未来变更协议的基础类型
- 在运行时才返回具体类保障了基础身份识别的稳定性。同时相比直接返回具体类型,此方案也失去了一些灵活性
- 此方案将返回类型的权限全部移交给函数了
通过实例深入探究Opaque return types
为了更加深入的理解Opaque return type 与 协议的返回类型的不同,让我们通过一些具体例子来展示Opaque的优势
- 定义一个protocal
protocol MobileOS {
associatedtype Version
var version: Version { get }
init(version: Version)
}
- 通过具体类型来实现协议
struct iOS: MobileOS {
var version: Float
}
struct Android: MobileOS {
var version: String
}
- 写个函数返回类型
(1) 直接返回协议类型会报错
func buildPreferredOS() -> MobileOS {
return iOS(version: 13.1)
}
// Compiler ERROR
Protocol 'MobileOS' can only be used as a generic constraint because it has Self or associated type requirements
(2) 直接返回具体类型类型没有问题
func buildPreferredOS() -> iOS {
return iOS(version: 13.1)
}
// Build successfully
此解决方案有效,但是如您所见,API现在将具体类型泄漏给调用方。如果将来我们改变主意并将Android作为函数的返回类型返回,则将需要大量的代码重构
(3) 返回通用类型
func buildPreferredOS<T: MobileOS>(version: T.Version) -> T {
return T(version: version)
}
let android: Android = buildPreferredOS(version: "Jelly Bean")
let ios: iOS = buildPreferredOS(version: 5.0)
是的,这种方法很好用。但是现在,API的调用者需要提供返回函数的具体类型。如果我们真的想让调用者不必关心具体的返回类型,那么这仍然不是正确的解决方案
(4) 最优的解决方案
func buildPreferredOS() -> some MobileOS {
return iOS(version: 13.1)
}
使用不透明的返回类型,我们最终可以将MobileOS作为函数的返回类型返回。编译器在此维护基础特定返回类型的标识,并且调用者不必知道返回类型的内部类型,只要它实现MobileOS协议即可。
Opaque return types 每次只能返回一种特定类型
func buildPreferredOS() -> some MobileOS {
let isEven = Int.random(in: 0...100) % 2 == 0
return isEven ? iOS(version: 13.1) : Android(version: "Pie")
}
// Compiler ERROR ?
Cannot convert return expression of type 'iOS' to return type 'some MobileOS'
func buildPreferredOS() -> some MobileOS {
let isEven = Int.random(in: 0...100) % 2 == 0
return isEven ? iOS(version: 13.1) : iOS(version: "13.0")
}
// Build Successfully ?
SwiftUI中Opaque Return Types的使用
struct Row: View {
var body: some View {
HStack {
Text("Hello SwiftUI")
Image(systemName: "star.fill")
}
}
}
如果不使用some,那么代码将无比复杂
HStack<TupleView<(Text, Image)>>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。