本文讲述OC和Swift混编中,OC转换Swift,Swift转换OC的桥接和调用过程。
主要内容:
- Swift调用OC
- OC调用Swift
1. Swift调用OC
Swift文件中使用OC代码,需要增加桥接文件,在文件中添加需要被调用的OC的信息。并将该桥接文件设置给XCode,XCode会自动帮我们将桥接文件中的OC代码转换成Swift代码。
1.1 桥接文件
文件默认命名为:{targetName}-Bridging-Header.h。
桥接文件中写入需要被Swift调用的OC代码头文件
创建好头文件后需要在工程中进行配置
1.2 转换过程
OC代码
int sum(int a, int b);
@interface WYPerson : NSObject
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, copy) NSString *name;
- (instancetype)initWithAge:(NSInteger)age name:(NSString *)name;
+ (instancetype)personWithAge:(NSInteger)age name:(NSString *)name;
- (void)run;
+ (void)run;
- (void)eat:(NSString *)food other:(NSString *)other;
+ (void)eat:(NSString *)food other:(NSString *)other;
@end
@implementation WYPerson
- (instancetype)initWithAge:(NSInteger)age name:(NSString *)name {
if (self = [super init]) {
self.age = age;
self.name = name;
}
return self;
}
+ (instancetype)personWithAge:(NSInteger)age name:(NSString *)name {
return nil;
}
+ (void)run { NSLog(@"Person +run"); }
- (void)run { NSLog(@"%zd %@ -run", _age, _name); }
+ (void)eat:(NSString *)food other:(NSString *)other { NSLog(@"Person +eat %@ %@", food, other); }
- (void)eat:(NSString *)food other:(NSString *)other { NSLog(@"%zd %@ -eat %@ %@", _age, _name, food, other); }
@end
int sum(int a, int b) { return a + b; }
桥接文件:
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "WYPerson.h"
Swift调用
let p = WYPerson.init(age: 10, name: "WY")
p.age = 18
p.name = "wenyi"
p.run() // 18 Rose -run
p.eat("Apple", other: "Water") // 18 Rose -eat Apple Water
WYPerson.run() // Person +run
WYPerson.eat("Pizza", other: "Banana") // Person +eat Pizza Banana
print(sum(10, 20)) // 30
说明:
- 只要在桥接文件中声明,在Swift中就可以正常的去调用OC代码
- 这时系统会自动将OC代码改成Swift代码格式,所以在使用时就和Swift原生代码一样
1.3 函数名冲突
如果C语言暴露给Swift的函数名跟Swift中的其他函数名冲突了,可以在Swift中使用 @_silgen_name 修改C函数名
代码:
// C语言
int sum(int a, int b) {
return a + b;
}
// Swift
@_silgen_name("sum") func swift_sum(_ v1: Int32, _ v2: Int32) -> Int32
print(swift_sum(10, 20)) // 30
print(sum(10, 20)) // 30
说明:
1、将OC中的函数重命名一下,不仅是修改函数名称,可以看到需要把函数参数这些也要写成Swift的格式
2、更重要的用途是可以通过这种方式调用Swift底层的C++函数
2. OC调用Swift
OC调用Swift也需要一个桥接文件,桥接文件是系统生成的,并且系统会帮我们在桥接文件中自动生成暴露给OC的Swift代码。
2.1 桥接文件
XCode会自动生成一个用于OC调用Swift的头文件,格式为:{targetName}-Swift.h。使用时直接导入头文件即可
2.2 转换过程
Swift文件
/*
1、继承自NSObject
2、使用@objcMembers或@object修饰需要暴露给OC的内容
*/
@objcMembers class Car: NSObject {
var price: Double
var band: String
init(price: Double, band: String) {
self.price = price
self.band = band
}
func run() {
print(price, band, "run")
}
static func run() { print("Car run") }
}
//扩展
extension Car {
func test() { print(price, band, "test") }
}
桥接文件:
//将Swift类转换成OC类的格式
SWIFT_CLASS("_TtC16OC和Swift混编3Car")
@interface Car : NSObject
@property (nonatomic) double price;
@property (nonatomic, copy) NSString * _Nonnull band;
- (nonnull instancetype)initWithPrice:(double)price band:(NSString * _Nonnull)band OBJC_DESIGNATED_INITIALIZER;
- (void)run;
+ (void)run;
- (nonnull instancetype)init SWIFT_UNAVAILABLE;
+ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable");
@end
//扩展相当于分类
@interface Car (SWIFT_EXTENSION(OC和Swift混编))
- (void)test;
@end
OC调用:
#import "OC和Swift混编-Swift.h"
void testSwift() {
Car *c = [[Car alloc] initWithPrice:100 band:@"bm"];
[c run];
[c test];
[Car run];
}
说明:
- 自定义Swift类添加到头文件中需要两个条件
- Swift类需要继承自NSObject,这是因为OC调用方法必须使用isa,所以需要继承自NSObject
- 将需要暴露给OC的成员增加关键字
- 在类前写上@objcMembers就可以使用类中所有成员,这时还会暴露扩展中的成员
- 也可以使用@objc修饰需要暴露给OC的成员
- 但是最终是否会暴露成功,还要考虑成员自身的访问级别
2.3 重命名
可以通过 @objc 重命名Swift暴露给OC的符号名(类名、属性名、函数名等)。OC在使用时,就可以使用重命名的名称来写了
@objc(MJCar)
@objcMembers class Car: NSObject {
var price: Double
@objc(name)
var band: String
init(price: Double, band: String) {
self.price = price
self.band = band
}
@objc(drive)
func run() { print(price, band, "run") }
static func run() { print("Car run") }
}
extension Car {
@objc(exec:v2:)
func test() { print(price, band, "test") }
}
3. 总结
注意:
1、Swift调用OC的方法,会走Runtime流程
2、OC调用Swift的方法,也会走Runtime流程
3、暴露给OC的Swift方法被Swift内部调用,会走Swift流程
4、如果Swift调用Swift方法但是走Runtime流程,需要使用dynamic来修饰一下
总结:
- OC和Swift相互调用均需使用桥接文件进行转换
- 系统都会帮我们完成转换,但是OC转换Swift需要手动创建文件并添加头文件
- 系统转换后,Swift使用Swift的形式调用OC的代码,OC使用OC的形式调用Swift的代码
- OC项目和Swift项目在桥接时均无差异