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字值缓冲区(可以堆分配)中,然后将该缓冲区传递给参数
- 对于每个通用占位符,该函数都采用元数据指针参数。用于满足占位符的类型的元类型在调用时传递给此参数
- 对于给定占位符上的每个协议约束,函数采用协议见证表指针参数
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
}