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

Swift编译器无法移除未使用公共代码?如何实现移除?

关于Swift SPM模块中未使用公共代码的推断验证与移除方案

你的推断是否正确?

你的推断部分正确,但需要补充关键细节:

  • Swift编译器的全模块优化(WMO)和死代码剥离确实会安全移除未使用的internal符号:因为internal作用域仅限模块内部,编译器可以通过全模块分析确定符号未被任何代码引用,因此会在编译或链接阶段剥离。
  • 对于public符号,编译器默认会保守处理:public符号被设计为模块对外暴露的API,编译器无法确定是否会被当前依赖关系之外的代码(比如动态调用、未来新增的依赖模块)使用,因此即使当前依赖模块未用到,也不会轻易移除——这就是你观察到public未使用函数仍占用二进制空间的原因。

如何让编译器移除未使用的公共代码?

可以通过以下几种方案实现,根据你的场景选择最合适的方式:

1. 使用@_spi标记非对外API的公共函数

如果该public函数只是模块间内部协作使用,并非对外公开的正式API,可以用@_spi标记,将其标记为“SPI(Stable Public Interface)”符号,编译器会将其视为半内部符号,在全模块优化下可以被安全剥离:

@_spi(Internal)
public func unusedPublicFunction() {
    // 函数实现
}

注意:@_spi是Swift的非官方特性,虽然在SPM场景中广泛使用,但未来版本可能有变化,不建议用于对外的正式SDK。

2. 确保全模块优化与死代码剥离已启用

需要同时开启两个关键编译/链接选项,且确保依赖目标为静态框架(动态框架的public符号会被保留供动态链接):

  • 在Xcode中,对两个目标分别设置:
    • Swift Compiler - Code Generation -> Compilation Mode:设为Whole Module
    • Linking -> Dead Code Stripping:设为Yes
  • 如果使用SPM,可以在Package.swift中配置:
    targets: [
        .target(
            name: "YourDependencyTarget",
            swiftSettings: [
                .unsafeFlags(["-whole-module-optimization"])
            ],
            linkerSettings: [
                .linkedFlag("-dead_strip")
            ]
        ),
        .target(
            name: "YourMainTarget",
            dependencies: ["YourDependencyTarget"],
            swiftSettings: [
                .unsafeFlags(["-whole-module-optimization"])
            ]
        )
    ]
    

3. 谨慎使用@inlinable标记小函数

如果未使用的public函数逻辑简单、体积小,可以标记为@inlinable:

@inlinable
public func unusedPublicFunction() {
    // 简单实现
}

这样主模块编译时会将函数体内联到调用处,依赖模块中的原函数因无其他引用会被剥离。但注意@inlinable会将函数体暴露给调用模块,可能破坏ABI稳定性,仅适合无状态的工具类小函数。

4. 拆分模块隔离非公开代码

将不需要对外暴露的代码拆分到一个internal子模块中,仅保留真正需要对外的API在主公共模块。这样子模块中的internal符号可以被正常剥离,主模块的public API也保持简洁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 16:33:15