安全地混合 C、C++ 和 Swift
Safely mix C, C++, and Swift
2025年6月9日
一句话判断
Swift 终于给 C/C++ 指针互操作提供了「安全逃生通道」——通过注解让编译器帮你做 bounds 和 lifetime 检查,而不是靠人肉审计。
这场 Session 讲了什么
如果你的项目里有 C/C++ 遗留代码或者依赖了第三方 C 库,那你一定对 UnsafeMutablePointer 不陌生。Swift 是安全的语言,但一旦跨进 C/C++ 的领地,buffer overflow 和 use-after-free 就像地雷一样埋着。
这次 Swift 6.2 带来了两件事:
Strict Memory Safety 模式:在 Xcode build settings 里打开后,编译器会对所有 unsafe 构造发出警告,帮你定位每一个需要处理的 C/C++ 调用点。
一套 C/C++ 注解体系,让 Swift 编译器能把 UnsafePointer 自动桥接成安全的 Span 类型:
counted_by:告诉编译器指针指向的 buffer 有多大(解决 bounds 问题)noescape:声明参数不会在函数返回后被持有(解决 lifetime 问题)lifetimebound:声明返回值的生命周期依赖于某个参数SWIFT_NONESCAPABLE:让自定义 C++ view 类型被导入为 Swift 的 non-escapable 类型SWIFT_SHARED_REFERENCE:让引用计数的 C++ 类型自动被 Swift 管理
值得深挖的点
-
Span 是核心概念。Swift 6.2 的
Span和MutableSpan是新的安全指针类型,它们通过 non-escapable 语言特性保证不会逃逸出作用域,从根本上杜绝 use-after-free。C/C++ 指针通过注解后,能被自动导入为 Span,调用侧零 boilerplate。 -
C++ Span 不等于 Swift Span。C++ 的
std::span有 bounds 信息但没有 lifetime 保证。Swift 通过noescape注解让 C++ Span 也能安全导入,这比你想象的更有价值——因为你不需要把 C++ Span 拆成 raw pointer 再传。 -
C 语言的 Bounds Safety 扩展。Xcode 新增了一个 C 语言扩展,开启后编译器会要求你在 C 代码里也加上 bounds 注解,并在运行时插入检查。这是 Apple 在 C 安全性上迈出的实质性一步。
代码片段
安全调用带指针参数的 C 函数——加注解后 Swift 侧直接传 Span:
// C 侧:添加 counted_by 和 noescape 注解
void invertImage(
uint8_t *_Nullable counted_by(imageSize) noescape imagePointer,
int imageSize
);
// Swift 侧:直接传 Span,无 unsafe 代码
let imageData: Span<UInt8> = image.bytes
invertImage(imagePointer: imageData, imageSize: imageData.count)
自定义 C++ 类型导入为 non-escapable:
struct __attribute__((swift_attr("@nonescapable"))) ImageView {
int width;
int height;
const uint8_t* data; // 不拥有的内存
};
最佳实践
- 立即开启 Strict Memory Safety,特别是安全敏感的应用。它不会阻断编译,只发警告,但能帮你系统性地扫出所有 unsafe 调用。
- 先注解声明再注解定义,头文件和实现文件都要加注解,否则 Swift 侧无法正确导入。
- 从 leaf functions 开始注解,也就是那些不调用其他 C/C++ 函数的最底层函数,然后逐步往上推。
- C++ 项目开启 Standard Library Hardening,在 build settings 里设置
Enforce Bounds-Safe Buffer Usage为 Yes,能同时开启 bounds 检查和 raw pointer 使用报错。
还有什么值得关注
- Swift 的
Span类型族(Span、MutableSpan、RawSpan、UTF8Span)是 6.2 的基础设施级更新,值得单独深入了解。 SWIFT_RETURNS_RETAINED和SWIFT_RETURNS_UNRETAINED注解解决了 C++ 函数返回引用计数类型时的 ownership 语义问题,对有自定义 C++ 容器的项目非常有用。- Apple 明确说 “it is never possible to make C/C++ as safe as Swift”,但他们给的工具已经能把差距缩小到可接受的范围。
- 参考 session 312(Improve memory usage and performance with Swift)了解 Span 在性能优化中的应用。