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

Aar*_*sen 7 protocols swift

采取以下协议和扩展:

protocol ProtocolA {
    func myFunc()
}

extension ProtocolA {
    func myFunc() {
        print("Default ProtocolA implementation.")
    }
}
Run Code Online (Sandbox Code Playgroud)

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

protocol ProtocolB { }

extension ProtocolB {
    func myFunc() {
        print("Default ProtocolB implementation.")
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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."
Run Code Online (Sandbox Code Playgroud)

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

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

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

Cri*_*tik 7

声明协议中的函数指示编译器在调用函数时使用动态调度,因为编译器期望实现协议的类型为该函数提供实现.这被称为a 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()
}
Run Code Online (Sandbox Code Playgroud)

上面的代码将有输出

This is a Circle 
This is a Square
Run Code Online (Sandbox Code Playgroud)

这是因为draw()是a method requirement,意味着当draw()调用when时,运行时将搜索draw ()元素的实际类型内的实现,在本例中是CircleSquare.

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

protocol Shape {
}
Run Code Online (Sandbox Code Playgroud)

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

This is a Shape
This is a Shape
Run Code Online (Sandbox Code Playgroud)

更多,如果我们向下转换将数组的元素转换为我们期望的类型,那么我们将得到静态绑定.这将打印This is a Circle

if let circle = shapes[0] as? Circle {
    circle.draw()
}
Run Code Online (Sandbox Code Playgroud)

因为编译器现在能够告诉第一个元素shapes是a Circle,并且因为Circle有一个draw()方法它会调用那个元素.

这是Swift处理抽象类的方法:它为您提供了一种方法,您可以指定符合该协议的类型所期望的内容,同时允许这些方法的默认实现.