在OC和Swift混合项目中处理语言交互,核心围绕 “双向调用的配置”“类型映射”“场景化适配” 三大维度展开,需按“Swift调用OC”和“OC调用Swift”两个方向分别处理,同时解决协议、闭包、枚举等特殊场景的交互问题:
一、Swift 调用 OC:依赖“桥接文件”,直接访问暴露的 OC 元素
Swift 无法直接识别 OC 代码,需通过 桥接文件(Bridging Header) 导入 OC 头文件,本质是让 Swift 编译器“感知”OC 的类、协议、方法。
1. 核心步骤(必做)
• Step 1:配置桥接文件
确保已创建桥接文件(如 Demo-Bridging-Header.h),并在 Build Settings 中配置正确路径(前文已讲,此处略)。
• Step 2:在桥接文件中导入 OC 头文件
需调用的 OC 类、协议、分类,必须在桥接文件中导入其头文件(仅需导入“公开头文件”,私有头文件无需导入)。
示例(桥接文件内容):
// 导入 OC 类头文件
#import "OCNetworkTool.h"
// 导入 OC 协议头文件
#import "OCLoginDelegate.h"
// 导入 OC 分类头文件
#import "UIView+OCFrame.h"
• Step 3:Swift 中直接调用 OC 元素
导入后,Swift 可像调用 Swift 代码一样使用 OC 类(OC 的类默认继承 NSObject,天然兼容 Swift)。
示例:
// 1. 实例化 OC 类并调用方法
let ocNetwork = OCNetworkTool.shared()
ocNetwork.requestData(url: "https://api.demo.com", success: { data in
print("Swift 调用 OC 网络请求成功")
})
// 2. 调用 OC 分类方法
let view = UIView()
view.oc_setFrame(x: 10, y: 10, width: 100, height: 100) // OC 分类的方法
// 3. 实现 OC 协议
class SwiftLoginVC: UIViewController, OCLoginDelegate {
func loginSuccess(user: String) { // OC 协议的方法
print("登录成功:\(user)")
}
}
2. 特殊场景处理(避坑点)
• OC 宏的处理:Swift 不支持 OC 的宏(尤其是带参数的宏,如 #define ADD(a,b) a+b),需将宏替换为 OC 的静态方法或常量,再通过桥接文件导入。
示例:OC 中定义静态方法替代宏:
// OC 头文件
@interface OCMacroTool : NSObject
+ (NSInteger)addA:(NSInteger)a andB:(NSInteger)b;
@end
// OC 实现文件
@implementation OCMacroTool
+ (NSInteger)addA:(NSInteger)a andB:(NSInteger)b {
return a + b;
}
@end
Swift 中调用:let sum = OCMacroTool.addA(10, andB: 20)。
• OC 枚举的处理:OC 的枚举(enum)需用 NS_ENUM 定义(而非 enum 关键字),才能被 Swift 识别为“枚举类型”(否则会被当作整数)。
示例(OC 枚举):
// OC 中用 NS_ENUM 定义枚举
typedef NS_ENUM(NSInteger, OCLoginType) {
OCLoginTypePassword = 0,
OCLoginTypeWeChat = 1
};
Swift 中使用:let type: OCLoginType = .password(自动映射为 Swift 枚举)。
二、OC 调用 Swift:依赖“系统自动生成的 Swift 头文件”,暴露 Swift 元素
OC 调用 Swift 需通过 “项目名-Swift.h”(系统自动生成,无需手动创建),且 Swift 元素需满足“OC 兼容条件”(否则无法被识别)。
1. 核心步骤(必做)
• Step 1:确保 Swift 元素可被 OC 识别
Swift 类/属性/方法需满足以下条件,才能被 OC 调用:
1. 类必须继承自 NSObject(纯 Swift 类如 struct、不继承 NSObject 的 class 无法被 OC 识别);
2. 用 @objc 修饰单个元素(类、属性、方法),或用 @objcMembers 修饰类(批量暴露所有元素,推荐)。
示例(Swift 类):
// 用 @objcMembers 批量暴露,类需继承 NSObject
@objcMembers
class SwiftUser: NSObject {
// 可被 OC 调用的属性
var name: String
var age: Int
// 可被 OC 调用的构造器(需带参数,或重写 init())
init(name: String, age: Int) {
self.name = name
self.age = age
}
// 可被 OC 调用的方法
func sayHello() -> String {
return "Hello, \(name)"
}
}
• Step 2:OC 中导入“项目名-Swift.h”头文件
必须在 OC 的 .m 文件 中导入该头文件(不能在 .h 文件中导入,否则会引发循环引用)。
示例(OC .m 文件):
// 导入系统自动生成的 Swift 头文件(项目名为 Demo)
#import "Demo-Swift.h"
@implementation OCViewController
- (void)useSwiftClass {
// 1. 实例化 Swift 类
SwiftUser *swiftUser = [[SwiftUser alloc] initWithName:@"Swift" age:10];
// 2. 访问 Swift 属性
NSLog(@"SwiftUser: %@, %ld", swiftUser.name, swiftUser.age);
// 3. 调用 Swift 方法
NSString *hello = [swiftUser sayHello];
NSLog(@"%@", hello);
}
@end
2. 特殊场景处理(避坑点)
• Swift 闭包与 OC Block 的转换
Swift 闭包需用 @convention(block) 修饰,才能被 OC 识别为 Block 类型。
示例(Swift 暴露闭包参数的方法给 OC):
@objcMembers
class SwiftTool: NSObject {
// 用 @convention(block) 修饰闭包,参数类型需与 OC Block 匹配
func fetchData(completion: @convention(block) (String?, Error?) -> Void) {
// 模拟网络请求
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
completion("Swift 数据", nil)
}
}
}
OC 中调用(Block 对应 Swift 闭包):
SwiftTool *tool = [[SwiftTool alloc] init];
[tool fetchDataWithCompletion:^(NSString * _Nullable data, NSError * _Nullable error) {
NSLog(@"OC 收到 Swift 数据:%@", data);
}];
• Swift 协议被 OC 实现
Swift 协议需用 @objc 修饰,且协议方法需标记为 optional(或用 @objc optional),才能被 OC 遵守(OC 不支持 Swift 的“必须实现”协议方法默认规则)。
示例(Swift 协议):
// 用 @objc 修饰协议,方法用 @objc optional 标记为可选
@objc protocol SwiftDataDelegate {
@objc optional func dataLoaded(data: String)
}
// Swift 类中持有协议属性
@objcMembers
class SwiftDataManager: NSObject {
weak var delegate: SwiftDataDelegate?
func loadData() {
delegate?.dataLoaded?(data: "Swift 加载的数据")
}
}
OC 中遵守协议并实现方法:
#import "Demo-Swift.h"
@interface OCDataVC () <SwiftDataDelegate>
@property (nonatomic, strong) SwiftDataManager *dataManager;
@end
@implementation OCDataVC
- (void)viewDidLoad {
[super viewDidLoad];
self.dataManager = [[SwiftDataManager alloc] init];
self.dataManager.delegate = self;
[self.dataManager loadData];
}
// 实现 Swift 协议的可选方法
- (void)dataLoadedWithData:(NSString *)data {
NSLog(@"OC 收到协议数据:%@", data);
}
@end
三、通用避坑点(交互必看)
1. 类型映射必须严格对应
避免直接强转不兼容类型,核心映射关系如下(前文已提,此处强调关键易错点):
OC 的 NSArray → Swift 的 [Any](需强转为具体类型,如 as! [String]);
OC 的 NSDictionary → Swift 的 [AnyHashable: Any](需强转为 [String: Int] 等具体类型);
OC 的 NSNumber → Swift 的 Int/Double(需用 intValue/doubleValue 转换,或 as? Int);
OC 的 nil → Swift 的 Optional(OC 传 nil 给 Swift 可选类型,需用 if let 安全解包,避免 ! 强解)。
2. 避免循环引用
Swift 闭包调用 OC 时,若捕获 self,需用 [weak self](如 ocTool.request { [weak self] in self?.updateUI() });
OC Block 调用 Swift 时,若引用 self,需用 __weak typeof(self) weakSelf = self(如 [swiftTool fetchData:^(id data) { [weakSelf updateUI:data]; }])。
3. Swift 专属特性不支持 OC 调用
以下 Swift 特性无法被 OC 识别,需避免在跨语言交互中使用:
泛型类/方法(如 class SwiftGeneric<T>);
枚举关联值(如 enum SwiftEnum { case a(Int), case b(String) });
值类型(struct、enum,除非包装为 NSObject 子类);
Swift 独有的协议(如 Identifiable,未用 @objc 修饰)。
总结:交互核心原则
1. 入口唯一:Swift 调 OC 仅通过“桥接文件”,OC 调 Swift 仅通过“项目名-Swift.h”,不允许绕开这两个入口直接引用;
2. 可见性优先:跨语言调用的元素,必须用 @objc/@objcMembers(Swift 侧)或导入头文件(OC 侧)暴露,否则编译器无法识别;
3. 类型安全:不随意强转类型,用 as?/if let 安全处理,避免因类型不匹配崩溃;
4. 场景适配:闭包/Block、协议、枚举等特殊场景,需按 OC 兼容规则修改 Swift 代码(如 @convention(block)、@objc 协议)。











网友评论