新的SwiftUI教程有以下代码:

struct ContentView: View {
    var body: some View {
        Text("Hello World")
    }
}

第二行是单词some,在他们的网站上突出显示,就好像它是一个关键字一样。

Swift 5.1似乎没有把some作为关键字,而且我不知道some这个词还能在那里做什么,因为它在类型通常的位置。有没有一个新的、未公布的Swift版本?它是一个我不知道的被用在类型上的函数吗?

关键字有的作用是什么?


当前回答

另一个答案很好地解释了新some关键字的技术方面,但这个答案将试图简单地解释为什么。


假设我有一个协议动物,我想比较两个动物是否是兄弟姐妹:

protocol Animal {
    func isSibling(_ animal: Self) -> Bool
}

这样,如果两个动物是同一类型的动物,那么比较它们是否是兄弟姐妹才有意义。


现在我举一个动物的例子供大家参考

class Dog: Animal {
    func isSibling(_ animal: Dog) -> Bool {
        return true // doesn't really matter implementation of this
    }
}

没有T的方式

现在,假设我有一个函数,从一个“家族”中返回一个动物。

func animalFromAnimalFamily() -> Animal {
    return myDog // myDog is just some random variable of type `Dog`
}

注意:这个函数实际上不会编译。这是因为在添加'some'特性之前,如果协议使用'Self'或泛型,则不能返回协议类型。但是假设你可以…假设这将myDog向上转换为抽象类型Animal,让我们看看会发生什么

现在问题来了,如果我试着这么做:

let animal1: Animal = animalFromAnimalFamily()
let animal2: Animal = animalFromAnimalFamily()

animal1.isSibling(animal2) // error

这将抛出一个错误。

为什么?原因是,当你调用animal1.isSibling(animal2)时,Swift不知道这些动物是狗、猫还是什么。据斯威夫特所知,animal1和animal2可能是不相关的动物物种。因为我们不能比较不同类型的动物(见上文)。这会出错

某个T如何解决这个问题

让我们重写之前的函数:

func animalFromAnimalFamily() -> some Animal {
    return myDog
}
let animal1 = animalFromAnimalFamily()
let animal2 = animalFromAnimalFamily()

animal1.isSibling(animal2)

animal1和animal2不是Animal,但它们是实现Animal的类。

这让你现在做的是,当你调用animal1. issibling (animal2)时,Swift知道animal1和animal2是同一类型。

所以我喜欢这样思考:

一些T让Swift知道T的什么实现正在被使用,但类的用户不知道。

(自我推销免责声明)我已经写了一篇博客文章,更深入地讨论了这个新功能(例子和这里一样)

其他回答

不透明的返回类型

如果你看一下我的例子,你会发现一些手势意味着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

我认为,到目前为止,所有的答案都遗漏了一些,其中一些主要用于像DSL(领域特定语言)这样的东西,比如SwiftUI或库/框架,这些东西的用户(其他程序员)与你自己不同。

你可能永远不会在你的正常应用程序代码中使用一些,除非它可以包装一个泛型协议,以便它可以用作类型(而不仅仅是作为类型约束)。some所做的是让编译器保持特定类型的知识,同时在它前面放置一个超类型facade。

因此,在SwiftUI中,你是用户,你所需要知道的就是某个东西是某个视图,而在幕后,各种各样的恶作剧可以继续进行,而你是被屏蔽的。这个对象实际上是一个非常特定的类型,但您永远不需要知道它是什么。然而,与协议不同的是,它是一个成熟的类型,因为无论它出现在哪里,它都只是某个特定成熟类型的外观。

在未来版本的SwiftUI中,你期待的是某个视图,开发人员可以改变特定对象的底层类型。但这不会破坏您的代码,因为您的代码一开始就没有提到底层类型。

因此,有些人实际上使协议更像一个超类。它几乎是一种真正的对象类型,尽管不是(例如,协议的方法声明不能返回some)。

因此,如果要使用某些类型来处理任何事情,很可能是在编写DSL或框架/库供他人使用时,并且希望掩盖底层类型细节。这将使您的代码更便于他人使用,并且允许您在不破坏他人代码的情况下更改实现细节。

但是,您也可以在自己的代码中使用它,作为一种方法,将代码的一个区域与隐藏在代码的另一个区域中的实现细节隔离开来。

另一个答案很好地解释了新some关键字的技术方面,但这个答案将试图简单地解释为什么。


假设我有一个协议动物,我想比较两个动物是否是兄弟姐妹:

protocol Animal {
    func isSibling(_ animal: Self) -> Bool
}

这样,如果两个动物是同一类型的动物,那么比较它们是否是兄弟姐妹才有意义。


现在我举一个动物的例子供大家参考

class Dog: Animal {
    func isSibling(_ animal: Dog) -> Bool {
        return true // doesn't really matter implementation of this
    }
}

没有T的方式

现在,假设我有一个函数,从一个“家族”中返回一个动物。

func animalFromAnimalFamily() -> Animal {
    return myDog // myDog is just some random variable of type `Dog`
}

注意:这个函数实际上不会编译。这是因为在添加'some'特性之前,如果协议使用'Self'或泛型,则不能返回协议类型。但是假设你可以…假设这将myDog向上转换为抽象类型Animal,让我们看看会发生什么

现在问题来了,如果我试着这么做:

let animal1: Animal = animalFromAnimalFamily()
let animal2: Animal = animalFromAnimalFamily()

animal1.isSibling(animal2) // error

这将抛出一个错误。

为什么?原因是,当你调用animal1.isSibling(animal2)时,Swift不知道这些动物是狗、猫还是什么。据斯威夫特所知,animal1和animal2可能是不相关的动物物种。因为我们不能比较不同类型的动物(见上文)。这会出错

某个T如何解决这个问题

让我们重写之前的函数:

func animalFromAnimalFamily() -> some Animal {
    return myDog
}
let animal1 = animalFromAnimalFamily()
let animal2 = animalFromAnimalFamily()

animal1.isSibling(animal2)

animal1和animal2不是Animal,但它们是实现Animal的类。

这让你现在做的是,当你调用animal1. issibling (animal2)时,Swift知道animal1和animal2是同一类型。

所以我喜欢这样思考:

一些T让Swift知道T的什么实现正在被使用,但类的用户不知道。

(自我推销免责声明)我已经写了一篇博客文章,更深入地讨论了这个新功能(例子和这里一样)

你可以假设swift是通用的。

对于那些被这个主题弄晕的人,这里有一篇非常解密和一步步的文章,感谢Vadim Bulavin。

https://www.vadimbulavin.com/opaque-return-types-and-the-some-keyword-in-swift/