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

时间:2016-12-30 12:36:46

标签: swift generics parameters protocols

由于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)
}

第一个函数使用generic作为带有类型约束的参数类型,第二个函数直接使用protocol作为参数类型。但是,这两个函数可以产生相同的效果,这一点让我感到困惑。所以我的问题是:

  1. 每种情况的具体情况(或只能由特定情况而非另一情况进行的情况)?

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

  3. This great talk提到了泛型专业化,这是一种优化,可以从动态调度转变功能调度方式(带有非泛型的功能)参数)静态调度内联(带有泛型参数的功能)。由于静态调度内联动态调度相比成本更低,因此使用泛型实现功能始终可以提供更好的性能。

    @Hamish还在this post中提供了很多信息,请查看更多信息。

    以下是一个新问题:

    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. 如果有几种类型符合协议,并且动态地传入不同条件的通用函数。如上图所示,按下不同的按钮会将参数的不同类型(StructA或StructB)传递给函数,在这种情况下,通用特化仍然可以工作吗?

2 个答案:

答案 0 :(得分:9)

实际上有一个来自今年WWDC的视频(关于类,结构和协议的性能;我没有链接,但你应该能够找到它)。

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

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

答案 1 :(得分:-1)

现在我们下面有两个protocol

protocol A {
    func sometingA()
}
protocol B {
    func sometingB()
}

然后参数需要符合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()
}

似乎没有错,但是当我们定义这样的结构时:

struct S:A,B {
    func sometingB() {
        print("B")
    }

    func sometingA() {
        print("A")
    }
}

methodGeneric有效但我们需要将struct S:A,B更改为struct S:C才能使methodProtocol正常工作。一些问题:

  1. 我们真的需要protocol C吗?
  2. 我们为什么不写func method(s:S)
  3. 您可以在Generic Doc中详细了解此信息,以获取更多信息。