Swift基础语法(十八)OC和Swift混编

Swift基础语法文章汇总

本文讲述OC和Swift混编中,OC转换Swift,Swift转换OC的桥接和调用过程。

主要内容:

  1. Swift调用OC
  2. OC调用Swift

1. Swift调用OC

Swift文件中使用OC代码,需要增加桥接文件,在文件中添加需要被调用的OC的信息。并将该桥接文件设置给XCode,XCode会自动帮我们将桥接文件中的OC代码转换成Swift代码。

1.1 桥接文件

文件默认命名为:{targetName}-Bridging-Header.h。
桥接文件中写入需要被Swift调用的OC代码头文件
创建好头文件后需要在工程中进行配置

配置桥接文件.png

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];
}

说明:

  1. 自定义Swift类添加到头文件中需要两个条件
  2. Swift类需要继承自NSObject,这是因为OC调用方法必须使用isa,所以需要继承自NSObject
  3. 将需要暴露给OC的成员增加关键字
  4. 在类前写上@objcMembers就可以使用类中所有成员,这时还会暴露扩展中的成员
  5. 也可以使用@objc修饰需要暴露给OC的成员
  6. 但是最终是否会暴露成功,还要考虑成员自身的访问级别

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来修饰一下

总结:

  1. OC和Swift相互调用均需使用桥接文件进行转换
  2. 系统都会帮我们完成转换,但是OC转换Swift需要手动创建文件并添加头文件
  3. 系统转换后,Swift使用Swift的形式调用OC的代码,OC使用OC的形式调用Swift的代码
  4. OC项目和Swift项目在桥接时均无差异

你可能感兴趣的:(Swift基础语法(十八)OC和Swift混编)