当前位置: 首页 > 面试题库 >

为什么要在协议定义中包含功能而不是在扩展中定义功能?

司空赞
2023-03-14
问题内容

采用以下协议和扩展名:

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

换句话说,如果您像in中那样 从协议定义中删除
函数,ProtocolB则可以通过将对象强制转换为协议来访问默认实现。另一方面,如果将函数保留在协议定义中,则无法转换为协议以获取默认协议行为。

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

不利之处是什么?如果从协议定义中删除该功能,将会损失什么?您完全失去任何功能吗?


问题答案:

将函数声明为协议定义的一部分将指示编译器在调用函数时使用动态调度,因为编译器希望实现协议的类型能够为该函数提供实现。这称为method requirement。现在,如果类型未定义html" target="_blank">方法,则运行时会将方法调用解析为协议扩展中声明的方法。

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

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

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()是a method requirement,这意味着在draw()调用时,运行时将draw ()在元素的实际类型内(在这种情况下,在Circle和内)搜索实现Square

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

protocol Shape {
}

这样,编译器将不再使用动态分配,而直接进入协议扩展中定义的实现。因此,代码将打印:

This is a Shape
This is a Shape

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

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

因为编译器现在可以判断的第一个元素shapesCircle,并且由于Circle具有一个draw()方法,因此它将调用该元素。

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



 类似资料:
  • 问题内容: 我正在使用swift 2.0,我有一个协议和对该协议的扩展来创建方法的默认实现,代码如下: 稍后,我在代码中尝试调用此方法,并收到一条错误消息: “不能在不可变值上使用变异成员:’自身’是不可变的” 代码如下: 我唯一想到的是这种情况下的“ Self”是协议,而不是类。但是,我必须缺少一些东西才能使该概念起作用。该协议定义的方法的默认实现,该方法还可以编辑同一协议定义的值。 谢谢您的帮

  • 问题内容: 我正在尝试通过符合协议的编码模型来获取数据。但是它无法像下面的代码那样调用func : 但是在另一个演示中,效果很好,为什么呢? 问题答案: 解决方案1。 https://github.com/satishVekariya/SVCodable 试试这个代码,它扩展了可编码 解决方案2。 避免污染带有扩展名的Apple提供的协议 用

  • 问题内容: 我使用atocomplete.jquery插件来建议输入文本,结果得到了这个数组: 当我开始搜索从子字符串开始的东西时,它显示出数组排序如下: 我需要这样的东西: 有任何想法吗? 问题答案: 该插件可能区分大小写。尝试输入而不是。您可能将结果设置为不区分大小写。这个问题可能会有所帮助。 对于上的自定义排序函数,您可以使用任何JavaScript函数并将其作为参数传递给的方法,如下所示:

  • 问题内容: 我的项目结构是这样的。 在中,我有此代码。 在中时,我有此代码。 当我尝试进行编译时,我得到了。这种构造代码的方式可行吗? 问题答案: 尝试只运行。当给它一个go文件作为参数时,它将不会寻找其他go文件。你也可以

  • 问题内容: Swift协议可以通过向函数和计算属性添加扩展来为其提供默认实现。我已经做了很多次。据我了解, 默认实现仅用作“后备” :当类型符合协议但不提供其自己的实现时,将 执行默认 实现。 至少这就是我阅读《 Swift编程语言》 指南的方式: 如果符合类型提供了自己的所需方法或属性的实现,则将使用该实现而不是扩展提供的实现。 现在,我遇到了这样的情况:我的实现某种协议的自定义类型 确实 为特

  • 本文向大家介绍racket 功能定义,包括了racket 功能定义的使用技巧和注意事项,需要的朋友参考一下 示例 可以使用以下lambda表格创建Racket中的函数。该表格包含一个参数列表和一个正文。 在上面的示例中,该函数接受两个参数,并返回将它们相乘的结果。 每当我们想将两个数字相乘时,重新编写函数及其主体都是很麻烦的,所以让我们给它起一个名字。要为其命名,请使用define表格。这会将功能