Swift:使用协议和子类进行动态调度

Swift: dynamic dispatch with protocol and subclass

请考虑以下Swift5个代码:

protocol P: class {
    func call_foo()
    func foo()
    func call_bar()
    func bar()
}

extension P {
    func call_foo() { foo() }
    func foo() { print("P.foo") }
    func call_bar() { bar() }
    func bar() { print("P.bar") }
}

class C1: P {
    func foo() { print("C1.foo") }
}

class C2: C1 {
    func bar() { print("C2.bar") }
}

let c = C2()
c.call_foo()    //  C1.foo
c.foo()         //  C1.foo
c.call_bar()    //  P.bar
c.bar()         //  C2.bar

如果 P.call_foo() 中的 foo() 调用被动态调度到 C1.foo(),那么为什么 P.call_bar() 中的 bar() 调用没有被动态调度到C2.bar()?

唯一不同的是foo()在符合P的class中直接被覆盖,而bar()只在子class中被覆盖].为什么会有所不同?

鉴于 bar() 是一项协议要求,难道不应该始终动态调度对它的所有调用吗?

在您的扩展上下文中:

extension P {
    func call_foo() { foo() }
    func foo() { print("P.foo") }
    func call_bar() { bar() }
    func bar() { print("P.bar") }
}

C2不存在,P是一个协议,方法是静态调度的,虽然bar()是[=18=的要求],但没有实现通过 C1 符合 P 所以:

let c1: some P = C1()
c1.call_foo()    //  C1.foo
c1.foo()         //  C1.foo
c1.call_bar()    //  P.bar
c1.bar()         //  P.bar

这很正常,有趣的是你有:

let someP: some P = C2()
someP.call_foo()    //  C1.foo
someP.foo()         //  C1.foo
someP.call_bar()    //  P.bar
someP.bar()         //  P.bar

意思是,如果您只引用 some PC1 的子类 C2 的行为与它的超类完全相同:call_bar() 调用 P.bar()因为 C1 没有实现 bar()

现在让我们看看如果在 C1 中实现 bar() 会发生什么:

class C1: P {
    func foo() { print("C1.foo") }
    func bar() { print("C1.bar") }
}

class C2: C1 {
    override func bar() { print("C2.bar") }
}

如果我们使用 some P 引用 C1:

let c1: some P = C1()
c1.call_foo()    //  C1.foo
c1.foo()         //  C1.foo
c1.call_bar()    //  C1.bar
c1.bar()         //  C1.bar

现在 call_bar() 编译器 知道 它必须使用 C1.bar() 所以使用 some P 引用 C2 :

let someP: some P = C2()
someP.call_foo()    //  C1.foo
someP.foo()         //  C1.foo
someP.call_bar()    //  C2.bar
someP.bar()         //  C2.bar

子类 C2 的行为方式与它的超类 C1 相同,并且调用了 bar() get 的实现。 (我发现它有点 reassuring 当子类表现得像它们的父类时)。

现在让我们检查原始片段:

let c = C2()
c.call_foo()    //  C1.foo
c.foo()         //  C1.foo
c.call_bar()    //  C2.bar
c.bar()         //  C2.bar

有效!