通常在讨论像some这样的东西时,他们指的是返回类型.这个问题具体是关于在参数列表中使用anysome.

例如,在SWIFT的String文档中,您有这个初始化器...

init<T>(_ value: T, radix: Int = 10, uppercase: Bool = false)
where T : BinaryInteger

在SWIFT 5.6中,他们引入了关键字any,让我们可以更轻松地处理存在主义类型.有了这个改变,我知道理论上你可以像这样重写上面的内容.

init(_ value: any BinaryInteger, radix: Int = 10, uppercase: Bool = false)

当然,也有这个基于some关键字的版本,它也适用于...

init(_ value: some BinaryInteger, radix: Int = 10, uppercase: Bool = false)

我的问题是.哪一个最有意义呢?像这样使用存在类型而不是泛型有什么不利之处吗?那some版呢?我最初认为是的,泛型版本是最好的,因为编译器可以在编译时确定传递给它的是什么,但话又说回来,Existential any甚至some版本也是如此,因为如果不传递BinaryInteger,它就不会编译,我不太确定如何编写测试来判断这一点.

推荐答案

将协议类型P的值传递给SWIFT中的方法时,您当前可以使用4种可能的拼写:

  1. func f<T: P>(_ value: T)(通用)
  2. func f(_ value: some P)("不透明参数")
  3. func f(_ value: P)("‘赤裸裸的’存在主义‘")
  4. func f(_ value: any P)("‘显式’存在主义")

在这些拼写中,(1)和(2)是同义词,目前(3)和(4)是同义词.在使用(1)和(2)之间没有差别,在(3)和(4)之间目前*没有差别;但在(1)/(2)和(3)/(4)之间,有is个差别.

  1. f<T: P>(_: T)是获取保证符合协议Pconcrete类型T的参数的传统方式.这种获取参数的方式:
    • 允许您在编译时和运行时访问具体类型T,因此您可以对T本身执行操作
    • 没有开销,因为类型T在编译时是已知的,并且编译器知道值的大小和布局,并且可以适当地设置堆栈/寄存器;它可以将给它的任何参数直接传递给方法
    • 只能在参数类型为statically已知(在编译时)时调用;因此,可以使用具有Selfassociatedtype要求的协议类型来调用
  2. SE-0341 (Opaque Parameter Declarations)中引入的采用some Protocol的方法的版本是exactly,相当于用尖括号拼写的泛型版本.我将避免重复提议的内容,但在简介部分中,我详细说明了将此语法作为简化泛型参数拼写复杂性的一种方法
  3. f(_: P) is the traditional way of taking a parameter of an existential type which is guaranteed to conform to protocol P. This way of taking a parameter:
    • 不允许在编译时访问参数的具体基础类型;尽管这可以在运行时通过type(of:)动态访问
    • Has runtime overhead both to pass an argument to the method, and when accessing the value inside of the method: because the type of the argument to the method might not be known statically (而当 the compiler still needs to know how to set up the stack and registers in order to call the method), the parameter must be boxed up in an "existential box", which has the interface of P and can dynamically pass methods along to the underlying concrete value. This both has a cost of allocating an additional "box" at runtime to hold the actual value inside of a consistently-sized and -laid-out container, as well as the cost of indirecting as method calls on the box must dynamically dispatch to the underlying type
    • 无论参数的类型是否静态已知,都可以调用;因此,可以使用具有Selfassociatedtype要求的协议类型来调用cannot
  4. 协议类型之前的any关键字是在SE-0335 (Existential Any)中引入的,它有助于指示该类型被用作存在词.exactly等同于使用协议的裸名称right now(即any P == P),但是已经有一些关于最终使用协议的裸名称来代替地表示some P的讨论

因此,为了解决你的具体例子:

init(_ value: some BinaryInteger, radix: Int = 10, uppercase: Bool = false)

完全等同于原来的

init<T>(_ value: T, radix: Int = 10, uppercase: Bool = false)
where T : BinaryInteger

而当

init(_ value: any BinaryInteger, radix: Int = 10, uppercase: Bool = false)

not.而且,因为BinaryIntegerassociatedtype个需求,所以您也可以使用any版本,因为存在类型不会提供对基础关联类型的访问.(如果你试一试,你会得到classic 的error: protocol 'BinaryInteger' can only be used as a generic constraint because it has Self or associated type requirements)


general中,泛型在可能的情况下比存在型类型更可取,因为没有开销和更大的灵活性;但是,它们需要知道参数的静态类型,这并不总是可能的.

Existentials更容易接受输入,但在功能上明显更有限,而且是有成本的.

在更喜欢<T: P>some P之间,或者Pany P之间--目前的 Select 是主观的,但:

  1. 作为SE-0335的一部分,目前计划SWIFT 6将使用关键字any来表示存在协议的使用;与使用该关键字对应的是some,因此,如果您想要开始保护您的代码并保持一致性,开始迁移到anysome可能是个好主意
  2. 在不透明的参数语法和泛型语法之间, Select 由您自己决定,但不透明的参数目前不能涵盖泛型可以覆盖的所有用例,特别是具有更复杂的约束的用例.无论是在任何地方都坚持使用泛型,还是更喜欢不透明的参数并仅在必要时使用泛型,都是您需要做出的代码风格 Select

Swift相关问答推荐

在SWIFTUI&39;S视图修改器中使用等待关键字

KMM-Swift铸造密封类

可以将属性名称传递给 Swift 中的函数吗?

有没有办法让文本字段以不同的 colored颜色 显示而不影响半透明背景?

除法中如果除以完全因数需要更长时间吗?

在Swift中如何用变量计算0.9的立方

Swift:结果的失败类型不能是协议 - Type 'any ShadowError' cannot conform to Error

If 语句在 Swift 中有 2 个日期

为什么 SwiftUI 中 ForEach 的这个 Select 不起作用?

NavigationStack 和 TabView - 工具栏和标题不显示

从 iPhone 中的安全飞地获取真正的随机数?

无法使用 RealityKit 播放 USDZ 动画

如何在一个视图控制器中创建具有相同行为的多个集合视图?

类型 '()' 不能符合 View (除非它肯定是 View,这次没有恶作剧)

swiftui更改显示的 Select 器值

任何使 ScrollView 像 List 一样执行更灵活的修饰符?

调用从 SwiftUI 视图传递到 PageViewController.Coordinator 的函数

如何仅将 SwiftUI 不透明度应用于父视图?

让我的函数计算数组 Swift 的平均值

UILabel 文本不换行