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项目在桥接时均无差异
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容