[英]Difference between using Generic and Protocol as type parameters, what are the pros and cons of implement them in a function
Since Swift allows us using both Protocol and Generic as parameter types in a function, the scenario below has come into my mind: 由于Swift允许我们在函数中使用Protocol和Generic作为参数类型,因此我想到了下面的场景:
protocol AProtocol {
var name: String{ get }
}
class ClassA: AProtocol {
var name = "Allen"
}
func printNameGeneric<T: AProtocol>(param: T) {
print(param.name)
}
func printNameProtocol(param: AProtocol) {
print(param.name)
}
The first function uses generic as parameter type with a type constraint, and the second function uses protocol as the parameter type directly. 第一个函数使用generic作为带有类型约束的参数类型,第二个函数直接使用protocol作为参数类型。 However, these two functions can have the same effect, which is the point confusing me. 但是,这两个函数可以产生相同的效果,这一点让我感到困惑。 So my questions are: 所以我的问题是:
What are the specific scenarios for each of them (or a case which can only be done by the specific one, but not another)? 每种情况的具体情况是什么(或者只能通过特定情况而不是另一种情况进行的情况)?
For the given case, both functions turn out the same result. 对于给定的情况,两个函数都得出相同的结果。 Which one is better to implement (or the pros and cons of each of them)? 哪一个更好实现(或者每个的优点和缺点)?
This great talk has mentioned generic specialization , which is a optimization that turn the way of function dispatching from dynamic dispatching (function with non-generic parameters) to static dispatching or inlining (function with generic parameters) . 这个伟大的演讲提到了泛型专业化 ,这是一种优化,它将函数调度的方式从动态调度 (具有非泛型参数的函数)转变为静态调度或内联 (具有泛型参数的函数) 。 Since static dispatching and inlining are less expensive in contrast with dynamic dispatching , to implement functions with generic can always provide a better performance. 由于静态调度和内联与动态调度相比成本较低,因此使用泛型实现函数总能提供更好的性能。
@Hamish also gave great information in this post , have a look for more information. @Hamish在这篇文章中也提供了很多信息,请查看更多信息。
Here is a new question came to me: 这是一个新的问题来找我:
struct StructA: AProtocol {
var a: Int
}
struct StructB: AProtocol {
var b: Int
}
func buttonClicked(sender: UIButton) {
var aVar: AProtocol
if sender == self.buttonA
{
aVar = StructA(a: 1)
}
else if sender == self.buttonA
{
aVar = StructB(b: 2)
}
foo(param: aVar)
}
func foo<T: AProtocol>(param: T) {
//do something
}
There is actually a video from this year's WWDC about that (it was about performance of classes, structs and protocols; I don't have a link but you should be able to find it). 实际上有一个来自今年WWDC的视频(关于类,结构和协议的性能;我没有链接,但你应该能够找到它)。
In your second function, where you pass a any value that conforms to that protocol, you are actually passing a container that has 24 bytes of storage for the passed value, and 16 bytes for type related information (to determine which methods to call, ergo dynamic dispatch). 在第二个函数中,您传递符合该协议的任何值,实际上您传递的容器具有24个字节的存储空间用于传递的值,16个字节用于传递类型相关信息(以确定要调用的方法,ergo动态调度)。 If the passed value is now bigger than 24 bytes in memory, the object will be allocated on the heap and the container stores a reference to that object! 如果传递的值现在大于内存中的24个字节,则对象将在堆上分配,容器存储对该对象的引用! That is actually extremely time consuming and should certainly be avoided if possible. 这实际上是非常耗时的,如果可能的话,当然应该避免。
In your first function, where you use a generic constraint, there is actually created another function by the compiler that explicitly performs the function's operations upon that type. 在您使用泛型约束的第一个函数中,编译器实际上创建了另一个函数,该函数在该类型上显式执行函数的操作。 (If you use this function with lots of different types, your code size may, however, increase significantly; see C++ code bloat for further reference.) However, the compiler can now statically dispatch the methods, inline the function if possible and does certainly not have to allocate any heap space. (如果你使用这个函数有很多不同的类型,你的代码大小可能会显着增加;请参阅C ++代码膨胀以供进一步参考。)但是,编译器现在可以静态调度方法,如果可能的话内联函数并确实不必分配任何堆空间。 Stated in the video mentioned above, code size does not have to increase significantly as code can still be shared... so the function with generic constraint is certainly the way to go! 在上面提到的视频中,代码大小不必显着增加,因为代码仍然可以共享...所以具有通用约束的函数肯定是要走的路!
Now we have two protocol
below: 现在我们有两个protocol
:
protocol A {
func sometingA()
}
protocol B {
func sometingB()
}
Then the parameter need to conform to both A
and B
. 然后参数需要符合A
和B
//Generic solution
func methodGeneric<T:A>(t:T)where T:B {
t.sometingA()
t.sometingB()
}
//we need protocol C to define the parameter type
protocol C:A,B {}
//Protocol solution
func methodProtocol(c:C){
c.sometingA()
c.sometingB()
}
It seems that nothing is wrong but when we define a struct like this: 似乎没有错,但是当我们定义这样的结构时:
struct S:A,B {
func sometingB() {
print("B")
}
func sometingA() {
print("A")
}
}
The methodGeneric
works but we need to change struct S:A,B
to struct S:C
to make methodProtocol
work. 方法methodGeneric
有效,但我们需要将struct S:A,B
更改为struct S:C
以使methodProtocol
有效。 Some questions: 一些问题:
protocol C
? 我们真的需要protocol C
吗? func method(s:S)
? 为什么我们不像func method(s:S)
那样写func method(s:S)
? You could read more about this in the Generic Doc for additional information . 您可以在Generic Doc中阅读有关此内容的更多信息以获取更多信息。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.