swift - 在协议(protocol)定义中包含功能与仅在扩展中定义它的原因?

标签 swift protocols

采用以下协议(protocol)和扩展:

protocol ProtocolA {
    func myFunc()
}

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

这和将函数完全排除在协议(protocol)定义之外有什么区别(如果有的话),如下所示:

protocol ProtocolB { }

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

我发现了一个不同之处。如果我定义一个覆盖默认实现的结构,我只能将它转换为协议(protocol)并在我将函数保留在定义之外时调用协议(protocol)的实现:

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 那样从协议(protocol)定义中 获取函数,那么您可以通过将对象强制转换为协议(protocol)来访问默认实现。另一方面,如果您将函数保留在协议(protocol)定义中,则无法强制转换为协议(protocol)以获得默认协议(protocol)行为。

将函数定义排除在协议(protocol)之外似乎可以在行为方面实现最大的灵 active 。

缺点是什么?如果将函数从协议(protocol)定义中移除,您会失去什么?您是否完全失去了任何功能?

最佳答案

将函数声明为协议(protocol)定义的一部分指示编译器在调用函数时使用动态分派(dispatch),因为编译器希望实现协议(protocol)的类型为该函数提供实现。这称为方法要求。现在,如果类型未定义方法,则运行时会将方法调用解析为协议(protocol)扩展中声明的方法。

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

为了举例说明上述内容,让我们考虑以下代码:

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()是一个方法要求,意味着当draw()被调用时,runtime会搜索draw () 在元素的实际类型中的实现,在本例中是在 CircleSquare 中。

现在如果我们不将 draw 声明为方法要求,这意味着我们不会在协议(protocol)声明中提及它

protocol Shape {
}

那么编译器将不再使用动态调度,直接进入协议(protocol)扩展中定义的实现。因此代码将打印:

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 处理抽象类的方法:它为您提供了一种方法来指定您希望从符合该协议(protocol)的类型中得到什么,同时允许这些方法的默认实现。

关于swift - 在协议(protocol)定义中包含功能与仅在扩展中定义它的原因?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34777926/

相关文章:

ios - SwiftUI macos NSWindow 实例

swift - 通过更改 uv 坐标在 Metal 中出现意外性能

swift - 如何在类层次结构中应用 Swift 泛型/协议(protocol)?

swift - 类型 'StorageMetadata' 的值没有成员 'downloadURL'

swift - 如何使函数采用树文字?

protocols - 必须为现有的 proto 包含 Map 创建 java pojo

ios - 检查 Swift 中的协议(protocol)可用性

Objective-C 协议(protocol)不发送消息

arrays - 如何创建谓词以在 Swift 中过滤具有关联值的枚举数组?

swift - 我该如何解决有关委托(delegate)和协议(protocol)的问题?