Objective-C 与 Swift 互调完整教程
一、概念前置:桥接文件(Bridging Header)
桥接文件是 OC 和 Swift 互调的核心机制,它是一个.h文件,作用类似于"双向翻译官"。Xcode 通过这个文件知道哪些 OC 代码需要暴露给 Swift。
| 场景 | 需要的文件 | 谁创建 |
|---|---|---|
| OC 项目 → 添加 Swift | 项目名-Bridging-Header.h | Xcode 自动提示创建 |
| Swift 项目 → 添加 OC | 项目名-Bridging-Header.h | Xcode 自动提示创建 |
| Swift → 暴露给 OC | 项目名-Swift.h | Xcode自动生成(无需手动创建) |
二、场景一:OC 项目中添加 Swift 支持
第 1 步:创建 Swift 文件
在 Xcode 中:File → New → File...→ 选择Swift File→ 命名并保存。
第 2 步:创建桥接头文件
保存时 Xcode 会弹窗提示:
“Would you like to configure an Objective-C bridging header?”
点击Create Bridging Header,Xcode 会自动生成项目名-Bridging-Header.h。
⚠️ 如果误点了
Don't Create,可以手动创建:File → New → File... → Header File,然后在Build Settings中搜索Objective-C Bridging Header,填入该文件的路径。
第 3 步:在 Swift 中调用 OC
在项目名-Bridging-Header.h中导入需要暴露给 Swift 的 OC 头文件:
// 项目名-Bridging-Header.h#import"Person.h"#import"Student.h"#import"EcgAnalysisModel.h"之后在 Swift 中就可以直接使用这些 OC 类了:
// Swift 代码letperson=Person()person.name="张三"person.age=25person.sayHello()第 4 步:在 OC 中调用 Swift
Swift 类需要加@objc暴露给 OC:
// Swift 代码importFoundation// 必须继承 NSObject,并加 @objc@objcMembersclassSwiftManager:NSObject{varname:String=""funcdoSomething(){print("Swift 方法被调用")}}在 OC 文件中导入 Xcode 自动生成的头文件:
// OC 代码 (.m 文件)#import"项目名-Swift.h"// Xcode 自动生成,无需手动创建// 使用 Swift 类SwiftManager*manager=[[SwiftManager alloc]init];manager.name=@"李四";[manager doSomething];📋 OC 项目互调总结
| 方向 | 操作 |
|---|---|
| Swift → 调用 OC | 在 Bridging-Header.h 中#importOC 头文件 |
| OC → 调用 Swift | Swift 类加@objc,OC 中#import "项目名-Swift.h" |
三、场景二:Swift 项目中添加 OC 支持
第 1 步:创建 OC 文件
在 Xcode 中:File → New → File...→ 选择Objective-C File→ 命名并保存。
第 2 步:创建桥接头文件
同样,Xcode 会弹窗提示创建桥接头文件,点击Create Bridging Header。
第 3 步:在 Swift 中调用 OC
在项目名-Bridging-Header.h中导入 OC 头文件:
// 项目名-Bridging-Header.h#import"OldClass.h"#import"Utils.h"Swift 中直接使用:
// Swift 代码letold=OldClass()old.doSomething()第 4 步:在 OC 中调用 Swift
Swift 类加@objc暴露:
@objcMembersclassSwiftHelper:NSObject{funcshowMessage(_text:String){print(text)}}OC 中导入项目名-Swift.h:
#import"项目名-Swift.h"SwiftHelper*helper=[[SwiftHelper alloc]init];[helper showMessage:@"Hello from OC"];📋 Swift 项目互调总结
| 方向 | 操作 |
|---|---|
| Swift → 调用 OC | 在 Bridging-Header.h 中#importOC 头文件 |
| OC → 调用 Swift | Swift 类加@objc,OC 中#import "项目名-Swift.h" |
四、关键文件对比
| 文件 | 谁创建 | 作用 | 是否手动维护 |
|---|---|---|---|
项目名-Bridging-Header.h | 开发者 / Xcode 自动 | Swift 调用 OC 的入口 | ✅ 需要手动#import |
项目名-Swift.h | Xcode 自动生成 | OC 调用 Swift 的入口 | ❌ 不需要创建和修改 |
五、关于桥接文件的常见疑问
Q1:桥接文件中需要写什么?
桥接文件中只写#import语句,把你需要暴露给 Swift 的 OC 头文件放进去。
// HeartBook2.0-Bridging-Header.h#ifndefHeartBook2_0_Bridging_Header_h#defineHeartBook2_0_Bridging_Header_h// 这些 OC 类会被 Swift 代码看到#import"Person.h"#import"Student.h"#import"EcgAnalysisModel.h"#import"ServerEcgDataModel.h"#import"HeartBookReportView.h"#endifQ2:什么时候需要在桥接文件中添加?
只有当你要在 Swift 代码中调用某个 OC 类时,才需要把那个 OC 类的头文件#import到桥接文件中。
| 场景 | 是否需要添加 |
|---|---|
| Swift 代码调用 OC 类 | ✅ 需要在桥接文件中#import对应的 OC 头文件 |
| OC 代码调用 Swift 类 | ❌ 不需要引用桥接文件,改为引用项目名-Swift.h |
| 只在 OC 内部使用 OC 类 | ❌ 不需要,直接在 OC 的.m文件中#import即可 |
Q3:桥接文件需要被其他文件#import吗?
不需要。Xcode 在编译时通过Build Settings中的Objective-C Bridging Header配置项自动读取它。你不需要在其他任何地方手动#import它。
Q4:每次新建 OC 类都要去桥接文件里添加吗?
是的。如果 Swift 代码需要调用新建的 OC 类,就必须去桥接文件里#import它。如果只是 OC 内部使用,就不需要。
Q5:项目名-Bridging-Header.h和项目名-Swift.h是什么关系?
| 文件 | 作用 | 谁维护 |
|---|---|---|
项目名-Bridging-Header.h | Swift 能看到哪些 OC 类 | 开发者手动添加#import |
项目名-Swift.h | OC 能看到哪些 Swift 类 | Xcode 自动生成,不要手动修改 |
六、常见问题与避坑指南
1.#import "项目名-Swift.h"找不到?
检查Build Settings中Defines Module是否为YES。
2. Swift 类在 OC 中无法使用?
- 确保类继承了
NSObject - 确保加了
@objc或@objcMembers - 结构体、枚举等值类型无法直接暴露给 OC
3. OC 的nil和 Swift 的nil怎么转换?
// OC 返回 nil → Swift 中是 Optionalletresult=ocObject.someMethod()// 类型是 String?4. 桥接文件路径配置
如果桥接文件位置移动了,需要在Build Settings→Objective-C Bridging Header中更新路径。
路径示例:HeartBook2.0/HeartBook2.0-Bridging-Header.h
5. Swift 调用 OC 的 Block
// OC 中的 Blocktypedefvoid(^CompletionBlock)(BOOL success,NSString*_Nullable message);-(void)doTaskWithCompletion:(CompletionBlock)completion;Swift 中调用:
ocObject.doTask{success,messageinprint("success:\(success), message:\(message??"")")}6. OC 调用 Swift 的闭包
@objcMembersclassSwiftService:NSObject{funcfetchData(completion:@escaping(String)->Void){completion("数据返回")}}OC 中调用:
SwiftService*service=[[SwiftService alloc]init];[service fetchDataWithCompletion:^(NSString*_Nonnull data){NSLog(@"收到数据: %@",data);}];7. 桥接文件中为什么要用#ifndef ... #endif?
#ifndefHeartBook2_0_Bridging_Header_h#defineHeartBook2_0_Bridging_Header_h// ... 你的 #import 语句 ...#endif这是标准的 C/C++ 头文件保护宏,作用是防止同一个头文件被重复导入。如果这个头文件被多次#import,宏会确保它的内容只被编译一次。这是 Xcode 自动生成的,不需要手动修改。
七、完整示例
OC 项目添加 Swift 的目录结构
HeartBook2.0/ ├── HeartBook2.0-Bridging-Header.h // 桥接头文件(手动维护) ├── HeartBook2.0-Swift.h // Xcode 自动生成(不要手动修改) ├── Person.h // OC 头文件 ├── Person.m // OC 实现 ├── SwiftManager.swift // Swift 文件 └── ViewController.m // OC 调用 Swift桥接文件示例
// HeartBook2.0-Bridging-Header.h#import"Person.h"#import"Student.h"#import"EcgAnalysisModel.h"Swift 示例
// SwiftManager.swiftimportFoundation@objcMembersclassSwiftManager:NSObject{funcgreet(person:Person)->String{return"你好,\(person.name??"未知")"}funcprocessEcg(model:EcgAnalysisModel)->Int{returnInt(model.max_heart)}}OC 调用 Swift 示例
// ViewController.m#import"HeartBook2.0-Swift.h"-(void)testSwift{SwiftManager*manager=[[SwiftManager alloc]init];Person*p=[[Person alloc]init];p.name=@"王五";NSString*greeting=[manager greetWithPerson:p];NSLog(@"%@",greeting);}八、快速决策表
| 你的情况 | 要做的事 |
|---|---|
| OC 项目,想用 Swift 写新功能 | 创建 Swift 文件 → 创建 Bridging-Header → 暴露 OC 类 |
| Swift 项目,想复用 OC 旧代码 | 创建 OC 文件 → 创建 Bridging-Header → 暴露 OC 类 |
| Swift 类想让 OC 调用 | 加@objc→ 在 OC 中#import "项目名-Swift.h" |
| Swift 代码想调用某个 OC 类 | 在 Bridging-Header 中#import该 OC 类的头文件 |
| 新建了一个 OC 类,Swift 想用它 | 去 Bridging-Header 中#import它的头文件 |
编译报错-Swift.h找不到 | 检查Defines Module = YES |
| 桥接文件路径不对 | Build Settings → Objective-C Bridging Header → 修正路径 |