新的SwiftUI教程有以下代码:
struct ContentView: View {
var body: some View {
Text("Hello World")
}
}
第二行是单词some,在他们的网站上突出显示,就好像它是一个关键字一样。
Swift 5.1似乎没有把some作为关键字,而且我不知道some这个词还能在那里做什么,因为它在类型通常的位置。有没有一个新的、未公布的Swift版本?它是一个我不知道的被用在类型上的函数吗?
关键字有的作用是什么?
我认为,到目前为止,所有的答案都遗漏了一些,其中一些主要用于像DSL(领域特定语言)这样的东西,比如SwiftUI或库/框架,这些东西的用户(其他程序员)与你自己不同。
你可能永远不会在你的正常应用程序代码中使用一些,除非它可以包装一个泛型协议,以便它可以用作类型(而不仅仅是作为类型约束)。some所做的是让编译器保持特定类型的知识,同时在它前面放置一个超类型facade。
因此,在SwiftUI中,你是用户,你所需要知道的就是某个东西是某个视图,而在幕后,各种各样的恶作剧可以继续进行,而你是被屏蔽的。这个对象实际上是一个非常特定的类型,但您永远不需要知道它是什么。然而,与协议不同的是,它是一个成熟的类型,因为无论它出现在哪里,它都只是某个特定成熟类型的外观。
在未来版本的SwiftUI中,你期待的是某个视图,开发人员可以改变特定对象的底层类型。但这不会破坏您的代码,因为您的代码一开始就没有提到底层类型。
因此,有些人实际上使协议更像一个超类。它几乎是一种真正的对象类型,尽管不是(例如,协议的方法声明不能返回some)。
因此,如果要使用某些类型来处理任何事情,很可能是在编写DSL或框架/库供他人使用时,并且希望掩盖底层类型细节。这将使您的代码更便于他人使用,并且允许您在不破坏他人代码的情况下更改实现细节。
但是,您也可以在自己的代码中使用它,作为一种方法,将代码的一个区域与隐藏在代码的另一个区域中的实现细节隔离开来。
不透明的返回类型
如果你看一下我的例子,你会发现一些手势意味着myGesture属性将始终实现手势协议,然而,具体的实现类型不需要被调用者知道(它是隐藏的)。body属性也是如此——不是提供具体的类型,而是根据它支持的协议(即View)来描述返回值。
代码如下:
import SwiftUI
struct ContentView: View {
@State private var rotate: Angle = .zero
var myGesture: some Gesture {
RotationGesture()
.onChanged { rotate = $0 }
.onEnded { angle in rotate = angle }
}
var body: some View {
Rectangle()
.frame(width: 200, height: 200)
.foregroundColor(.blue)
.rotationEffect(rotate)
.gesture(myGesture)
}
}
除此之外,所有应用于矩形的SwiftUI修饰符在返回值时也使用some关键字。例如:
func foregroundColor(_ color: Color?) -> some View
我将尝试用非常基本的实际示例回答这个问题(这是一个关于什么的不透明结果类型)
假设你有关联类型的协议,并且有两个结构实现它:
protocol ProtocolWithAssociatedType {
associatedtype SomeType
}
struct First: ProtocolWithAssociatedType {
typealias SomeType = Int
}
struct Second: ProtocolWithAssociatedType {
typealias SomeType = String
}
在Swift 5.1之前,下面是非法的,因为ProtocolWithAssociatedType只能用作泛型约束错误:
func create() -> ProtocolWithAssociatedType {
return First()
}
但在Swift 5.1中,这是可以接受的(一些人补充说):
func create() -> some ProtocolWithAssociatedType {
return First()
}
以上是实际使用,广泛用于SwiftUI的一些视图。
但有一个重要的限制-返回类型需要在编译时知道,所以下面的函数声明了一个不透明的返回类型,但其主体中的返回语句没有匹配的底层类型错误:
func create() -> some ProtocolWithAssociatedType {
if (1...2).randomElement() == 1 {
return First()
} else {
return Second()
}
}