swift 中有一个不透明类型的概念,在 SwiftUI 开发中经常看到的 var body: some View
正是基于这个概念。这篇文章来研究下 some
的作用。
看下面的代码
protocol Shape {
func describe() -> String
}
struct Square: Shape {
func describe() -> String {
return "I'm Square"
}
}
struct Circle: Shape {
func describe() -> String {
return "I'm circle"
}
}
func makeShape() -> some Shape {
return Circle()
}
let shape = makeShape()
print(shape.describe())
复制代码
上面代码定义一个 protocol
,并且有两个 struct
实现了该协议,some
的使用出现在 makeShape
函数的返回值中,这里使用 some
表示:该函数的返回值是实现了 protocol
协议的某一个类型,具体是什么类型不知道。some
的「不透明类型」指的就是:没有明明白白告诉你具体是什么类型的,只是说明了这个类型和 Shape protocol
有关。
但是,makeShape
的返回值改成 Shape
仍然可以运行:
func makeShape() -> Shape {
return Circle()
}
复制代码
那 some
和 protocol
有啥区别?
我们知道 protocol
中可以有 associatedtype
,基于上面的代码,引入 associatedtype
看看。
protocol Shape {
associatedtype ColorType
var color: ColorType { get }
func describe() -> String
}
struct Square: Shape {
var color: String
typealias ColorType = String
func describe() -> String {
return "I'm Square"
}
}
struct Circle: Shape {
var color: Int
typealias ColorType = Int
func describe() -> String {
return "I'm circle"
}
}
func makeShape() -> some Shape {
return Circle(color: 255)
}
let shape = makeShape()
print(shape.describe())
复制代码
现在 Shape
引入了 associatedtype
,每个实现 Shape protocol
的 struct
都需要明确 ColorType
的具体类型。现在 makeShape
的返回值如果仍然是 Shape
就会报错:Protocol 'Shape' can only be used as a generic constraint because it has Self or associated type requirements
。原因在于 swift 不知道返回值 Shape
的 associatedtype
是什么类型。在引入 some
之后,swift 可以从 return
的类型推断出 associatedtype
为 Int
类型。
总结一下:为什么需要 some
?因为 protocol
中有 associatedtype
时,使用 protocol
的地方无法确定 associatedtype
的类型,那么这个时候使用 some
修饰,表示:这个地方是一个实现了 protocol
** 的类型,具体是什么类型尚未确定**。
最后,来理解 SwiftUI 中 some
的使用。
var body: some View {
VStack(alignment: .leading) {
Text("hello")
Text("world")
}
}
复制代码
这里,body
属性的具体类型为 VStack<TupleView<(Text, Text)>>
,随着布局深入,那么类型就会越来越复杂。所以这里使用 some View
来代替,告诉 swift:body
是一个实现了 View protocol
的结构体,具体是什么类型不清楚,你可以在编译的时候通过返回值推断出来。