Swift与OC混编

OC,Swift,混编 · 浏览次数 : 66

小编点评

** Swift 调OC 类方法的步骤:** 1. **创建{targetName}-Bridging-Header.h文件** - 在项目设置中添加一个新的文件,命名为 "{targetName}-Bridging-Header.h"。 - 在文件中添加以下代码: ```swift #import "OCClass.h" // OC类名称 @objc(targetName) // 方法重定义标记 class MyClass: NSObject {} ``` 2. **创建OC类** - 在项目中创建一个新的OC类,并将其命名为 "{targetName}"。 3. **编写OC方法** - 在"{targetName}"中编写需要暴露给Swift的方法。 4. **在Swift中使用OC类** - 在Swift中使用OC类时,使用`@objc`关键字进行声明。 - 在方法上添加 `@objc` 修饰符,并在方法头添加 `@objc` 属性,指定方法的名称和参数类型。 5. **编译项目** - 编译项目,并确保{targetName}-Bridging-Header.h文件被创建。 **示例:** **OC类:** ```c #import "MyClass.h" @objc(MyClass) class MyClass: NSObject { var speed: Float var name: String init(speed: Float, name: String) { self.speed = speed self.name = name } @objc(bigName) func bigRun() { print("跑圈,时速:\(speed)") } } ``` **Swift 类:** ```swift import MyClass class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let p = MyClass(speed: 11, name: "jack") p.bigRun() } } ``` **注意:** - {targetName}-Bridging-Header.h 文件中的路径会自动添加到项目的Bridging-Header文件中。 - 由于OC方法的名称可能与Swift方法名相同,可以使用`@_silgen_name`语法进行重命名。 - `@objcMembers` 属性可用于暴露全部信息,包括成员变量和方法以及扩展信息。

正文

Swift调OC
 
在Swift项目中调用OC类中的方法需要有个{targetName}-Bridging-Header.h文件,在这个文件中导入OC要暴露给Swift的类。
{targetName}-Bridging-Header.h文件的创建有2种方式
1.自己手动创建,然后在配置文件的Object-C Bridging Header中添加这个{targetName}-Bridging-Header.h文件的路径
2.在Swift项目工程中创建一个OC类,此时Xcode会自动帮助创建。
0
 
0
3.在Swift中使用OC类
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        let p = Person();
        p.name = "jack";
        p.age = 11;
        p.say();
    }
}
如果自己在OC中定义的C语言函数名字和Swift运行中的函数名一样,冲突时,可以使用@_silgen_name将C函数的名字进行重新成Swift的另一个函数名,供Swift中使用。
@_silgen_name("sum") func swift_sum(a: Int, b: Int) -> Int

 

OC调Swift
Swift项目在创建时就默认生成了一个{targetName}-Swift.h文件,用于暴露Swift类给OC使用。注意这里的targetName不允许使用-链接单词,如果出现这种情况会把-自动改成_进行单词连接。
如下,当前项目名是Swift-OC,而targetName被自动改成了Swift_OC。
0
 
Swift暴露给OC类使用时需要做一定的修改。
1.要暴露的类需要继承自NSObject
2.要暴露成员变量和方法给OC时,需要加@objc声明。
3.要暴露全部信息时,包含成员变量和方法以及扩展中的信息,则要在类前面加@objcMembers进行声明。
@objcMembers class Car: NSObject {
    var speed: Float;
    var name: String;
    
    init(speed: Float, name: String) {
        self.speed = speed;
        self.name = name;
    }
    
    func run() -> Void {
        print("出发,时速:\(self.speed)")
    }
}

extension Car {
    func circleRun() {
        print("跑圈,时速:\(self.speed)")
    }
}
 
Swift中@objc的使用
可以使用@objc重命名swift暴露给oc的方法
selector是oc的runtime里使用的一套东西,只有swift中的类暴露给OC是才能正常使用。
@objcMembers class Car: NSObject {
    var speed: Float;
    @objc(bigName) //重写暴露给OC的方法
    var name: String;
    
    init(speed: Float, name: String) {
        self.speed = speed;
        self.name = name;
    }
    
    func run() -> Void {
        print("出发,时速:\(self.speed)")
    }
    
    func runPerform() -> Void {
        perform(#selector(run))
    }
}

extension Car {
    @objc(bigRun)
    func circleRun() {
        print("跑圈,时速:\(self.speed)")
    }
}
Swift中被@objc 修改的协议可以被OC实现
@objc protocol RunProtocol {}
Swift的类中中被@objc dynamic 修改的方法 走runtime的那套消息发送机制
class Computer: NSObject {
    @objc dynamic func work(num: Int) {
        print("工作时长:\(num)")
    }
}
Swift的类中中被@objc dynamic 修改的属性可以进行KVC/KVO
class Person: NSObject {
    @objc dynamic var age: Int = 10
    
    override init() {
        super.init()
        self.addObserver(self, forKeyPath: "age", options: .new, context: nil)
    }
    
    override class func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        print("observer回调:\(change!)")
    }
    
    deinit {
        self.removeObserver(self, forKeyPath: "age")
    }
}
Swift与OC互相调用理解
 
1.Swift要暴露给OC调用,为什么要继承自NSObject?
OC的方法调用走的runtime的objc_sendMsg(), 这就需要isa指针, 而NSObject中有isa指针,所以要继承自NSObject
 
2.Swift调用OC桥接类的方法是怎么调用的,OC调用Swift暴露的方法又是如何调用的?
Swift调用自己定义的纯Swift方法底部走的是C++虚函数表那套。
Swift调用OC桥接类的方法底层走的还是OC的objc_msgSender机制,因为OC文件被编译后是转换的消息发送代码。
OC调用Swift暴露的方法时,因为是继承自NSObject, 所以底部走的还是objc_sendMsg机制
 
3.对于Swift类继承了NSObject,再在swift调用时,是怎么调用的?
底层走的还是Swift的需函数表那套,这套性能更高。
 
如何让Swift的方法走Runtime的消息转发机制呢?
通过在方法前面添加dynamic修饰
@objcMembers class Car: NSObject {
    var speed: Float;
    @objc(bigName) //重写暴露给OC的方法
    var name: String;
    
    init(speed: Float, name: String) {
        self.speed = speed;
        self.name = name;
    }
        
    dynamic func fillOil() -> Void {
        print("加油")
    }
}

 

 
 
 

与Swift与OC混编相似的内容:

Swift与OC混编

Swift调OC 在Swift项目中调用OC类中的方法需要有个{targetName}-Bridging-Header.h文件,在这个文件中导入OC要暴露给Swift的类。 {targetName}-Bridging-Header.h文件的创建有2种方式 1.自己手动创建,然后在配置文件的Objec

作为移动开发你不能不了解的编译流程

阅读本文,或许能够了解关于以下的几个问题: 1、编译器是什么?为什么会有编译器这样一个东西? 2、编译器做了哪些工作?整个编译过程又是什么? 3、Apple的编译器发展历程以及为什么会抛弃GCC换成自研的LLVM? 4、从编译器角度看Swift与OC能够实现混编的底层逻辑

Swift开发基础06-闭包

Swift的闭包(Closures)是一种将功能块和上下文整合并演示在代码中的一种手段。闭包可以捕获并存储其上下文中的变量和常量。与普遍存在于其他语言的匿名函数(如Python的lambda、JavaScript的函数表达式)类似,Swift的闭包提供了强大的功能,并在很多场景中替代了函数。 闭包有

Swift开发基础08-高阶函数

高阶函数是指接受其它函数作为参数,或者返回其它函数的函数。Swift 提供了许多内置的高阶函数,这些函数在处理集合类型数据(如数组、集合等)时尤其有用。常见的高阶函数包括 map、filter、reduce、flatMap 和 compactMap。 一、常用高阶函数 1. map map 函数会对

深入理解 Swift Combine

Combine 文中写一些 Swift 方法签名时,会带上 label,如 subscribe(_ subscriber:),正常作为 Selector 的写法时会忽略掉 label,只写作 subscribe(_:) ,本文特意带上 label 以使含义更清晰。 Combine Framework

Swift中发布-订阅框架Combine的使用

Combine简介 Combine是一个苹果用来处理事件的新的响应式框架,支持iOS 13及以上版本。 你可以使用Combine去统一和简化在处理类似于target-action,delegate,kvo等事情的代码。 iOS目前已经有第三方的响应式框架了,如:RxSwift、ReactiveCoc

Swift中UITableViewDiffableDataSource的使用

在 iOS 13 中 Apple 为 UITableView 和 UICollectionView 引入了 DiffableDataSource, 让开发者可以更简单高效的实现 UITableView、UICollectionView 的局部数据刷新。 新的刷新的方法为 apply 通过使用 app

Swift之struct二进制大小分析

随着Swift的日渐成熟和给开发过程带来的便利性及安全性,京喜App中的原生业务模块和基础模块使用Swift开发占比逐渐增高。本次讨论的是struct对比Class的一些优劣势,重点分析对包体积带来的影响及规避措施。

Swift下Data处理全流程:从网络下载,数模转换,本地缓存到页面使用

Swift下将网络返回json数据转换成struct 假如网络请求返回的数据结构是一个深层嵌套的Json 首先要通过key-value取出这个json中的数据源 // 将返回的json字符串转Dictory let json = """ { "name": "jack", "age": 20, "d

Swift中常见的String用法,Array高阶使用,Set集合操作

String字符串常见用法 生成字符串 创建字符串 let greeting = "Hello, world!" let name = String("John") 连接字符串:使用加号(+)或者字符串插值(使用())来将多个字符串连接起来。 var firstName = "John" let l