【问题标题】:Override of protocol default implementation in a subsubclass doesn't participate in dynamic dispatch子类中协议默认实现的覆盖不参与动态调度
【发布时间】:2016-08-17 23:53:20
【问题描述】:

考虑以下游乐场:

protocol A {
    func f() -> String
}

extension A {
    func f() -> String { return "AAAA" }
}

class B: A {}

class C: B {
    func f() -> String { return "CCCC" }
}

let a: A = C()
let b: B = C()
let c: C = C()

a.f() // "AAAA" - why?
b.f() // "AAAA" - why?
c.f() // "CCCC"

我不明白为什么 a.f()b.f() 返回 "AAAA" - 它们应该返回 "CCCC" 因为 func f() -> String 应该是动态调度的(正如它在协议中声明的那样)。

如果我将class B 更改为如下所示:

class B: A {
    func f() -> String { return "BBBB" }
}

然后对.f() 的所有三个调用都按预期返回"CCCC"

我觉得这是 Swift 编译器中的一个错误,我在 Xcode 7.3.1 和 8.0-beta3 中检查过,这种行为在两者中都可以重现。

这实际上是预期的行为吗?

【问题讨论】:

  • @matt,我不同意这个问题是重复的,因为基本协议将 f() 方法声明为要求,而不是扩展,因此链接的问题没有回答我的问题。请考虑重新提出这个问题。
  • 这是重复的,因为答案回答了您的问题。
  • 也不要认为这是重复的。
  • 虽然相似,但我也不认为这是重复的。拥有相同的答案不会使问题重复。例如。 “什么是2+1?”和“π的第一个数字是多少?”。不是重复的问题,相同的答案。
  • 引用朋友的话,tldr 是:“只有声明一致性的类才能获得协议见证表”。出于这个原因,子类忽略了协议

标签: swift protocols


【解决方案1】:

这里涉及到几条规则。

有时会使用静态调度(在这种情况下,我们必须查看 var/let 的类型以找出将要使用的实现)。

其他时候使用 Dynamic Dispatch 代替(这意味着使用变量内部对象的实现)。

让我们考虑一般的例子

let foo: SomeType1 = SomeType2()
foo.f()

我将使用以下定义

  • classic implementation of f() 表示 f() 何时定义在协议扩展之外(因此在结构/类中)。

  • default implementation of f() 表示何时在协议扩展中定义了f()

动态调度

如果SomeType1struct/class 并具有自己的f() 的“经典”实现,则应用多态

这意味着如果SomeType2 没有f() 的经典实现,则使用SomeType1.f()。否则SomeType2.f() 获胜。

静态调度

如果SomeType1 没有f() 的经典实现但有默认实现,则关闭多态

在这种情况下,let/var 类型的默认实现获胜。

一个。

让我们看看你的第一个例子

let a: A = C()
a.f() // "AAAA"

在这个 A 中没有它自己的经典实现(因为它不是结构/类),但有一个默认实现。所以关闭了多态性,使用了A.f()

b.

第二个例子的规则相同

let b: B = C()
b.f() // "AAAA"

B 没有 f() 的经典实现,但有 f() 的默认实现。所以关闭了多态性并使用了B.f()(来自协议扩展)。

c.

最后C 类型的对象在C 类型的常量内。

var c:C
c.f() // "CCCC"

这里C 有一个经典的f() 实现。在这种情况下,协议实现被忽略并使用C.f()

更多

让我们看另一个例子

protocol Alpha { }
extension Alpha { func f() -> String { return "Alpha"} }
protocol Beta { }
extension Beta { func f() -> String { return "Beta"} }

class Foo: Alpha, Beta { }

let alpha: Alpha = Foo()
alpha.f() // "Alpha"

let beta: Beta = Foo()
beta.f() // "Beta"

如您所见,同样,包含该值的常量的类型获胜。如果将 Foo 对象放在 Foo 常量中,则会出现编译错误

let foo: Foo = Foo()
foo.f() //

error: ambiguous use of 'f()'
foo.f()
^
Swift 2.playground:2:23: note: found this candidate
extension Beta { func f() -> String { return "Beta"} }
                      ^
Swift 2.playground:6:24: note: found this candidate
extension Alpha { func f() -> String { return "Alpha"} }

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-19
  • 2017-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多