Safely mix C, C++, and Swift
Swift 进阶 2m

安全地混合 C、C++ 和 Swift

Safely mix C, C++, and Swift

2025年6月9日

在 Apple 官方观看视频

一句话判断

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 管理

值得深挖的点

  1. Span 是核心概念。Swift 6.2 的 SpanMutableSpan 是新的安全指针类型,它们通过 non-escapable 语言特性保证不会逃逸出作用域,从根本上杜绝 use-after-free。C/C++ 指针通过注解后,能被自动导入为 Span,调用侧零 boilerplate。

  2. C++ Span 不等于 Swift Span。C++ 的 std::span 有 bounds 信息但没有 lifetime 保证。Swift 通过 noescape 注解让 C++ Span 也能安全导入,这比你想象的更有价值——因为你不需要把 C++ Span 拆成 raw pointer 再传。

  3. 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 类型族(SpanMutableSpanRawSpanUTF8Span)是 6.2 的基础设施级更新,值得单独深入了解。
  • SWIFT_RETURNS_RETAINEDSWIFT_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 在性能优化中的应用。
Swift