简体   繁体   English

使用Generic和Protocol作为类型参数之间的区别,在函数中实现它们的优缺点是什么

[英]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: 所以我的问题是:

  1. What are the specific scenarios for each of them (or a case which can only be done by the specific one, but not another)? 每种情况的具体情况是什么(或者只能通过特定情况而不是另一种情况进行的情况)?

  2. 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
}
  1. If there are several types conform to a Protocol, and are pass in to a generic function in different conditions dynamically. 如果有几种类型符合协议,并且动态地传入不同条件下的泛型函数。 As shown above, pressing different buttons will pass different types(StructA or StructB) of parameter into function, would the generic specialization still work in this case? 如上图所示,按下不同的按钮会将参数的不同类型(StructA或StructB)传递给函数,在这种情况下,通用特化仍然可以工作吗?

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 . 然后参数需要符合AB

//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: 一些问题:

  1. Do we really need protocol C ? 我们真的需要protocol C吗?
  2. Why not would we write like 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.

相关问题 泛型和协议类型函数参数之间的实际区别是什么? - What is the in-practice difference between generic and protocol-typed function parameters? 如何实现一个通用协议,它具有一个使用关联类型的函数? - How to implement to a generic protocol, which has a function using the Type of associatedtype? 具有泛型类型的协议功能 - Protocol function with generic type 如何要求泛型类型使用协议中的特定类型实现通用协议 - How to require a generic type implement a generic protocol using a specific type in the protocol Swift通用协议功能参数 - Swift Generic Protocol Function Parameters 使用具有通用数据类型的协议在屏幕之间传递数据 - Using protocol with generic data type to pass data between screens 如何在函数中实现泛型对协议的条件一致性? - How to implement conditional conformance of a generic to a protocol in a function? 在函数定义中将通用类型定义为协议类型 - Define a generic type as protocol type in function definition IOS开发中的协议、扩展和类别有什么区别? 以及如何恰当地使用它们? - What is the difference between protocol, extension and category in IOS development? And how to use them appropriately? 在Swift中使用通用协议实现委托 - Implement delegate using generic protocol in Swift
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM