美文网首页Swift
Swift基础语法(十八)OC和Swift混编

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

作者: iOS之文一 | 来源:发表于2022-05-21 00:53 被阅读0次

    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混编

        本文链接:https://www.haomeiwen.com/subject/rtzaurtx.html