Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift 泛型和协议类型的函数参数在实践中有什么区别?_Swift_Generics_Swift Protocols - Fatal编程技术网

Swift 泛型和协议类型的函数参数在实践中有什么区别?

Swift 泛型和协议类型的函数参数在实践中有什么区别?,swift,generics,swift-protocols,Swift,Generics,Swift Protocols,给定没有任何关联类型的协议: protocol SomeProtocol { var someProperty: Int { get } } 在实践中,这两个功能之间有什么区别(不是说“一个是通用的,另一个不是”)?它们是否生成不同的代码,它们是否具有不同的运行时特征?当协议或功能变得非常重要时,这些差异是否会改变?(因为编译器可能会内联这样的内容) func-generic(部分:T)->Int { 返回一些 } func非通用(some:SomeProtocol)->Int { 返

给定没有任何关联类型的协议:

protocol SomeProtocol
{
    var someProperty: Int { get }
}
在实践中,这两个功能之间有什么区别(不是说“一个是通用的,另一个不是”)?它们是否生成不同的代码,它们是否具有不同的运行时特征?当协议或功能变得非常重要时,这些差异是否会改变?(因为编译器可能会内联这样的内容)

func-generic(部分:T)->Int
{
返回一些
}
func非通用(some:SomeProtocol)->Int
{
返回一些
}

我主要是问编译器的不同之处,我理解两者在语言层面的含义。基本上,
nonGeneric
是否意味着恒定的代码大小但较慢的动态调度,而
generic
使用每个传递类型不断增长的代码大小,但快速的静态调度?

如果您的
generic
方法有多个涉及
T
的参数,则会有所不同

func generic<T: SomeProtocol>(some: T, someOther: T) -> Int
{
    return some.someProperty
}
一些
和另一些可以是不同的类型,只要它们符合
SomeProtocol

(我意识到OP对语言含义的要求越来越少,而对编译器的功能要求越来越高,但我觉得列出泛型函数参数和协议类型函数参数之间的一般差异也是值得的)

1.受协议约束的通用占位符必须满足具体类型 这是的结果,因此不能使用
SomeProtocol
类型化参数调用
generic(some:)

struct Foo : SomeProtocol {
    var someProperty: Int
}

// of course the solution here is to remove the redundant 'SomeProtocol' type annotation
// and let foo be of type Foo, but this problem is applicable anywhere an
// 'anything that conforms to SomeProtocol' typed variable is required.
let foo : SomeProtocol = Foo(someProperty: 42)

generic(some: something) // compiler error: cannot invoke 'generic' with an argument list
                         // of type '(some: SomeProtocol)'
这是因为泛型函数需要符合
SomeProtocol
的某种类型的
T
参数,但
SomeProtocol
不是符合
SomeProtocol
的类型

但是,参数类型为
SomeProtocol
的非泛型函数将接受
foo
作为参数:

nonGeneric(some: foo) // compiles fine
这是因为它接受“任何可以键入为
SomeProtocol
”的内容,而不是“符合
SomeProtocol
”的特定类型”

2.专业化 如中所述,使用“存在容器”来表示协议类型的值

该容器包括:

  • 用于存储值本身的值缓冲区,长度为3个字。大于此长度的值将被堆分配,并且对值的引用将存储在值缓冲区中(因为引用的大小仅为1个字)

  • 指向类型元数据的指针。包含在类型元数据中的是指向其值见证表的指针,该表管理存在容器中的值的生存期

  • 指向给定类型的协议见证表的一个或多个指针(在这种情况下)。这些表跟踪该类型对可用于调用给定协议类型实例的协议要求的实现

默认情况下,使用类似的结构将值传递到通用占位符类型的参数中

struct Foo : SomeProtocol {
    var someProperty: Int
}

// of course the solution here is to remove the redundant 'SomeProtocol' type annotation
// and let foo be of type Foo, but this problem is applicable anywhere an
// 'anything that conforms to SomeProtocol' typed variable is required.
let foo : SomeProtocol = Foo(someProperty: 42)

generic(some: something) // compiler error: cannot invoke 'generic' with an argument list
                         // of type '(some: SomeProtocol)'
  • 该参数存储在一个3字值缓冲区(可以堆分配)中,然后将该缓冲区传递给参数

  • 对于每个通用占位符,该函数都采用元数据指针参数。用于满足占位符的类型的元类型在调用时传递给此参数

  • 对于给定占位符上的每个协议约束,函数采用协议见证表指针参数

然而,在优化的构建中,Swift能够专门实现泛型函数——允许编译器为其应用的每种类型的泛型占位符生成一个新函数。这使得参数总是以增加代码大小为代价简单地按值传递。然而,随着讨论的进行也就是说,积极的编译器优化,特别是内联,可以抵消这种膨胀

3.发送协议要求 由于泛型函数可以被专门化,因此对传入的泛型参数的方法调用可以被静态调度(尽管显然不适用于使用动态多态性的类型,例如非final类)

但是,协议类型的函数通常不能从中受益,因为它们不能从专门化中受益。因此,对协议类型参数的方法调用将通过该给定参数的协议见证表动态调度,这将更加昂贵

尽管如此,简单的协议类型函数可能会从内联中受益。在这种情况下,编译器能够消除值缓冲区、协议和值见证表的开销(这可以通过检查a-O构建中发出的SIL来看出),允许它以与泛型函数相同的方式静态分派方法。但是,与泛型专门化不同,这种优化不能保证用于给定函数(除非您–但通常最好由编译器决定)

因此,一般来说,就性能而言,泛型函数优于协议类型函数,因为它们可以实现方法的静态分派,而不必内联

4.过载解决方案 在执行重载解析时,编译器将优先使用协议类型的函数而不是泛型函数

struct Foo : SomeProtocol {
    var someProperty: Int
}

func bar<T : SomeProtocol>(_ some: T) {
    print("generic")
}

func bar(_ some: SomeProtocol) {
    print("protocol-typed")
}

bar(Foo(someProperty: 5)) // protocol-typed

除了参数之外不带任何承诺,返回必须符合
SomeProtocol
。传递和返回的实际具体类型不一定必须相同。

值得注意的一点是,如果将它们展开为两个参数:
func-generic(some:T,other:T)->Int
func-nonGeneric(some:SomeProtocol,other:SomeProtocol)->Int
这些将不再等效。不确定
struct Foo : SomeProtocol {
    var someProperty: Int
}

func bar<T : SomeProtocol>(_ some: T) {
    print("generic")
}

func bar(_ some: SomeProtocol) {
    print("protocol-typed")
}

bar(Foo(someProperty: 5)) // protocol-typed
func generic<T : SomeProtocol>(a: T, b: T) -> T {
    return a.someProperty < b.someProperty ? b : a
}
func nongeneric(a: SomeProtocol, b: SomeProtocol) -> SomeProtocol {
    return a.someProperty < b.someProperty ? b : a
}