使用泛型和协议作为类型参数之间的区别,在函数中实现它们的优点和缺点有哪些 [英] Difference between using Generic and Protocol as type parameters, what are the pros and cons of implement them in a function

查看:225
本文介绍了使用泛型和协议作为类型参数之间的区别,在函数中实现它们的优点和缺点有哪些的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

由于Swift允许我们在函数中使用Protocol和Generic作为参数类型,所以我想到了下面的场景:

pre $ protocol AProtocol {
var name:String {get}
}
$ b $ class ClassA:AProtocol {
var name =Allen
}

func printNameGeneric< T:AProtocol>(param:T){
print(param.name)
}

func printNameProtocol(param:AProtocol) {
print(param.name)
}

第一个函数使用通用作为具有类型约束的参数类型,而第二个函数直接使用协议作为参数类型。但是,这两个功能可以产生相同的效果,这是令我困惑的一点。所以我的问题是:


  1. 它们每个的具体情况是什么(或者只能由特定的一个,但不是另一个)?


  2. 对于给定的情况,两个函数都会得到相同的结果。哪一个更好实施(或每个人的利弊)?/ b>

这次伟大的演讲提到了通用专业化,这是一个优化将函数调度的方式从动态调度(具有<非>非泛型参数的函数)转换为静态调度内联 (使用泛型参数)。由于静态调度内联动态调度相比较便宜,因此使用泛型实现函数总是可以提供更好的性能。



@Hamish也在这篇文章,看看更多的信息。



这是一个新的问题来找我:

  struct StructA:AProtocol {
var a:Int
}

struct StructB:AProtocol {
var b:Int
}

func buttonClicked(sender:UIButton){
var aVar:AProtocol

如果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){
//做某事
}



< ol start =3>

  • 如果有几种类型符合协议,并且动态地在不同条件下传递给泛型函数。如上所示,按下不同的按钮会将参数的不同类型(StructA或StructB)传递到函数中,这种情况下通用专业化是否仍然有效?




  • 在第二个函数中,如果您传递符合该协议的任何值,则实际上会传递一个容器,该容器具有传递值的24个字节的存储空间;类型相关信息16字节(用于确定调用哪些方法,ergo动态分派)。如果传入的值现在在内存中大于24个字节,则该对象将分配到堆上,并且容器将存储对该对象的引用!这实际上非常耗时,应该尽可能避免。



    在第一个使用通用约束的函数中,编译器实际上创建了另一个函数明确地执行该类型的函数操作。 (如果你使用这个函数有许多不同的类型,你的代码大小可能会显着增加;参见C ++代码bloat以供进一步参考。)但是,编译器现在可以静态调度方法,如果可能的话内联函数,不必分配任何堆空间。在上面提到的视频中,代码大小不必显着增加,因为代码仍然可以共享......所以具有通用约束的函数肯定是要走的路!


    Since Swift allows us using both Protocol and Generic as parameter types in a function, the scenario below has come into my mind:

    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. 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.

    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?

    解决方案

    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).

    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). 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! 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. 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!

    这篇关于使用泛型和协议作为类型参数之间的区别,在函数中实现它们的优点和缺点有哪些的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆