swift分别与oc、c、c++互相调用

3,834 阅读5分钟

前言

最近从大佬处请教到一套c++写的跨平台的socket封装底层来学习, 但是swift与c++无法直接调用, 又不想去写oc, 所以折腾下中转来调用. swift通过桥接文件可以直接调用oc、c, 调用c++的话需要再次桥接下

swift与oc:

  1. swift调oc: swift调用oc需要通过桥接文件导入oc的头文件, 创建桥接文件有两种方式:
    • 系统自动创建:
      在swift项目中创建oc类时会提示是否创建桥接文件, 选择创建, 就会自动创建一个名为:工程名-Bridging-Header.h的桥接文件
    • 手动创建:
      创建一个头文件:***.h, 然后在Build Settings中查找bridging, 将Objective-C Bridging Header设置为创建的头文件即可(注意文件路径)
      然后在桥接文件中导入oc类的头文件, 就可以在swift类中调用oc的类与方法了:
    桥接文件-Bridging-Header.h中:
    #import "OCPerson.h"
    
    swift类中:
    //MARK: swift x oc
    let person = OCPerson()
    let name = person.getName()
    person.printInfo()
    print("oc person name = \(name)")
    
  2. oc调swift
    oc调用swift的话, 首先需要将swift需要被oc调用的属性与方法使用@objc标记, 接着只要导入通用的swift头文件, 即可调用全部swift的类, 这个头文件的名字有自动生成, 也可以自己修改, 在Build Settings中搜索generated interface: 这就是系统默认的swift头文件, 默认为工程名-Swift.h, 如果工程名为中文, 这里也会是中文, 可以自己手动修改, 找到头文件后, 建议在pch文件中统一导入该头文件, 即可在oc中调用swift类.
    swift类中
    class SwiftPerson: NSObject {
        @objc var name = ""
        @objc var age = 0
        @objc var isMale = false
    
        func printInfo() {
            print("name = \(name), age = \(age), isMale = \(isMale)")
        }
    
        @objc func getName() -> String {
            return name
        }
    }
    oc类中调用:
    - (void)callSwiftFunc {
        SwiftPerson *person = [[SwiftPerson alloc] init];
        NSString *name = person.name;
        NSLog(@"oc call swiftPerson name = %@, callName = %@", name, [person getName]);
    }
    

swift与c

  1. swift调c swift调用c也需要桥接,与oc一样, 先设置好工程桥接文件.
    先创建c文件与头文件:
    first_c.h中:
    void printHello(void);
    first_c.c中:
    
    #include "first_c.h"
    
    void printHello(void) {
        printf("hello world ! \n");
    }
    
    然后创建一个二次桥接的头文件CBridging.h,用来声明供swift调用的c函数名, 以及后续c用来调用swift的函数名. 然后在桥接文件中导入CBridging.h即可调用

    代码示意详见gitee demo

  2. c调用swift c调用swift比较折腾:
    1. 在二次桥接文件CBridging.h中声明c用来调用swift的方法名:
    void c_callSwiftFunc(int value);
    
    1. 在swift中 在c的头文件中将该方法实现设置为null:
    void(^ __nonnull c_swiftFuncIMP)(int) = NULL;
    
    1. 在swift中实现一个方法, 入参出参与将要被调用的方法一致:
    func c_swiftFuncIMP(value: Int32) -> Void {
        print("c call swift func: \(value)")
    }
    
    1. 在c即将调用swift方法前, 将要调用的函数指向swift的方法:
    c_swiftFuncIMP = c_swiftFuncIMP(value:)
    
    1. 在c中调用该方法, 即可调用swift中对应方法:
    void c_callSwiftFunc(int value) {
        c_swiftFuncIMP(value);
    }
    

    代码示意详见gitee demo

swift与c++

swift与oc可以通过桥接文件无缝调用, 直接调用c、c++比较折腾, 因此想要通过swift调用c、c++, 一种方法可以通过oc类做中转, 在oc中对c、c++进行包装, 使swift通过调用oc来调用对应c、c++的相关方法, 而oc只要把.m文件改写为.mm, 即可直接在代码中调用c++相关方法, swift可以通过oc桥接来调用c++, 另外一种方法使用swift与c++互相调用为使用c包装, 通过c的封装来调用c++方法, 缺点是所有调用风格都变成c的风格, 没办法像c++, oc那样面向对象调用了.

  1. swift调用c++:
    1. 先创建c++类.
    2. 在二次桥接文件CBridging.h中, 定义传递c++对象的指针, 声明需要给swift调用的c++函数:
    //cpp调用相关
    typedef void* CPPPersonModel;//定义C++的对象指针
    
    CPPPersonModel PMCreate();
    CPPPersonModel PMCreateBy(const char *name, int age, int isMale);
    void PMPrintInfo(CPPPersonModel model);
    const char *PMGetName(CPPPersonModel model);
    void PMDestoryModel(CPPPersonModel model);
    void PMCallSwift(CPPPersonModel model, int value);
    

    注意所有的类的方法, 均变成了将调用对象指针作为第一个参数传入

    1. 在c++的实现文件中导入该头文件(这就是为什么需要二次桥接文件, 因为工程侨界头文件会带有oc相关宏, c或者c++中编译无法识别), 并实现这些方法:
    CPPPersonModel PMCreate() {
        return new CPPPerson();
    }
    
    CPPPersonModel PMCreateBy(const char * name, int age, int isMail) {
        return new CPPPerson(name, age, isMail != 0);
    }
    
    void PMPrintInfo(CPPPersonModel model) {
        CPPPerson *p = (CPPPerson*)model;
        p->printInfo();
    }
    
    const char * PMGetName(CPPPersonModel model) {
        CPPPerson *p = (CPPPerson*)model;
        return p->getName();
    }
    
    void PMDestoryModel(CPPPersonModel model) {
        CPPPerson *p = (CPPPerson*)model;
        delete p;
    }
    
    void PMCallSwift(CPPPersonModel model, int value) {
        CPPPerson *p = (CPPPerson*)model;
        p->callSwiftFunc(value);
    }
    
    1. swift中调用:
    //MARK: swift x cpp
    cpp_swiftFuncIMP = cpp_swiftFuncIMP(value:)
    let p = PMCreateBy("p", 33, 1)
    let pname = PMGetName(p)
    print("swift call cpp: p = \(String.init(cString: pname!))")
    PMPrintInfo(p)
    PMCallSwift(p, 2222)
    PMDestoryModel(p)
    
  2. c++调用swift: 封装过程同上面c调用swift类似, 详细实现可以看demo

Demo地址:

gitee.com/callmedoby/…

总结:

  • swift与oc完美互相调用, swift也可以使用oc runtime的各种黑魔法, 但是swift的一些高级语言特性: 结构体对象, 高级枚举, 协议扩展, 范型, 约束等, oc无法调用,swift自带的高阶函数(filter, map等), oc没有自带, 但是可以自己用类别+block扩展实现.
  • swift调用c需要通过桥接文件引入一下函数名即可调用, 挺方便, 但是c调用swift比较折腾, 需要在swift中指定函数实现
  • swift调用c++有两种方式: 1.通过oc封装, 2.通过c封装成c风格的方法. 各有好处.
  • c++调用swift跟c调用swift一样折腾...

以上纯属个人理解, 如有错误, 欢迎评论指出, 将第一时间修改, ~~谢谢~~