当前位置: 首页 > 知识库问答 >
问题:

Swift(用户界面)中的“某些”关键字是什么?

滑畅
2023-03-14

新的SwiftUI教程包含以下代码:

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

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

Swift 5.1似乎没有将some作为关键字,我看不出单词some还能在那里做什么,因为它去了类型通常去的地方。斯威夫特有没有新的、未宣布的版本?这是一个函数,正在使用的类型,以我不知道的方式?

关键字some的作用是什么?

共有2个答案

仉英朗
2023-03-14

另一个答案很好地解释了新的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
    }
}

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

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

注意:此函数实际上不会编译。这是因为在添加“some”功能之前,如果协议使用“Self”或泛型,则无法返回协议类型。但假设你可以。。。假装这把我的狗提升为抽象类型的动物,让我们看看会发生什么

现在问题来了,如果我尝试这样做:

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

animal1.isSibling(animal2) // error

这将抛出一个错误。

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

让我们重写前面的函数:

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

animal1.isSibling(animal2)

animal1animal2不是Animal,但它们是实现Animal的类。

现在,您可以在调用animal1时执行此操作。isSibling(animal2),Swift知道animal1animal2是同一类型。

所以我喜欢这样想:

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

(自我推销免责声明)我写了一篇博文,对这个新功能进行了更深入的探讨(与这里的例子相同)

濮冠宇
2023-03-14

some View是SE-0244引入的不透明结果类型,在Swift 5.1和Xcode 11中提供。您可以将其视为“反向”通用占位符。

与调用者满足的常规通用占位符不同:

protocol P {}
struct S1 : P {}
struct S2 : P {}

func foo<T : P>(_ x: T) {}
foo(S1()) // Caller chooses T == S1.
foo(S2()) // Caller chooses T == S2.

不透明结果类型是实现所满足的隐式泛型占位符,因此您可以考虑:

func bar() -> some P {
  return S1() // Implementation chooses S1 for the opaque result.
}

就像这样:

func bar() -> <Output : P> Output {
  return S1() // Implementation chooses Output == S1.
}

事实上,这个特性的最终目标是允许以这种更明确的形式反向泛型,这也允许您添加约束,例如-

主要的一点是,返回some P的函数是返回符合P的特定具体类型的值的函数。尝试在函数中返回不同的一致性类型会产生编译器错误:

// error: Function declares an opaque return type, but the return
// statements in its body do not have matching underlying types.
func bar(_ x: Int) -> some P {
  if x > 10 {
    return S1()
  } else {
    return S2()
  }
}

因为隐式泛型占位符不能由多个类型满足。

这与返回P的函数相反,该函数可用于表示S1S2,因为它表示任意P一致性值:

func baz(_ x: Int) -> P {
  if x > 10 {
    return S1()
  } else {
    return S2()
  }
}

好的,那么不透明的结果类型-

协议当前的一个主要限制是PAT(具有相关类型的协议)不能作为实际类型使用。尽管这一限制可能会在该语言的未来版本中取消,因为不透明的结果类型实际上只是通用占位符,但它们现在可以与PAT一起使用。

这意味着您可以执行以下操作:

func giveMeACollection() -> some Collection {
  return [1, 2, 3]
}

let collection = giveMeACollection()
print(collection.count) // 3

由于不透明结果类型强制返回单个具体类型,编译器知道对同一函数的两个调用必须返回同一类型的两个值。

这意味着您可以执行以下操作:

//   foo() -> <Output : Equatable> Output {
func foo() -> some Equatable { 
  return 5 // The opaque result type is inferred to be Int.
}

let x = foo()
let y = foo()
print(x == y) // Legal both x and y have the return type of foo.

这是合法的,因为编译器知道xy具有相同的具体类型。这是==的一个重要要求,其中类型的两个参数都是Self

protocol Equatable {
  static func == (lhs: Self, rhs: Self) -> Bool
}

这意味着它需要两个值,这两个值都是与混凝土一致性类型相同的类型。即使Equatable可用作一种类型,您也无法将两个任意Equatable一致性值相互比较,例如:

func foo(_ x: Int) -> Equatable { // Assume this is legal.
  if x > 10 {
    return 0
  } else {
    return "hello world"      
  }
}

let x = foo(20)
let y = foo(5)
print(x == y) // Illegal.

因为编译器无法证明两个任意的equalable值具有相同的底层具体类型。

同样,如果我们引入另一个不透明类型返回函数:

//   foo() -> <Output1 : Equatable> Output1 {
func foo() -> some Equatable { 
  return 5 // The opaque result type is inferred to be Int.
}

//   bar() -> <Output2 : Equatable> Output2 {
func bar() -> some Equatable { 
  return "" // The opaque result type is inferred to be String.
}

let x = foo()
let y = bar()
print(x == y) // Illegal, the return type of foo != return type of bar.

该示例是非法的,因为尽管foobar都返回一些相等的,但它们的“反向”通用占位符Output1Output2可以由不同的类型来满足。

与常规协议类型的值不同,不透明结果类型与常规通用占位符很好地组合在一起,例如:

protocol P {
  var i: Int { get }
}
struct S : P {
  var i: Int
}

func makeP() -> some P { // Opaque result type inferred to be S.
  return S(i: .random(in: 0 ..< 10))
}

func bar<T : P>(_ x: T, _ y: T) -> T {
  return x.i < y.i ? x : y
}

let p1 = makeP()
let p2 = makeP()
print(bar(p1, p2)) // Legal, T is inferred to be the return type of makeP.

如果makeP刚刚返回P,这将不起作用,因为两个P值可能具有不同的底层具体类型,例如:

struct T : P {
  var i: Int
}

func makeP() -> P {
  if .random() { // 50:50 chance of picking each branch.
    return S(i: 0)
  } else {
    return T(i: 1)
  }
}

let p1 = makeP()
let p2 = makeP()
print(bar(p1, p2)) // Illegal.

此时,您可能在想,为什么不将代码编写为:

func makeP() -> S {
  return S(i: 0)
}

好的,使用不透明的结果类型,您可以通过只公开P提供的接口,使类型S成为一个实现细节,这样您就可以灵活地在以后更改具体类型,而不会破坏任何依赖于函数的代码。

例如,您可以替换:

func makeP() -> some P {
  return S(i: 0)
}

与:

func makeP() -> some P { 
  return T(i: 1)
}

不破坏任何调用makeP()代码

有关此功能的更多信息,请参阅《语言指南》和《Swift演进方案》的“不透明类型”部分。

 类似资料:
  • 问题内容: 来自标准库的文件包含围绕228行的以下几行代码: 在这种情况下是什么意思,或者通常是什么关键字? 问题答案: 是Swift 3中的新访问级别,随实现而引入 SE-0117允许区分公共访问权限和公共替代权限 从2016年8月7日开始,Swift 3快照和Xcode 8 beta 6都可以使用它。 简而言之: 在定义模块之外, 可以访问 和 可继承 一个类。一类成员是 可访问 和 可重写

  • 问题内容: 新的SwiftUI教程具有以下代码: 第二行单词和在其网站上被高亮显示,就像它是一个关键字一样。 Swift 5.1似乎没有作为关键字,而且我也看不出该词还有什么其他用处,因为它通常位于类型所在的位置。是否有Swift的未发布新版本?以某种我不知道的方式在类型上使用的函数吗? 关键字有什么作用? 问题答案: 是SE-0244引入的不透明结果类型,在带有Xcode 11的Swift 5.

  • 问题内容: 请看以下示例: 我已将的功能标记为。这到底是什么意思?我在子类中完全省略了它,并且编译器完全没有抱怨。那么,它是如何要求的呢? 问题答案: 请参见“自动初始化继承”: 规则1 如果子类没有定义任何指定的初始化器,它将自动继承其所有超类指定的初始化器。 规则2 如果您的子类提供了其所有超类指定初始化器的实现(通过按规则1继承它们,或通过提供自定义实现作为其定义的一部分),则它会自动继承所

  • 问题内容: 我想知道这些关键字在sql server中的用途以及何时使用它?我的意思是人们会遇到什么情况。 只是看到他们使用上面的SQL ,是什么,? 会发生什么,? 什么是?当我们使用时会发生什么? 这里上面的sql用于更新语句,并且还在嵌套的select子查询中……为什么? 会发生什么? 我的要求是,请以大量示例说明每个要点,以使我可以理解这些关键字的用法。谢谢 问题答案: 它们是用于指定锁定

  • 问题内容: 在JavaScript中的关键字可能会相当混乱首次遇到它的时候,人们往往会认为JavaScript是不是面向对象的编程语言。 它是什么? 它解决什么问题? 什么时候合适,什么时候不合适? 问题答案: 它做五件事: 它创建一个新对象。这个对象的类型就是 object 。 它将这个新对象的内部不可访问的 [[prototype]] (即 proto )属性设置为构造函数的外部可访问 原型

  • 问题内容: 我阅读了一些有关该关键字的文章,但无法弄清其正确用法。您能否告诉我在C#和Java中应该使用什么? 问题答案: 对于C#和Java,“ volatile”告诉编译器一个变量的值一定不能被缓存,因为它的值可能会在程序本身范围之外改变。然后,如果变量“超出其控制范围”更改,编译器将避免可能导致问题的任何优化。