背景

Flutter 项目中集成了 tencent_rtc_sdk 实现语音/视频通话功能。Debug 真机调试一切正常,但打出 Release 包(TestFlight / 蒲公英 IPA)安装到真机后,发起通话直接崩溃,日志里出现:

1
2
symbol not found: Dart_InitApiDL
symbol not found: <TUICallEngine callback bridge>

本文记录这个问题的根因和修复方案。

原因

tencent_rtc_sdk 通过 Flutter FFI(Foreign Function Interface)调用底层 C/C++ 接口,核心机制是:

1
2
3
// Dart 端通过 DynamicLibrary 在运行时查找 native 符号
final lib = DynamicLibrary.process();
final func = lib.lookupFunction<...>('Dart_InitApiDL');

它依赖系统 dlsym(RTLD_DEFAULT, ...) 在运行时动态解析 C 符号。

Debug 构建中,Xcode 不会进行符号裁剪,所有符号完整保留,所以一切正常。

Release 构建时,Xcode 的链接优化会:

  1. 符号裁剪(Strip) — 移除未被直接引用的符号,减小包体积
  2. 死代码消除(Dead Code Stripping) — 移除未显式调用的代码段

由于 FFI 调用的符号在编译期「看起来」没有被引用(它们是通过字符串在运行时查找的),链接器误将它们判定为无用符号并删除,导致 dlsym 解析失败,引发 symbol not found 崩溃。

1
2
3
4
5
6
7
┌────────────────────────────────────────────────────────┐
│ 编译期(Release): │
│ 链接器扫描所有符号引用 → FFI 符号「无人引用」→ 删除 │
│ │
│ 运行时: │
│ dlsym("Dart_InitApiDL") → 找不到 → symbol not found │
└────────────────────────────────────────────────────────┘

解决方案

在 Xcode 的 Build Settings 中调整两项配置,让 Release 构建保留 FFI 所需的符号。

步骤一:开启 Deployment Postprocessing

在项目的 Build Settings 中搜索 Deployment Postprocessing,将 Release 的值设为 Yes

Deployment Postprocessing

这个选项控制是否在构建完成后进行符号处理。设为 Yes 后,后续的 Strip Style 设置才会生效。

步骤二:修改 Strip Style

Build Settings 中搜索 Strip Style,将 Release 的值设为 Non-Global Symbols

Strip Style

Strip Style 选项 效果
All Symbols 移除全部符号,FFI 必然失败
Non-Global Symbols(推荐) 只移除本地符号,保留全局符号(dlsym 需要的都在全局表中)
Debugging Symbols 仅移除调试符号

备选方案:链接器 Flag(已兜底)

项目中也配置了 -Wl,-export_dynamic 链接器 flag,作为兜底策略。在 ios/Flutter/Release.xcconfig 中:

1
OTHER_LDFLAGS = $(inherited) -Wl,-export_dynamic

-export_dynamic 告诉链接器将所有全局符号标记为可导出,确保 dlsym 能找到它们。

推荐两者都配上:Strip Style = Non-Global Symbols 控制 Xcode 的符号处理行为,-export_dynamic 控制链接器的导出策略,双重保障。

配置汇总

配置项 位置 Release 值 作用
Deployment Postprocessing Build Settings Yes 启用符号处理
Strip Style Build Settings Non-Global Symbols 保留全局符号
OTHER_LDFLAGS Release.xcconfig -Wl,-export_dynamic 强制导出符号

配置完成后,重新 Archive → 导出 IPA → 真机安装测试,通话功能恢复正常。

验证

  1. Release 配置打包(Archive → Distribute)
  2. 安装到真机(不要用 Debug 包替代验证)
  3. 进入通话流程,发起呼叫
  4. 如果仍有问题,在 Xcode Console 中过滤 dlsymDynamicLibrarysymbol not found 关键词排查

延展:哪些场景会遇到这个问题?

不只是 tencent_rtc_sdk,任何通过 Dart FFI 调用 C 库的依赖都可能命中这个坑,比如:

  • sqlite3 / sqlite3_flutter_libs
  • ffi + 自定义 .a / .framework
  • 其他使用 DynamicLibrary.process() 的三方 SDK

如果你的 Release 包出现莫名其妙的 symbol not found,第一条排查思路就是:Xcode 是不是把你的 FFI 符号给 Strip 掉了

参考