试图找出奇怪的协议结果 Swift
Trying to Figure Out Strange Protocol Results in Swift
这是交易。我在我的实现编程中遇到过这种行为 运行,这让我很困惑。我正在努力弄清楚为什么会发生这种情况,以便将来可以解释它。
我将在一分钟内粘贴一个相当复杂(但最小)的游乐场,但我想首先概述这个问题。
问题是,如果我将 class(不是结构)定义为符合具有默认实现的协议,然后通过实现定义的方法来覆盖它,协议默认实现将继续执行,即使我希望执行辅助实现,继承层次结构也会被忽略。
下面是 playground(最新的 Xcode)和打印输出结果。
我期待什么
一切都应该遵循直接方法调用 (1) 设置的示例。这包括对协议默认值的一些回退。
实际发生了什么
协议中的默认实现(classes derive/to 与 classes 一致)不断被执行。
最费解的是(6) - (8).
(2) - (4) 也有点奇怪,因为 D 和 E(以及 Q 和 R)打印正确,但其他 none 打印正确。
有人知道我为什么会出现这种行为吗?
层级图
游乐场:
// MARK: - Test Functions
// Argument is Class A
func printValueFromClassA(_ inValueAsClassA: A) {
inValueAsClassA.printValue()
}
// Argument is Class H
func printValueFromClassH(_ inValueAsClassH: H) {
inValueAsClassH.printValue()
}
// Argument is Class J
func printValueFromClassJ(_ inValueAsClassJ: J) {
inValueAsClassJ.printValue()
}
// Argument is Class D
func printValueFromClassD(_ inValueAsClassD: D) {
inValueAsClassD.printValue()
}
// Argument is the Root protocol
func printValueFromProtocolA(_ inValueAsProtocolA: PA) {
inValueAsProtocolA.printValue()
}
// Argument is the first derived protocol
func printValueFromProtocolB(_ inValueAsProtocolB: PB) {
inValueAsProtocolB.printValue()
}
// Argument is the second derived protocol
func printValueFromProtocolC(_ inValueAsProtocolC: PC) {
inValueAsProtocolC.printValue()
}
// MARK: - Protocols
// Root protocol: Defines the method
protocol PA {
func printValue()
}
// Root protocol default implementation
extension PA {
func printValue() { print("\t\tProtocol A") }
}
// Derived protocol: Depends on Root protocol default
protocol PB: PA { }
// Derived protocol: defines a new default implementation
protocol PC: PB { }
// Deived protocol default implementation
extension PC {
func printValue() { print("\t\tProtocol C") }
}
// MARK: - Classes
// Direct conformance to PA; relying on the default implementation of the conforming method
class A: PA { }
// Derives from A, but implements the conforming method
class B: A {
func printValue() { print("\t\tClass B") }
}
// Derives from B, and overrides the conforming method
class C: B {
override func printValue() { print("\t\tClass C") }
}
// Direct conformance to PB (which is based on PA), and implements the conforming method
class D: PB {
func printValue() { print("\t\tClass D") }
}
// Derives from D, and overrides the conforming method
class E: D {
override func printValue() { print("\t\tClass E") }
}
// Direct conformance to PA, but defines the conforming method
class F: A {
func printValue() { print("\t\tClass F") }
}
// Derives from C, and overrides the conforming method (second override)
class G: C {
override func printValue() { print("\t\tClass G") }
}
// Direct conformance to PB, but relies on the default implementation of the conforming method
class H: PB { }
// Derives from H, but implements the conforming method.
class I: H {
func printValue() { print("\t\tClass I") }
}
// Direct conformance to the second derived protocol, and relies on that protocol's default implementation
class J: PC { }
// Derived from J, but implements the conforming method
class K: J {
func printValue() { print("\t\tClass K") }
}
// Derived from J, and overrides the method
class L: K {
override func printValue() { print("\t\tClass L") }
}
// Derived from F, and overrides the method
class M: F {
override func printValue() { print("\t\tClass M") }
}
// Derived from I, and overrides the method
class N: I {
override func printValue() { print("\t\tClass N") }
}
// Derived from N, and overrides the method
class O: N {
override func printValue() { print("\t\tClass O") }
}
// Derived from L, and overrides the method
class P: L {
override func printValue() { print("\t\tClass P") }
}
// Direct conformance to the second derived protocol, but implements the conforming method
class Q: PC {
func printValue() { print("\t\tClass Q") }
}
// Derived from Q, and overrides the method
class R: Q {
override func printValue() { print("\t\tClass R") }
}
// MARK: - Testing
// MARK: instance definitions
let instanceOfA = A()
let instanceOfB = B()
let instanceOfC = C()
let instanceOfD = D()
let instanceOfE = E()
let instanceOfF = F()
let instanceOfG = G()
let instanceOfH = H()
let instanceOfI = I()
let instanceOfJ = J()
let instanceOfK = K()
let instanceOfL = L()
let instanceOfM = M()
let instanceOfN = N()
let instanceOfO = O()
let instanceOfP = P()
let instanceOfQ = Q()
let instanceOfR = R()
// MARK: Directly calling the instance methods
print("1) Direct Method Call:")
print("\tClass A:")
instanceOfA.printValue() // "Protocol A"
print("\tClass B:")
instanceOfB.printValue() // "Class B"
print("\tClass C:")
instanceOfC.printValue() // "Class C"
print("\tClass D:")
instanceOfD.printValue() // "Class D"
print("\tClass E:")
instanceOfE.printValue() // "Class E"
print("\tClass F:")
instanceOfF.printValue() // "Class F"
print("\tClass G:")
instanceOfG.printValue() // "Class G"
print("\tClass H:")
instanceOfH.printValue() // "Protocol A"
print("\tClass I:")
instanceOfI.printValue() // "Class I"
print("\tClass J:")
instanceOfJ.printValue() // "Protocol C"
print("\tClass K:")
instanceOfK.printValue() // "Class K"
print("\tClass L:")
instanceOfL.printValue() // "Class L"
print("\tClass M:")
instanceOfM.printValue() // "Class M"
print("\tClass N:")
instanceOfN.printValue() // "Class N"
print("\tClass O:")
instanceOfO.printValue() // "Class O"
print("\tClass P:")
instanceOfP.printValue() // "Class P"
print("\tClass Q:")
instanceOfQ.printValue() // "Class Q"
print("\tClass R:")
instanceOfR.printValue() // "Class R"
// MARK: Calling via a function that requires the argument be the Root protocol
print("\n2) printValueFromProtocolA(_: PA):")
print("\tClass A:")
printValueFromProtocolA(instanceOfA) // "Protocol A"
print("\tClass B:")
printValueFromProtocolA(instanceOfB) // "Protocol A"
print("\tClass C:")
printValueFromProtocolA(instanceOfC) // "Protocol A"
print("\tClass D:")
printValueFromProtocolA(instanceOfD) // "Class D"
print("\tClass E:")
printValueFromProtocolA(instanceOfE) // "Class E"
print("\tClass F:")
printValueFromProtocolA(instanceOfF) // "Protocol A"
print("\tClass G:")
printValueFromProtocolA(instanceOfG) // "Protocol A"
print("\tClass H:")
printValueFromProtocolA(instanceOfH) // "Protocol A"
print("\tClass I:")
printValueFromProtocolA(instanceOfI) // "Protocol A"
print("\tClass J:")
printValueFromProtocolA(instanceOfJ) // "Protocol C"
print("\tClass K:")
printValueFromProtocolA(instanceOfK) // "Protocol C"
print("\tClass L:")
printValueFromProtocolA(instanceOfL) // "Protocol C"
print("\tClass M:")
printValueFromProtocolA(instanceOfM) // "Protocol A"
print("\tClass N:")
printValueFromProtocolA(instanceOfN) // "Protocol A"
print("\tClass O:")
printValueFromProtocolA(instanceOfO) // "Protocol A"
print("\tClass P:")
printValueFromProtocolA(instanceOfP) // "Protocol A"
print("\tClass Q:")
printValueFromProtocolA(instanceOfQ) // "Class Q"
print("\tClass R:")
printValueFromProtocolA(instanceOfR) // "Class R"
// MARK: Calling via a function that requires the argument be the first Derived protocol
print("\n3) printValueFromProtocolB(_: PB):")
print("\tClass D:")
printValueFromProtocolB(instanceOfD) // "Class D"
print("\tClass E:")
printValueFromProtocolB(instanceOfE) // "Class E"
print("\tClass H:")
printValueFromProtocolB(instanceOfH) // "Protocol A"
print("\tClass I:")
printValueFromProtocolB(instanceOfI) // "Protocol A"
print("\tClass J:")
printValueFromProtocolB(instanceOfJ) // "Protocol C"
print("\tClass K:")
printValueFromProtocolB(instanceOfK) // "Protocol C"
print("\tClass L:")
printValueFromProtocolB(instanceOfL) // "Protocol C"
print("\tClass N:")
printValueFromProtocolB(instanceOfN) // "Protocol A"
print("\tClass O:")
printValueFromProtocolB(instanceOfO) // "Protocol A"
print("\tClass P:")
printValueFromProtocolB(instanceOfP) // "Protocol A"
print("\tClass Q:")
printValueFromProtocolB(instanceOfQ) // "Protocol A"
print("\tClass R:")
printValueFromProtocolB(instanceOfR) // "Protocol A"
// MARK: Calling via a function that requires the argument be the second Derived protocol
print("\n4) printValueFromProtocolC(_: PC):")
print("\tClass J:")
printValueFromProtocolC(instanceOfJ) // "Protocol C"
print("\tClass K:")
printValueFromProtocolC(instanceOfK) // "Protocol C"
print("\tClass L:")
printValueFromProtocolC(instanceOfL) // "Protocol C"
print("\tClass P:")
printValueFromProtocolC(instanceOfP) // "Protocol C"
print("\tClass Q:")
printValueFromProtocolC(instanceOfQ) // "Class Q"
print("\tClass R:")
printValueFromProtocolC(instanceOfR) // "Class R"
// MARK: Calling via a function that requires the argument be an instance or subclass of Class D
print("\n5) printValueFromClassD(_: D):")
print("\tClass D:")
printValueFromClassD(instanceOfD) // "Class D"
print("\tClass E:")
printValueFromClassD(instanceOfE) // "Class E"
// MARK: Calling via a function that requires the argument be an instance or subclass of Class A
print("\n6) printValueFromClassA(_: A):")
print("\tClass A:")
printValueFromClassA(instanceOfA) // "Protocol A"
print("\tClass B:")
printValueFromClassA(instanceOfB) // "Protocol A"
print("\tClass C:")
printValueFromClassA(instanceOfC) // "Protocol A"
print("\tClass G:")
printValueFromClassA(instanceOfG) // "Protocol A"
// MARK: Calling via a function that requires the argument be an instance or subclass of Class H
print("\n7) printValueFromClassH(_: H):")
print("\tClass H:")
printValueFromClassH(instanceOfH) // "Protocol A"
print("\tClass I:")
printValueFromClassH(instanceOfI) // "Protocol A"
print("\tClass N:")
printValueFromClassH(instanceOfN) // "Protocol A"
print("\tClass O:")
printValueFromClassH(instanceOfO) // "Protocol A"
// MARK: Calling via a function that requires the argument be an instance or subclass of Class J
print("\n8) printValueFromClassJ(_: J):")
print("\tClass J:")
printValueFromClassJ(instanceOfJ) // "Protocol C"
print("\tClass K:")
printValueFromClassJ(instanceOfK) // "Protocol C"
print("\tClass L:")
printValueFromClassJ(instanceOfL) // "Protocol C"
print("\tClass P:")
printValueFromClassJ(instanceOfP) // "Protocol C"
打印输出:
1) Direct Method Call:
Class A:
Protocol A
Class B:
Class B
Class C:
Class C
Class D:
Class D
Class E:
Class E
Class F:
Class F
Class G:
Class G
Class H:
Protocol A
Class I:
Class I
Class J:
Protocol C
Class K:
Class K
Class L:
Class L
Class M:
Class M
Class N:
Class N
Class O:
Class O
Class P:
Class P
Class Q:
Class Q
Class R:
Class R
2) printValueFromProtocolA(_: PA):
Class A:
Protocol A
Class B:
Protocol A
Class C:
Protocol A
Class D:
Class D
Class E:
Class E
Class F:
Protocol A
Class G:
Protocol A
Class H:
Protocol A
Class I:
Protocol A
Class J:
Protocol C
Class K:
Protocol C
Class L:
Protocol C
Class M:
Protocol A
Class N:
Protocol A
Class O:
Protocol A
Class P:
Protocol C
Class Q:
Class Q
Class R:
Class R
3) printValueFromProtocolB(_: PB):
Class D:
Class D
Class E:
Class E
Class H:
Protocol A
Class I:
Protocol A
Class J:
Protocol C
Class K:
Protocol C
Class L:
Protocol C
Class N:
Protocol A
Class O:
Protocol A
Class P:
Protocol C
Class Q:
Class Q
Class R:
Class R
4) printValueFromProtocolC(_: PC):
Class J:
Protocol C
Class K:
Protocol C
Class L:
Protocol C
Class P:
Protocol C
Class Q:
Class Q
Class R:
Class R
5) printValueFromClassD(_: D):
Class D:
Class D
Class E:
Class E
6) printValueFromClassA(_: A):
Class A:
Protocol A
Class B:
Protocol A
Class C:
Protocol A
Class G:
Protocol A
7) printValueFromClassH(_: H):
Class H:
Protocol A
Class I:
Protocol A
Class N:
Protocol A
Class O:
Protocol A
8) printValueFromClassJ(_: J):
Class J:
Protocol C
Class K:
Protocol C
Class L:
Protocol C
Class P:
Protocol C
要覆盖需要实现该方法的方法,因此如果您尝试在 B 中使用 override
关键字,则会出现编译错误,因为虽然 A 符合 PA,但它不会实现其中的功能PA 而是使用默认值。
所以这很可能是造成混淆的原因,因为 A 尚未实现 printValue
即使可以用它调用类型 A 的对象,它也不能被覆盖,a.printValue()
,因为它依赖于协议的默认实现。
鉴于此,我认为大多数测试用例都可以理解为什么它们的行为如此,尽管我只关注案例 6。
在 A 中执行 printValue()
,看看会发生什么。您首先遇到需要修复的编译错误,但之后输出将是您所期望的。
关于案例 6 的打印输出,由于 A 没有覆盖任何内容,因此发送到 printValueFromClassA
的所有对象都将被视为 A 的实例,并将使用协议默认实现。
好的。我将离开 Joakim 的 greencheck,但我现在可以更明确地说明这一点。
这是 Swift 中的一个 "sorta-kinda" 错误。
基本上,如果您在基于具有默认实现的协议的 class 层次结构中覆盖 method/property,则 class 层次结构的 vtable 不存在直到您在没有默认实现的情况下访问该实体的级别。
我知道这没有意义,恐怕我的解释对于这个场地来说可能太长和涉及,但我描述得很详细,here。
我确实有一个更简单的(比上面的)要点来展示它,here。
我将从那个要点粘贴操场,但它有点长:
// This is the little demo, at the start.
protocol A {
var thisIsRequired: String { get }
var thisIsOptional: String { get }
}
extension A {
var thisIsOptional: String { get { "" } set {} }
}
struct conformsToA: A {
var thisIsRequired: String { "Struct conformsToA" }
}
// In the first protocol, we don't use a default implementation (required conformance).
protocol PA {
var myName: String { get }
}
struct SA: PA {
var myName: String = "Struct A"
}
class CA: PA {
var myName: String = "Class A"
}
class CB: CA {
override var myName: String { get { "Class B" } set { } }
}
// In the second protocol, we add a default implementation (optional conformance).
protocol PB: PA { }
extension PB {
var myName: String { "Protocol B" }
}
// This will "fall back" to the default implementation.
struct SB: PB { }
struct SC: PB {
var myName: String = "Struct C"
}
// This will "fall back" to the default implementation.
class CC: PB { }
class CD: CC {
var myName: String = "Class D"
}
class CE: CD {
override var myName: String { get { "Class E" } set { } }
}
class CF: PB {
var myName: String = "Class F"
}
class CG: CF {
override var myName: String { get { "Class G" } set { } }
}
let structInstanceSA = SA()
let classInstanceCA = CA()
let classInstanceCB = CB()
// Part one is all about the required conformance protocol.
print("PART ONE")
print("\tDirect Print:")
print("\t\tStruct A: \(structInstanceSA.myName)")
print("\t\tClass A: \(classInstanceCA.myName)")
print("\t\tClass B: \(classInstanceCB.myName)")
func printAsProtoclA(_ inItem: PA) -> String { inItem.myName }
print("\tprintAsProtoclA(_: PA):")
print("\t\tStruct A: \(printAsProtoclA(structInstanceSA))")
print("\t\tClass A: \(printAsProtoclA(classInstanceCA))")
print("\t\tClass B: \(printAsProtoclA(classInstanceCB))")
func printAsStructA(_ inItem: SA) -> String { inItem.myName }
print("\tprintAsStructA(_: SA):")
print("\t\tStruct A: \(printAsStructA(structInstanceSA))")
func printAsClassA(_ inItem: CA) -> String { inItem.myName }
print("\tprintAsClassA(_: SA):")
print("\t\tClass A: \(printAsClassA(classInstanceCA))")
print("\t\tClass B: \(printAsClassA(classInstanceCB))")
let structInstanceSB = SB()
let structInstanceSC = SC()
let classInstanceCC = CC()
let classInstanceCD = CD()
let classInstanceCE = CE()
let classInstanceCF = CF()
let classInstanceCG = CG()
// Part two is where things get interesting.
print("PART TWO")
print("\tDirect Print:")
print("\t\tStruct B: \(structInstanceSB.myName)")
print("\t\tStruct C: \(structInstanceSC.myName)")
print("\t\tClass C: \(classInstanceCC.myName)")
print("\t\tClass D: \(classInstanceCD.myName)")
print("\t\tClass E: \(classInstanceCE.myName)")
print("\t\tClass F: \(classInstanceCF.myName)")
print("\t\tClass G: \(classInstanceCG.myName)")
func printAsProtoclB(_ inItem: PB) -> String { inItem.myName }
print("\tprintAsProtoclA(_: PA):")
print("\t\tStruct B: \(printAsProtoclA(structInstanceSB))")
print("\t\tStruct C: \(printAsProtoclA(structInstanceSC))")
print("\t\tClass C: \(printAsProtoclA(classInstanceCC))")
print("\t\tClass D: \(printAsProtoclA(classInstanceCD))")
print("\t\tClass E: \(printAsProtoclA(classInstanceCE))")
print("\t\tClass F: \(printAsProtoclA(classInstanceCF))")
print("\t\tClass G: \(printAsProtoclA(classInstanceCG))")
print("\tprintAsProtoclB(_: PB):")
print("\t\tStruct B: \(printAsProtoclA(structInstanceSB))")
print("\t\tStruct C: \(printAsProtoclA(structInstanceSC))")
print("\t\tClass C: \(printAsProtoclA(classInstanceCC))")
print("\t\tClass D: \(printAsProtoclA(classInstanceCD))")
print("\t\tClass E: \(printAsProtoclA(classInstanceCE))")
print("\t\tClass F: \(printAsProtoclA(classInstanceCF))")
print("\t\tClass G: \(printAsProtoclA(classInstanceCG))")
func printAsStructB(_ inItem: SB) -> String { inItem.myName }
print("\tprintAsStructB(_: SB):")
print("\t\tStruct B: \(printAsStructB(structInstanceSB))")
func printAsStructC(_ inItem: SC) -> String { inItem.myName }
print("\tprintAsStructC(_: SC):")
print("\t\tStruct C: \(printAsStructC(structInstanceSC))")
func printAsClassC(_ inItem: CC) -> String { inItem.myName }
print("\tprintAsClassC(_: CC):")
print("\t\tClass C: \(printAsClassC(classInstanceCC))")
print("\t\tClass D: \(printAsClassC(classInstanceCD))")
print("\t\tClass E: \(printAsClassC(classInstanceCE))")
func printAsClassD(_ inItem: CD) -> String { inItem.myName }
print("\tprintAsClassD(_: CD):")
print("\t\tClass D: \(printAsClassD(classInstanceCD))")
print("\t\tClass E: \(printAsClassD(classInstanceCE))")
func printAsClassF(_ inItem: CF) -> String { inItem.myName }
print("\tprintAsClassF(_: SF):")
print("\t\tClass F: \(printAsClassF(classInstanceCF))")
print("\t\tClass G: \(printAsClassF(classInstanceCG))")
// In a perfect world, this would be a syntax error:
let name = classInstanceCC.myName
这是上面的 UML:
这是交易。我在我的实现编程中遇到过这种行为 运行,这让我很困惑。我正在努力弄清楚为什么会发生这种情况,以便将来可以解释它。
我将在一分钟内粘贴一个相当复杂(但最小)的游乐场,但我想首先概述这个问题。
问题是,如果我将 class(不是结构)定义为符合具有默认实现的协议,然后通过实现定义的方法来覆盖它,协议默认实现将继续执行,即使我希望执行辅助实现,继承层次结构也会被忽略。
下面是 playground(最新的 Xcode)和打印输出结果。
我期待什么
一切都应该遵循直接方法调用 (1) 设置的示例。这包括对协议默认值的一些回退。
实际发生了什么
协议中的默认实现(classes derive/to 与 classes 一致)不断被执行。
最费解的是(6) - (8).
(2) - (4) 也有点奇怪,因为 D 和 E(以及 Q 和 R)打印正确,但其他 none 打印正确。
有人知道我为什么会出现这种行为吗?
层级图
游乐场:
// MARK: - Test Functions
// Argument is Class A
func printValueFromClassA(_ inValueAsClassA: A) {
inValueAsClassA.printValue()
}
// Argument is Class H
func printValueFromClassH(_ inValueAsClassH: H) {
inValueAsClassH.printValue()
}
// Argument is Class J
func printValueFromClassJ(_ inValueAsClassJ: J) {
inValueAsClassJ.printValue()
}
// Argument is Class D
func printValueFromClassD(_ inValueAsClassD: D) {
inValueAsClassD.printValue()
}
// Argument is the Root protocol
func printValueFromProtocolA(_ inValueAsProtocolA: PA) {
inValueAsProtocolA.printValue()
}
// Argument is the first derived protocol
func printValueFromProtocolB(_ inValueAsProtocolB: PB) {
inValueAsProtocolB.printValue()
}
// Argument is the second derived protocol
func printValueFromProtocolC(_ inValueAsProtocolC: PC) {
inValueAsProtocolC.printValue()
}
// MARK: - Protocols
// Root protocol: Defines the method
protocol PA {
func printValue()
}
// Root protocol default implementation
extension PA {
func printValue() { print("\t\tProtocol A") }
}
// Derived protocol: Depends on Root protocol default
protocol PB: PA { }
// Derived protocol: defines a new default implementation
protocol PC: PB { }
// Deived protocol default implementation
extension PC {
func printValue() { print("\t\tProtocol C") }
}
// MARK: - Classes
// Direct conformance to PA; relying on the default implementation of the conforming method
class A: PA { }
// Derives from A, but implements the conforming method
class B: A {
func printValue() { print("\t\tClass B") }
}
// Derives from B, and overrides the conforming method
class C: B {
override func printValue() { print("\t\tClass C") }
}
// Direct conformance to PB (which is based on PA), and implements the conforming method
class D: PB {
func printValue() { print("\t\tClass D") }
}
// Derives from D, and overrides the conforming method
class E: D {
override func printValue() { print("\t\tClass E") }
}
// Direct conformance to PA, but defines the conforming method
class F: A {
func printValue() { print("\t\tClass F") }
}
// Derives from C, and overrides the conforming method (second override)
class G: C {
override func printValue() { print("\t\tClass G") }
}
// Direct conformance to PB, but relies on the default implementation of the conforming method
class H: PB { }
// Derives from H, but implements the conforming method.
class I: H {
func printValue() { print("\t\tClass I") }
}
// Direct conformance to the second derived protocol, and relies on that protocol's default implementation
class J: PC { }
// Derived from J, but implements the conforming method
class K: J {
func printValue() { print("\t\tClass K") }
}
// Derived from J, and overrides the method
class L: K {
override func printValue() { print("\t\tClass L") }
}
// Derived from F, and overrides the method
class M: F {
override func printValue() { print("\t\tClass M") }
}
// Derived from I, and overrides the method
class N: I {
override func printValue() { print("\t\tClass N") }
}
// Derived from N, and overrides the method
class O: N {
override func printValue() { print("\t\tClass O") }
}
// Derived from L, and overrides the method
class P: L {
override func printValue() { print("\t\tClass P") }
}
// Direct conformance to the second derived protocol, but implements the conforming method
class Q: PC {
func printValue() { print("\t\tClass Q") }
}
// Derived from Q, and overrides the method
class R: Q {
override func printValue() { print("\t\tClass R") }
}
// MARK: - Testing
// MARK: instance definitions
let instanceOfA = A()
let instanceOfB = B()
let instanceOfC = C()
let instanceOfD = D()
let instanceOfE = E()
let instanceOfF = F()
let instanceOfG = G()
let instanceOfH = H()
let instanceOfI = I()
let instanceOfJ = J()
let instanceOfK = K()
let instanceOfL = L()
let instanceOfM = M()
let instanceOfN = N()
let instanceOfO = O()
let instanceOfP = P()
let instanceOfQ = Q()
let instanceOfR = R()
// MARK: Directly calling the instance methods
print("1) Direct Method Call:")
print("\tClass A:")
instanceOfA.printValue() // "Protocol A"
print("\tClass B:")
instanceOfB.printValue() // "Class B"
print("\tClass C:")
instanceOfC.printValue() // "Class C"
print("\tClass D:")
instanceOfD.printValue() // "Class D"
print("\tClass E:")
instanceOfE.printValue() // "Class E"
print("\tClass F:")
instanceOfF.printValue() // "Class F"
print("\tClass G:")
instanceOfG.printValue() // "Class G"
print("\tClass H:")
instanceOfH.printValue() // "Protocol A"
print("\tClass I:")
instanceOfI.printValue() // "Class I"
print("\tClass J:")
instanceOfJ.printValue() // "Protocol C"
print("\tClass K:")
instanceOfK.printValue() // "Class K"
print("\tClass L:")
instanceOfL.printValue() // "Class L"
print("\tClass M:")
instanceOfM.printValue() // "Class M"
print("\tClass N:")
instanceOfN.printValue() // "Class N"
print("\tClass O:")
instanceOfO.printValue() // "Class O"
print("\tClass P:")
instanceOfP.printValue() // "Class P"
print("\tClass Q:")
instanceOfQ.printValue() // "Class Q"
print("\tClass R:")
instanceOfR.printValue() // "Class R"
// MARK: Calling via a function that requires the argument be the Root protocol
print("\n2) printValueFromProtocolA(_: PA):")
print("\tClass A:")
printValueFromProtocolA(instanceOfA) // "Protocol A"
print("\tClass B:")
printValueFromProtocolA(instanceOfB) // "Protocol A"
print("\tClass C:")
printValueFromProtocolA(instanceOfC) // "Protocol A"
print("\tClass D:")
printValueFromProtocolA(instanceOfD) // "Class D"
print("\tClass E:")
printValueFromProtocolA(instanceOfE) // "Class E"
print("\tClass F:")
printValueFromProtocolA(instanceOfF) // "Protocol A"
print("\tClass G:")
printValueFromProtocolA(instanceOfG) // "Protocol A"
print("\tClass H:")
printValueFromProtocolA(instanceOfH) // "Protocol A"
print("\tClass I:")
printValueFromProtocolA(instanceOfI) // "Protocol A"
print("\tClass J:")
printValueFromProtocolA(instanceOfJ) // "Protocol C"
print("\tClass K:")
printValueFromProtocolA(instanceOfK) // "Protocol C"
print("\tClass L:")
printValueFromProtocolA(instanceOfL) // "Protocol C"
print("\tClass M:")
printValueFromProtocolA(instanceOfM) // "Protocol A"
print("\tClass N:")
printValueFromProtocolA(instanceOfN) // "Protocol A"
print("\tClass O:")
printValueFromProtocolA(instanceOfO) // "Protocol A"
print("\tClass P:")
printValueFromProtocolA(instanceOfP) // "Protocol A"
print("\tClass Q:")
printValueFromProtocolA(instanceOfQ) // "Class Q"
print("\tClass R:")
printValueFromProtocolA(instanceOfR) // "Class R"
// MARK: Calling via a function that requires the argument be the first Derived protocol
print("\n3) printValueFromProtocolB(_: PB):")
print("\tClass D:")
printValueFromProtocolB(instanceOfD) // "Class D"
print("\tClass E:")
printValueFromProtocolB(instanceOfE) // "Class E"
print("\tClass H:")
printValueFromProtocolB(instanceOfH) // "Protocol A"
print("\tClass I:")
printValueFromProtocolB(instanceOfI) // "Protocol A"
print("\tClass J:")
printValueFromProtocolB(instanceOfJ) // "Protocol C"
print("\tClass K:")
printValueFromProtocolB(instanceOfK) // "Protocol C"
print("\tClass L:")
printValueFromProtocolB(instanceOfL) // "Protocol C"
print("\tClass N:")
printValueFromProtocolB(instanceOfN) // "Protocol A"
print("\tClass O:")
printValueFromProtocolB(instanceOfO) // "Protocol A"
print("\tClass P:")
printValueFromProtocolB(instanceOfP) // "Protocol A"
print("\tClass Q:")
printValueFromProtocolB(instanceOfQ) // "Protocol A"
print("\tClass R:")
printValueFromProtocolB(instanceOfR) // "Protocol A"
// MARK: Calling via a function that requires the argument be the second Derived protocol
print("\n4) printValueFromProtocolC(_: PC):")
print("\tClass J:")
printValueFromProtocolC(instanceOfJ) // "Protocol C"
print("\tClass K:")
printValueFromProtocolC(instanceOfK) // "Protocol C"
print("\tClass L:")
printValueFromProtocolC(instanceOfL) // "Protocol C"
print("\tClass P:")
printValueFromProtocolC(instanceOfP) // "Protocol C"
print("\tClass Q:")
printValueFromProtocolC(instanceOfQ) // "Class Q"
print("\tClass R:")
printValueFromProtocolC(instanceOfR) // "Class R"
// MARK: Calling via a function that requires the argument be an instance or subclass of Class D
print("\n5) printValueFromClassD(_: D):")
print("\tClass D:")
printValueFromClassD(instanceOfD) // "Class D"
print("\tClass E:")
printValueFromClassD(instanceOfE) // "Class E"
// MARK: Calling via a function that requires the argument be an instance or subclass of Class A
print("\n6) printValueFromClassA(_: A):")
print("\tClass A:")
printValueFromClassA(instanceOfA) // "Protocol A"
print("\tClass B:")
printValueFromClassA(instanceOfB) // "Protocol A"
print("\tClass C:")
printValueFromClassA(instanceOfC) // "Protocol A"
print("\tClass G:")
printValueFromClassA(instanceOfG) // "Protocol A"
// MARK: Calling via a function that requires the argument be an instance or subclass of Class H
print("\n7) printValueFromClassH(_: H):")
print("\tClass H:")
printValueFromClassH(instanceOfH) // "Protocol A"
print("\tClass I:")
printValueFromClassH(instanceOfI) // "Protocol A"
print("\tClass N:")
printValueFromClassH(instanceOfN) // "Protocol A"
print("\tClass O:")
printValueFromClassH(instanceOfO) // "Protocol A"
// MARK: Calling via a function that requires the argument be an instance or subclass of Class J
print("\n8) printValueFromClassJ(_: J):")
print("\tClass J:")
printValueFromClassJ(instanceOfJ) // "Protocol C"
print("\tClass K:")
printValueFromClassJ(instanceOfK) // "Protocol C"
print("\tClass L:")
printValueFromClassJ(instanceOfL) // "Protocol C"
print("\tClass P:")
printValueFromClassJ(instanceOfP) // "Protocol C"
打印输出:
1) Direct Method Call:
Class A:
Protocol A
Class B:
Class B
Class C:
Class C
Class D:
Class D
Class E:
Class E
Class F:
Class F
Class G:
Class G
Class H:
Protocol A
Class I:
Class I
Class J:
Protocol C
Class K:
Class K
Class L:
Class L
Class M:
Class M
Class N:
Class N
Class O:
Class O
Class P:
Class P
Class Q:
Class Q
Class R:
Class R
2) printValueFromProtocolA(_: PA):
Class A:
Protocol A
Class B:
Protocol A
Class C:
Protocol A
Class D:
Class D
Class E:
Class E
Class F:
Protocol A
Class G:
Protocol A
Class H:
Protocol A
Class I:
Protocol A
Class J:
Protocol C
Class K:
Protocol C
Class L:
Protocol C
Class M:
Protocol A
Class N:
Protocol A
Class O:
Protocol A
Class P:
Protocol C
Class Q:
Class Q
Class R:
Class R
3) printValueFromProtocolB(_: PB):
Class D:
Class D
Class E:
Class E
Class H:
Protocol A
Class I:
Protocol A
Class J:
Protocol C
Class K:
Protocol C
Class L:
Protocol C
Class N:
Protocol A
Class O:
Protocol A
Class P:
Protocol C
Class Q:
Class Q
Class R:
Class R
4) printValueFromProtocolC(_: PC):
Class J:
Protocol C
Class K:
Protocol C
Class L:
Protocol C
Class P:
Protocol C
Class Q:
Class Q
Class R:
Class R
5) printValueFromClassD(_: D):
Class D:
Class D
Class E:
Class E
6) printValueFromClassA(_: A):
Class A:
Protocol A
Class B:
Protocol A
Class C:
Protocol A
Class G:
Protocol A
7) printValueFromClassH(_: H):
Class H:
Protocol A
Class I:
Protocol A
Class N:
Protocol A
Class O:
Protocol A
8) printValueFromClassJ(_: J):
Class J:
Protocol C
Class K:
Protocol C
Class L:
Protocol C
Class P:
Protocol C
要覆盖需要实现该方法的方法,因此如果您尝试在 B 中使用 override
关键字,则会出现编译错误,因为虽然 A 符合 PA,但它不会实现其中的功能PA 而是使用默认值。
所以这很可能是造成混淆的原因,因为 A 尚未实现 printValue
即使可以用它调用类型 A 的对象,它也不能被覆盖,a.printValue()
,因为它依赖于协议的默认实现。
鉴于此,我认为大多数测试用例都可以理解为什么它们的行为如此,尽管我只关注案例 6。
在 A 中执行 printValue()
,看看会发生什么。您首先遇到需要修复的编译错误,但之后输出将是您所期望的。
关于案例 6 的打印输出,由于 A 没有覆盖任何内容,因此发送到 printValueFromClassA
的所有对象都将被视为 A 的实例,并将使用协议默认实现。
好的。我将离开 Joakim 的 greencheck,但我现在可以更明确地说明这一点。
这是 Swift 中的一个 "sorta-kinda" 错误。
基本上,如果您在基于具有默认实现的协议的 class 层次结构中覆盖 method/property,则 class 层次结构的 vtable 不存在直到您在没有默认实现的情况下访问该实体的级别。
我知道这没有意义,恐怕我的解释对于这个场地来说可能太长和涉及,但我描述得很详细,here。
我确实有一个更简单的(比上面的)要点来展示它,here。
我将从那个要点粘贴操场,但它有点长:
// This is the little demo, at the start.
protocol A {
var thisIsRequired: String { get }
var thisIsOptional: String { get }
}
extension A {
var thisIsOptional: String { get { "" } set {} }
}
struct conformsToA: A {
var thisIsRequired: String { "Struct conformsToA" }
}
// In the first protocol, we don't use a default implementation (required conformance).
protocol PA {
var myName: String { get }
}
struct SA: PA {
var myName: String = "Struct A"
}
class CA: PA {
var myName: String = "Class A"
}
class CB: CA {
override var myName: String { get { "Class B" } set { } }
}
// In the second protocol, we add a default implementation (optional conformance).
protocol PB: PA { }
extension PB {
var myName: String { "Protocol B" }
}
// This will "fall back" to the default implementation.
struct SB: PB { }
struct SC: PB {
var myName: String = "Struct C"
}
// This will "fall back" to the default implementation.
class CC: PB { }
class CD: CC {
var myName: String = "Class D"
}
class CE: CD {
override var myName: String { get { "Class E" } set { } }
}
class CF: PB {
var myName: String = "Class F"
}
class CG: CF {
override var myName: String { get { "Class G" } set { } }
}
let structInstanceSA = SA()
let classInstanceCA = CA()
let classInstanceCB = CB()
// Part one is all about the required conformance protocol.
print("PART ONE")
print("\tDirect Print:")
print("\t\tStruct A: \(structInstanceSA.myName)")
print("\t\tClass A: \(classInstanceCA.myName)")
print("\t\tClass B: \(classInstanceCB.myName)")
func printAsProtoclA(_ inItem: PA) -> String { inItem.myName }
print("\tprintAsProtoclA(_: PA):")
print("\t\tStruct A: \(printAsProtoclA(structInstanceSA))")
print("\t\tClass A: \(printAsProtoclA(classInstanceCA))")
print("\t\tClass B: \(printAsProtoclA(classInstanceCB))")
func printAsStructA(_ inItem: SA) -> String { inItem.myName }
print("\tprintAsStructA(_: SA):")
print("\t\tStruct A: \(printAsStructA(structInstanceSA))")
func printAsClassA(_ inItem: CA) -> String { inItem.myName }
print("\tprintAsClassA(_: SA):")
print("\t\tClass A: \(printAsClassA(classInstanceCA))")
print("\t\tClass B: \(printAsClassA(classInstanceCB))")
let structInstanceSB = SB()
let structInstanceSC = SC()
let classInstanceCC = CC()
let classInstanceCD = CD()
let classInstanceCE = CE()
let classInstanceCF = CF()
let classInstanceCG = CG()
// Part two is where things get interesting.
print("PART TWO")
print("\tDirect Print:")
print("\t\tStruct B: \(structInstanceSB.myName)")
print("\t\tStruct C: \(structInstanceSC.myName)")
print("\t\tClass C: \(classInstanceCC.myName)")
print("\t\tClass D: \(classInstanceCD.myName)")
print("\t\tClass E: \(classInstanceCE.myName)")
print("\t\tClass F: \(classInstanceCF.myName)")
print("\t\tClass G: \(classInstanceCG.myName)")
func printAsProtoclB(_ inItem: PB) -> String { inItem.myName }
print("\tprintAsProtoclA(_: PA):")
print("\t\tStruct B: \(printAsProtoclA(structInstanceSB))")
print("\t\tStruct C: \(printAsProtoclA(structInstanceSC))")
print("\t\tClass C: \(printAsProtoclA(classInstanceCC))")
print("\t\tClass D: \(printAsProtoclA(classInstanceCD))")
print("\t\tClass E: \(printAsProtoclA(classInstanceCE))")
print("\t\tClass F: \(printAsProtoclA(classInstanceCF))")
print("\t\tClass G: \(printAsProtoclA(classInstanceCG))")
print("\tprintAsProtoclB(_: PB):")
print("\t\tStruct B: \(printAsProtoclA(structInstanceSB))")
print("\t\tStruct C: \(printAsProtoclA(structInstanceSC))")
print("\t\tClass C: \(printAsProtoclA(classInstanceCC))")
print("\t\tClass D: \(printAsProtoclA(classInstanceCD))")
print("\t\tClass E: \(printAsProtoclA(classInstanceCE))")
print("\t\tClass F: \(printAsProtoclA(classInstanceCF))")
print("\t\tClass G: \(printAsProtoclA(classInstanceCG))")
func printAsStructB(_ inItem: SB) -> String { inItem.myName }
print("\tprintAsStructB(_: SB):")
print("\t\tStruct B: \(printAsStructB(structInstanceSB))")
func printAsStructC(_ inItem: SC) -> String { inItem.myName }
print("\tprintAsStructC(_: SC):")
print("\t\tStruct C: \(printAsStructC(structInstanceSC))")
func printAsClassC(_ inItem: CC) -> String { inItem.myName }
print("\tprintAsClassC(_: CC):")
print("\t\tClass C: \(printAsClassC(classInstanceCC))")
print("\t\tClass D: \(printAsClassC(classInstanceCD))")
print("\t\tClass E: \(printAsClassC(classInstanceCE))")
func printAsClassD(_ inItem: CD) -> String { inItem.myName }
print("\tprintAsClassD(_: CD):")
print("\t\tClass D: \(printAsClassD(classInstanceCD))")
print("\t\tClass E: \(printAsClassD(classInstanceCE))")
func printAsClassF(_ inItem: CF) -> String { inItem.myName }
print("\tprintAsClassF(_: SF):")
print("\t\tClass F: \(printAsClassF(classInstanceCF))")
print("\t\tClass G: \(printAsClassF(classInstanceCG))")
// In a perfect world, this would be a syntax error:
let name = classInstanceCC.myName
这是上面的 UML: