Swift中-dead_strip选项无法移除未使用类的问题咨询
关于Swift静态库-dead_strip选项无法移除未使用类的问题
场景复现
我创建了一个仅含单个源文件的简单Swift静态库,用于测试-dead_strip选项,代码如下:
public class UsedClass { public init() {} public func function() { print("print") } } public class PublicClas { func private_func() { print("print") } } class SimpleClass { func private_func() { print("print") } } public struct PublicStruct { func private_func() { print("print") } } struct SimpleStruct { func private_func() { print("print") } }
同时创建了一个仅依赖该静态库中UsedClass类的动态库,代码如下:
public class MyClass { public func callModule() { let qq = UsedClass() qq.function() } }
使用-dead_strip选项链接动态库后,执行命令nm -m test_lib | grep SimpleClass,发现二进制中仍保留SimpleClass的相关符号,输出如下:
0000000100003d54 (__TEXT,__const) non-external (was a private external) _$s13TestFramework11SimpleClassC11public_funcyyFTq 0000000100003d5c (__TEXT,__const) non-external (was a private external) _$s13TestFramework11SimpleClassC12private_funcyyFTq 0000000100003d64 (__TEXT,__const) non-external (was a private external) _$s13TestFramework11SimpleClassCACycfCTq 0000000100003e58 (__TEXT,__swift5_fieldmd) non-external _$s13TestFramework11SimpleClassCMF 0000000100003a38 (__TEXT,__text) non-external (was a private external) _$s13TestFramework11SimpleClassCMa 00000001000083e0 (__DATA,__data) non-external _$s13TestFramework11SimpleClassCMf 00000001000083b8 (__DATA,__data) non-external (was a private external) _$s13TestFramework11SimpleClassCMm 0000000100003d20 (__TEXT,__const) non-external (was a private external) _$s13TestFramework11SimpleClassCMn 00000001000083f0 (__DATA,__data) non-external (was a private external) _$s13TestFramework11SimpleClassCN 0000000100003a6c (__TEXT,__text) non-external (was a private external) _$s13TestFramework11SimpleClassCfD 0000000100008198 (__DATA,__objc_const) non-external __DATA__TtC13TestFramework11SimpleClass 0000000100008150 (__DATA,__objc_const) non-external __METACLASS_DATA__TtC13TestFramework11SimpleClass 0000000100003e24 (__TEXT,__swift5_typeref) non-external (was a private external) _symbolic ______ 13TestFramework11SimpleClassC
我已移除-ObjC选项,并尝试添加-internalize-at-link选项,但结果一致,仅私有结构体被移除。已知Apple静态库与Unix静态库存在差异,但不清楚具体区别,且认为该场景下常规C++链接器可移除未使用类,特此咨询原因及解决办法。
原因分析
- 链接粒度与元数据依赖:Unix静态库通常以单个目标文件(.o)为单位进行链接,若某个.o文件无引用则直接丢弃;但Swift静态库中,所有类都放在同一个源文件时,编译器会生成包含所有类型元数据的单个.o文件,链接器无法拆分其中的单个类符号进行移除——因为元数据、类型引用链是绑定在一起的,链接器无法判定
SimpleClass的元数据完全没有被潜在引用。 - Swift与ObjC的兼容机制:即使是纯Swift类,编译时也会生成类似ObjC的类结构数据(如输出中的
__DATA__TtC开头符号),这些数据会被Swift运行时的类型系统所依赖,链接器默认会保留这类元数据,避免运行时崩溃。 -dead_strip的局限性:该选项主要针对显式未被引用的函数和数据,但Swift的类型信息、虚表等属于隐式依赖范畴,链接器无法精准判断这些是否属于“无用代码”,因此不会主动移除。
解决办法
- 拆分源文件:将每个类单独放在一个.swift文件中,这样编译后每个类对应一个独立的.o文件。链接时,若某个.o文件完全无引用,会被
-dead_strip直接排除。 - 标记内部私有符号:使用Swift的
@_spi(Private)标记未对外暴露的类,明确告知编译器该类仅内部使用,链接器可安全移除未被引用的相关符号:@_spi(Private) class SimpleClass { func private_func() { print("print") } } - 开启全模块优化:编译静态库时添加
-whole-module-optimization选项,编译器会进行全局代码分析,标记真正未被使用的符号,帮助链接器更精准地执行dead strip。 - 手动移除符号:使用
-dead_strip_symbols选项配合符号列表文件,手动指定需要移除的SimpleClass相关符号,不过这种方式维护成本较高,仅适合特殊场景。
内容的提问来源于stack exchange,提问作者Fesss
相关产品推荐
相关产品推荐

