Swift中遵循继承协议的子类为何出现协议属性与方法默认实现不一致的异常?
Let's break down exactly why you're seeing that confusing output when calling doSomething() on your LowerClass instance. It all boils down to how Swift handles static vs dynamic dispatch for protocol extensions, especially when combined with class inheritance.
First, Let's Recap the Behavior
lowerClass.levelreturns1(correct, matchesLowerProtocol's default)lowerClass.doSomething()outputs"Lower level is 10"(mix ofLowerProtocol's method text andHigherProtocol's level value)
Why This Happens
1. How Property Access Works Here
When you directly access lowerClass.level, the compiler knows the static type of lowerClass is LowerClass, which conforms to LowerProtocol. Since LowerProtocol provides a more specific default for level than its parent HigherProtocol, the compiler statically binds this access to LowerProtocol's extension implementation, giving you 1 as expected.
2. How Method & Property Interaction Works in Protocol Extensions
The weirdness comes from the doSomething() method call:
- Since neither
LowerClassnor its parentHigherClassimplementdoSomething()themselves, the compiler statically binds the call toLowerProtocol's extension method (hence the "Lower level is" prefix). - Inside that method, when accessing
level,selfis treated as theLowerProtocoltype (a protocol type, not a concrete class). For protocol types, property access uses dynamic dispatch. - Here's the catch:
LowerClassdoesn't have its own implementation oflevel—it's just using the default fromLowerProtocol's extension. Protocol extension defaults don't get added to a class's vtable (the table used for dynamic dispatch). So when dynamically dispatchinglevel, Swift falls back to the parent classHigherClass's implementation oflevel, which comes fromHigherProtocol's extension (returning10).
Fixing the Issue
To get the expected behavior, you need to explicitly implement level in LowerClass instead of relying solely on the protocol extension default. This adds the property to the class's vtable, ensuring dynamic dispatch works as intended:
class LowerClass: HigherClass, LowerProtocol { var level: Int { 1 } }
Now, when doSomething() accesses level, dynamic dispatch will find LowerClass's implementation, and you'll get the output "Lower level is 1" as expected.
内容的提问来源于stack exchange,提问作者Kramer

