Swift协议扩展t与tt属性调用差异原因咨询
Swift协议扩展成员差异化行为的规则解释
你观察到的t和tt调用结果差异,核心来自Swift对协议成员的两套派发逻辑,分界点就是成员是否在协议本体中被明确声明为协议要求,和你之前的判断一致。
两类协议成员的派发规则
- 协议本体明确声明的要求成员:采用动态派发。当调用方的静态类型是协议类型时,运行时会优先查找实际实例类型对该成员的实现,协议扩展中提供的对应实现仅作为默认兜底,不会强制优先使用。
- 仅在协议扩展中定义、未纳入协议本体声明的成员:采用静态派发。调用时直接使用调用方静态类型对应的实现,不会在运行时动态查找实际实例类型的同名实现。
对应示例代码的逐行逻辑
示例代码复现:
protocol A { var t : Int {get} } extension A { var t : Int { return 1 } var tt : Int { return 1 } } struct B : A { var t = 2 var tt = 2 } let a:A = B() print(a.t, a.tt) // 输出 2 1 let b:B = B() print(b.t, b.tt) // 输出 2 2
属性t的行为逻辑
t在协议A的本体中被明确声明为var t : Int {get},属于协议要求成员,走动态派发逻辑。- 结构体B遵守协议A时,自己实现了存储属性
t = 2,覆盖了协议扩展提供的默认返回1的实现。 - 当静态类型为A的实例
a调用t时,动态派发查到实际类型是B,因此调用B的t实现返回2;当静态类型为B的实例b调用t时,直接使用B自身的存储属性返回2,因此两处t的输出都是2。
属性tt的行为逻辑
tt从未在协议A的本体中声明,仅在协议扩展中定义,不属于协议要求成员,走静态派发逻辑。- 结构体B中定义的
tt = 2是B类型自身的成员,并不是对协议要求的实现——因为协议本身根本没有要求tt这个成员。 - 当静态类型为A的实例
a调用tt时,编译器仅能看到协议扩展中定义的tt实现,因此静态绑定到扩展实现返回1;当静态类型为B的实例b调用tt时,编译器能看到B自身定义的tt存储属性,因此直接返回2,最终出现两处tt输出不一致的结果。
补充:哪怕扩展成员和遵守类型中的成员签名完全一致,只要该成员没有被写入协议本体的要求列表,协议类型的调用就永远不会动态派发到遵守类型的自定义实现,这是Swift的明确语言设计,不是非预期bug。
内容的提问来源于stack exchange,提问作者gloo
相关产品推荐
相关产品推荐

