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? |
踩坑清单
-
<...>语法错误 / "Unknown type name 'namespace'" 你在.h或 Bridging Header 里#include了 C++ 头。C++ 头只能出现在.mm里。 -
Swift 侧方法名跟你写的不一样 Swift 会按 "import as member" 规则重命名 ObjC 方法。本工程实测:
+ (NSArray *)primesUpTo:(NSInteger)limit被改成了primesUp(to:)。 解决办法是显式钉名字:+ (NSArray<NSNumber *> *)primesUpTo:(NSInteger)limit NS_SWIFT_NAME(primesUpTo(_:)); -
忘改
.m为.mm报 "Use of undeclared identifier 'mathcore'"。后缀决定语言模式,不是内容决定的。 -
C++ 异常穿过 ObjC 边界会崩
-fobjc-arc下 ObjC 消息发送和 C++ 异常不能混用。在.mm里全部try/catch兜住, 转成NSError **返回给 Swift。反过来 Swift 的throws也不能抛进 C++。 -
不要用
std::function接 Swift 闭包 ObjC block 在 C++ 容器里不会被 ARC 正确 retain/release。回调要在.mm里 把 block 转成 C 函数指针 +void *context的 trampoline,或者干脆把循环写在.mm里。 -
C++ 对象当 ivar 必须写在
@implementation里 直接写在@interface的{}里会报不完整类型。 -
.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:
.target(name: "MathCore", path: "Sources/MathCore"),
.target(name: "MathCoreBridge", dependencies: ["MathCore"], path: "Sources/MathCoreBridge"),
App 依赖这个包。好处是 C++ 核心有自己的编译单元和单元测试,Xcode 工程不会越滚越乱。
本示例的运行方式
# 命令行
xcodebuild -project HelloCpp.xcodeproj -scheme HelloCpp \
-destination 'platform=iOS Simulator,name=iPhone 16 Pro' build
# 或者直接
open HelloCpp.xcodeproj # 然后 ⌘R
纯 C++ 核心还可以完全脱离 Xcode 验证,这是分层设计最大的好处:
clang++ -std=c++20 -IHelloCpp/Cpp HelloCpp/Cpp/MathCore.cpp your_test.cpp -o t && ./t
浙公网安备 33010602011771号