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

Swift协议扩展对匹配类无预期影响的原因探究

Swift协议扩展对匹配类无预期影响的原因探究

嗨,这个问题其实戳中了Swift协议扩展里方法调度的核心逻辑,我给你一步步拆解明白~

先把你的代码贴出来方便对照:

import Foundation

protocol SampleProtocol {
    func foo()
}

extension SampleProtocol {
    func foo() {
        print("protocol foo")
    }
    func bar() {
        print("protocol bar")
    }
}

class SampleClass: SampleProtocol {
    func foo() {
        print("class foo")
    }
    func bar() {
        print("class bar")
    }
}

let sample: SampleProtocol = SampleClass()
sample.foo()
sample.bar()

输出结果:
class foo
protocol bar

问题的关键在于协议声明的方法和协议扩展新增方法的调度机制完全不同:

  • 协议里明确写出来的方法(比如这里的foo())属于「协议要求的方法」,Swift对这类方法采用动态调度——运行时会根据变量指向的实际实例类型(也就是SampleClass)来决定调用哪个实现,所以sample.foo()会触发类里的重写版本,输出"class foo"。
  • 而协议扩展里新增的、没有在协议本身声明的方法(比如bar()),属于「扩展默认实现」,这类方法是静态调度——编译时就直接绑定到扩展里的实现,不管变量背后的实际实例是什么类型。哪怕你的SampleClass里写了bar(),对于类型为SampleProtocol的sample来说,编译器根本看不到类里的这个方法,只会调用扩展里的版本,所以输出"protocol bar"。

你之所以预期输出"class bar",是误以为类里的bar()会覆盖扩展的实现,但实际上只有协议要求的方法才支持这种“重写+动态调度”的逻辑。如果想要达到你的预期,有两个简单的办法:

  • 把bar()也加到SampleProtocol的声明中,让它成为协议要求的方法:
    protocol SampleProtocol {
        func foo()
        func bar() // 新增这一行
    }
    
    这样bar()就会走动态调度,运行时会调用SampleClass里的实现。
  • 或者把sample的类型改成SampleClass,让编译时直接指向类的实现:
    let sample: SampleClass = SampleClass()
    

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 14:00:27