You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的行为逻辑

  1. t在协议A的本体中被明确声明为var t : Int {get},属于协议要求成员,走动态派发逻辑。
  2. 结构体B遵守协议A时,自己实现了存储属性t = 2,覆盖了协议扩展提供的默认返回1的实现。
  3. 当静态类型为A的实例a调用t时,动态派发查到实际类型是B,因此调用B的t实现返回2;当静态类型为B的实例b调用t时,直接使用B自身的存储属性返回2,因此两处t的输出都是2。

属性tt的行为逻辑

  1. tt从未在协议A的本体中声明,仅在协议扩展中定义,不属于协议要求成员,走静态派发逻辑。
  2. 结构体B中定义的tt = 2是B类型自身的成员,并不是对协议要求的实现——因为协议本身根本没有要求tt这个成员。
  3. 当静态类型为A的实例a调用tt时,编译器仅能看到协议扩展中定义的tt实现,因此静态绑定到扩展实现返回1;当静态类型为B的实例b调用tt时,编译器能看到B自身定义的tt存储属性,因此直接返回2,最终出现两处tt输出不一致的结果。

补充:哪怕扩展成员和遵守类型中的成员签名完全一致,只要该成员没有被写入协议本体的要求列表,协议类型的调用就永远不会动态派发到遵守类型的自定义实现,这是Swift的明确语言设计,不是非预期bug。

内容的提问来源于stack exchange,提问作者gloo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 20:06:40