iOS 用cjia

在 iOS / SwiftUI 里使用 C++

一个可以直接打开、编译、运行的完整示例(Xcode 26.2 / Swift 6.2.3 验证通过)。

运行截图

核心结论

Swift 不能直接调用 C++。 中间必须有 Objective-C++ 这一层。

三种语言在这个工程里的分工:

语言文件后缀角色
C++ .cpp / .hpp 纯业务核心,不认识 Apple 的任何东西,可跨平台复用
Objective-C++ .mm 桥接层,唯一的"两栖"文件,既 #include C++ 头,也写 @implementation
Swift .swift UI 层,只跟 Objective-C 类打交道,完全不知道背后是 C++
代码块
 
SwiftUI (ContentView.swift)
      │  把 CppBridge 当成普通 ObjC 类调用
      ▼
CppBridge.h        ← 纯 Objective-C 头,通过 Bridging Header 暴露给 Swift
      │
CppBridge.mm       ← ★ 承重墙:Objective-C++,唯一能碰 C++ 的地方
      │  #include "MathCore.hpp"
      ▼
MathCore.hpp/.cpp  ← 纯 C++,零 Apple 依赖,可原样搬到 Android / 服务端

文件清单

代码块
 
HelloCpp/
├── HelloCpp.xcodeproj
└── HelloCpp/
    ├── HelloCppApp.swift              @main
    ├── ContentView.swift              SwiftUI 界面,调用 CppBridge
    ├── HelloCpp-Bridging-Header.h     Swift ↔ ObjC 的通道
    └── Cpp/
        ├── MathCore.hpp               纯 C++ 接口
        ├── MathCore.cpp               纯 C++ 实现
        ├── CppBridge.h                ObjC 门面(Swift 唯一看得见的头)
        └── CppBridge.mm               ObjC++ 实现(唯一 include C++ 头的文件)

在你自己的 Xcode 工程里怎么加

假设你的工程叫 MyApp。

1. 建桥接头文件

File > New > File… > Header File,命名 MyApp-Bridging-Header.h,放在 MyApp/ 目录里。

内容只需要 #import 你想暴露给 Swift 的 Objective-C 头:

代码块
 
#import "Cpp/CppBridge.h"

2. 告诉 Xcode 这个文件在哪

选中 target → Build Settings → 搜 bridging → Objective-C Bridging Header 填:

代码块
 
MyApp/MyApp-Bridging-Header.h

路径相对于工程根目录(.xcodeproj 所在目录)。 小技巧:新建第一个 C/C++/ObjC 文件时 Xcode 会主动问你要不要自动创建并在设置里填好。

3. 加 C++ 文件

File > New > File… > C++ File,命名 MathCore,会同时生成 .hpp 和 .cpp。 不要勾选 "Also create a header file" 之外的任何 target 相关选项,加完确认它出现在 target → Build Phases > Compile Sources 里。

4. 加桥接文件

  • File > New > File… > Header File → CppBridge.h(纯 Objective-C,只 #import <Foundation/Foundation.h>,绝不 include C++ 头)
  • File > New > File… > Objective-C File → 随便选个类型 → 建完把后缀 .m 手动改成 .mm

或者更省事:直接新建一个空文件,命名 CppBridge.mm。

.mm 后缀是关键——它让 clang 同时开启 Objective-C 和 C++ 两套前端。

5. 设置 C++ 标准(可选但推荐)

target → Build Settings → 搜 C++ Language Dialect → 选 GNU++20 或 C++20。 (本示例工程已在 project 级设成 gnu++20。)

6. 编译

Swift 里直接 CppBridge().increment() 就能用了。 CppBridge.h 里 #import 的所有内容,Swift 自动可见,不需要写 import。

类型映射速查

C++桥接层转换Swift
int NSInteger Int
float / double float / double Float / Double
bool BOOL (注意 YES/NO) Bool
std::string [NSString stringWithUTF8String:s.c_str()] String
std::vector<T> NSArray<NSNumber *> [T]
std::map<K,V> NSDictionary [K: V]
class / struct 作为 ObjC 类的 ivar 持有 只能通过方法访问
std::optional<T> NSNumber *(nil 表示空) T?

踩坑清单

  1. <...> 语法错误 / "Unknown type name 'namespace'" 你在 .h 或 Bridging Header 里 #include 了 C++ 头。C++ 头只能出现在 .mm 里。

  2. Swift 侧方法名跟你写的不一样 Swift 会按 "import as member" 规则重命名 ObjC 方法。本工程实测: + (NSArray *)primesUpTo:(NSInteger)limit 被改成了 primesUp(to:)。 解决办法是显式钉名字:

    代码块
     
    + (NSArray<NSNumber *> *)primesUpTo:(NSInteger)limit NS_SWIFT_NAME(primesUpTo(_:));
  3. 忘改 .m 为 .mm 报 "Use of undeclared identifier 'mathcore'"。后缀决定语言模式,不是内容决定的。

  4. C++ 异常穿过 ObjC 边界会崩 -fobjc-arc 下 ObjC 消息发送和 C++ 异常不能混用。在 .mm 里全部 try/catch 兜住, 转成 NSError ** 返回给 Swift。反过来 Swift 的 throws 也不能抛进 C++。

  5. 不要用 std::function 接 Swift 闭包 ObjC block 在 C++ 容器里不会被 ARC 正确 retain/release。回调要在 .mm 里 把 block 转成 C 函数指针 + void *context 的 trampoline,或者干脆把循环写在 .mm 里。

  6. C++ 对象当 ivar 必须写在 @implementation 里 直接写在 @interface 的 {} 里会报不完整类型。

  7. .hpp 别被加进 Compile Sources 头文件不参与编译。如果 Xcode 误加了,手动从 Build Phases > Compile Sources 删掉。

附:另外两条路

方案 B — Swift/C++ 直接互操作(Xcode 15+,实验性)

target → Build Settings → 搜 C++ and Objective-C Interoperability → 选 C++ / Objective-C++。 然后把 C++ 头 #include 进 Bridging Header,Swift 就能直接调 C++ 的类和方法, 省掉手写桥接。适合 C++ 接口很大的场景。

代价:std::string、模板、多重继承等支持仍不完整,且会污染 Swift 侧的 API 形态; 第三方 C++ 库的宏和平台头文件经常会炸。生产项目建议还是用方案 A,桥接层虽然啰嗦, 但边界清晰、可控、易调试。

方案 C — 把 C++ 打包成 SwiftPM 包

C++ 代码量大 / 要多端复用时,把 MathCore 做成一个 Swift Package:

swift
 
.target(name: "MathCore", path: "Sources/MathCore"),
.target(name: "MathCoreBridge", dependencies: ["MathCore"], path: "Sources/MathCoreBridge"),

App 依赖这个包。好处是 C++ 核心有自己的编译单元和单元测试,Xcode 工程不会越滚越乱。

本示例的运行方式

bash
 
# 命令行
xcodebuild -project HelloCpp.xcodeproj -scheme HelloCpp \
  -destination 'platform=iOS Simulator,name=iPhone 16 Pro' build

# 或者直接
open HelloCpp.xcodeproj   # 然后 ⌘R

纯 C++ 核心还可以完全脱离 Xcode 验证,这是分层设计最大的好处:

bash
 
clang++ -std=c++20 -IHelloCpp/Cpp HelloCpp/Cpp/MathCore.cpp your_test.cpp -o t && ./t
posted @ 2026-10-06 13:04  cnchengv  阅读(7)  评论(0)    收藏  举报