在协议定义中包含函数与仅在扩展中定义函数的原因?

时间:2016-01-13 22:01:42

标签: swift protocols

采用以下协议和扩展名:

protocol ProtocolA {
    func myFunc()
}

extension ProtocolA {
    func myFunc() {
        print("Default ProtocolA implementation.")
    }
}

它之间有什么区别,如果有的话,将函数完全从协议定义中删除,如下所示:

protocol ProtocolB { }

extension ProtocolB {
    func myFunc() {
        print("Default ProtocolB implementation.")
    }
}

我发现了一个区别。如果我定义一个覆盖默认实现的结构,我只能将它转换为协议并调用协议的实现,如果我将该函数从定义中删除:

struct A: ProtocolA {
    func myFunc() {
        print("Struct A's implementation.")
    }
}

struct B: ProtocolB {
    func myFunc() {
        print("Struct B's implementation.")
    }
}

A().myFunc()                   // "Struct A's implementation."
(A() as ProtocolA).myFunc()    // "Struct A's implementation."

B().myFunc()                   // "Struct B's implementation."
(B() as ProtocolB).myFunc()    // "Default protocol implementation."

换句话说,如果你从ProtocolB中取出协议定义中的功能,那么你可以通过将对象转换为协议来访问默认实现。另一方面,如果将函数保留在协议定义中,则无法转换为协议以获取默认协议行为。

将功能定义排除在协议之外似乎允许在行为方面具有最大的灵活性。

缺点是什么?如果从协议定义中取出该功能,您会失去什么?你是否失去了任何功能?

1 个答案:

答案 0 :(得分:6)

将函数声明为协议定义的一部分指示编译器在调用函数时使用动态调度,因为编译器期望实现协议的类型为该函数提供实现。这称为method requirement。现在,如果类型没有定义方法,那么运行时会将方法调用解析为协议扩展中声明的方法。

但是,声明协议扩展中的函数告诉编译器他不需要使用动态调度,而是使用静态调度,这更快,但是因为多态性不能很好地工作,因为即使符合协议的类型也实现了该方法,也会调用协议扩展实现。

为了举例说明,请考虑以下代码:

protocol Shape {
    func draw()
}

extension Shape {
    func draw(){
        print("This is a Shape")
    }
}

struct Circle: Shape {
    func draw() {
        print("This is a Circle")
    }
}

struct Square: Shape {
    func draw() {
        print("This is a Square")
    }
}

let shapes: [Shape] = [Circle(), Square()]

for shape in shapes {
    shape.draw()
}

上面的代码将有输出

This is a Circle 
This is a Square

这是因为draw()method requirement,这意味着当调用draw()时,运行时将在元素的实际类型中搜索draw ()的实现,在这种情况下是CircleSquare

现在,如果我们没有声明draw作为方法要求,这意味着我们不会在协议声明中提及它

protocol Shape {
}

然后编译器将不再使用动态分派,并将直接进入协议扩展中定义的实现。因此代码将打印出来:

This is a Shape
This is a Shape

更多,如果我们向下转换将数组的元素强制转换为我们期望的类型,那么我们就会得到重载行为。这将打印This is a Circle

if let circle = shapes[0] as? Circle {
    circle.draw()
}

因为编译器现在能够告诉shapes的第一个元素是Circle,并且由于Circle具有draw()方法,所以它将调用该方法。

这是Swift处理抽象类的方法:它为您提供了一种方法,您可以指定符合该协议的类型,并允许这些方法的默认实现。