本文講述OC和Swift混編中,OC轉(zhuǎn)換Swift掐禁,Swift轉(zhuǎn)換OC的橋接和調(diào)用過程遭贸。
主要內(nèi)容:
- Swift調(diào)用OC
- OC調(diào)用Swift
1. Swift調(diào)用OC
Swift文件中使用OC代碼它改,需要增加橋接文件遣妥,在文件中添加需要被調(diào)用的OC的信息品姓。并將該橋接文件設(shè)置給XCode秩冈,XCode會自動幫我們將橋接文件中的OC代碼轉(zhuǎn)換成Swift代碼本缠。
1.1 橋接文件
文件默認(rèn)命名為:{targetName}-Bridging-Header.h。
橋接文件中寫入需要被Swift調(diào)用的OC代碼頭文件
創(chuàng)建好頭文件后需要在工程中進(jìn)行配置
1.2 轉(zhuǎn)換過程
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調(diào)用
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中就可以正常的去調(diào)用OC代碼
- 這時系統(tǒng)會自動將OC代碼改成Swift代碼格式丹锹,所以在使用時就和Swift原生代碼一樣
1.3 函數(shù)名沖突
如果C語言暴露給Swift的函數(shù)名跟Swift中的其他函數(shù)名沖突了稀颁,可以在Swift中使用 @_silgen_name 修改C函數(shù)名
代碼:
// 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中的函數(shù)重命名一下楣黍,不僅是修改函數(shù)名稱匾灶,可以看到需要把函數(shù)參數(shù)這些也要寫成Swift的格式
2、更重要的用途是可以通過這種方式調(diào)用Swift底層的C++函數(shù)
2. OC調(diào)用Swift
OC調(diào)用Swift也需要一個橋接文件租漂,橋接文件是系統(tǒng)生成的阶女,并且系統(tǒng)會幫我們在橋接文件中自動生成暴露給OC的Swift代碼。
2.1 橋接文件
XCode會自動生成一個用于OC調(diào)用Swift的頭文件哩治,格式為:{targetName}-Swift.h秃踩。使用時直接導(dǎo)入頭文件即可
2.2 轉(zhuǎn)換過程
Swift文件
/*
1、繼承自NSObject
2业筏、使用@objcMembers或@object修飾需要暴露給OC的內(nèi)容
*/
@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類轉(zhuǎn)換成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
//擴展相當(dāng)于分類
@interface Car (SWIFT_EXTENSION(OC和Swift混編))
- (void)test;
@end
OC調(diào)用:
#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調(diào)用方法必須使用isa,所以需要繼承自NSObject
- 將需要暴露給OC的成員增加關(guān)鍵字
- 在類前寫上@objcMembers就可以使用類中所有成員蒜胖,這時還會暴露擴展中的成員
- 也可以使用@objc修飾需要暴露給OC的成員
- 但是最終是否會暴露成功消别,還要考慮成員自身的訪問級別
2.3 重命名
可以通過 @objc 重命名Swift暴露給OC的符號名(類名、屬性名台谢、函數(shù)名等)寻狂。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. 總結(jié)
注意:
1朋沮、Swift調(diào)用OC的方法蛇券,會走Runtime流程
2、OC調(diào)用Swift的方法樊拓,也會走Runtime流程
3怀读、暴露給OC的Swift方法被Swift內(nèi)部調(diào)用,會走Swift流程
4骑脱、如果Swift調(diào)用Swift方法但是走Runtime流程,需要使用dynamic來修飾一下
總結(jié):
- OC和Swift相互調(diào)用均需使用橋接文件進(jìn)行轉(zhuǎn)換
- 系統(tǒng)都會幫我們完成轉(zhuǎn)換苍糠,但是OC轉(zhuǎn)換Swift需要手動創(chuàng)建文件并添加頭文件
- 系統(tǒng)轉(zhuǎn)換后叁丧,Swift使用Swift的形式調(diào)用OC的代碼,OC使用OC的形式調(diào)用Swift的代碼
- OC項目和Swift項目在橋接時均無差異