「这是我参与2022首次更文挑战的第10天,活动详情查看:2022首次更文挑战」。
- 本文主要介绍swift中如何使用
runtime
1. 回顾OC中runtime
-
什么是runtime 在OC中runtime本质是
底层的c,c++,汇编组成的API。将我们编译时确定的数据类型推迟到运行时确定,我们平时编写的OC的代码,需要用runtime来创建类和对象,进行消息发送和转发,最终都会转换为runtime的c语言代码。 -
为什么使用runtime?
- oc是一门
动态语言,主要体现在以下3个方面,因此需要运行时系统来处理编译后的代码- 动态类型:运行时确定
对象的类型 - 动态绑定:运行时确定对象的
调用方法 - 动态加载:运行时加载需要的
资源或者可执行代码
- 动态类型:运行时确定
runtime基本是用C语言和汇编语言写的,苹果和GNU各自维护一个开源的runtime版本,这两个版本之间都高度的保持一致。
- runtime有什么用?
- 进行方法交换 (method swizzling)
- 给分类添加属性
- 动态的注册,创建一个类以及销毁
- 消息的发送和转发
- 访问私有变量
- 模型转数组
2. swift中runtime
我们定义一个swift类,并定义它的方法和属性,之后通过我们在oc中获取方法列表和属性列表的方式打印
class Person {
var name = "jack"
func sleep() {
print("sleep")
}
}
do{
var methodCount:UInt32 = 0
let methodList = class_copyMethodList(Person.self, &methodCount)
for i in 0..<numericCast(methodCount){
if let method = methodList?[i] {
let methodName = method_getName(method)
print("methodName is \(methodName)")
}else{
print("not found method");
}
}
var properCount:UInt32 = 0
let proList = class_copyPropertyList(Person.self, &properCount)
for i in 0..<numericCast(properCount){
if let property = proList?[i] {
let propertyName = property_getName(property)
print("propertyName is \(propertyName)")
}else{
print("not found property");
}
}
}
最后什么也没打印,说明无法获取到,如果我们添加@objc在属性和方法前面,是否就具备了运行时的特性
就打印出来了,但是没有暴漏给oc,所以oc是无法使用的,这样做是没有意义的
没有任何关于Person的信息
- 我们把类继续
NSObject,并把@objc去除
只有析构方法,此时暴漏出来的只有init方法,有.cxx_destruct是因为我们的name是String类型一个对象。
我们换成常量的话就没有cxx_destruct了
查看提供给oc的方法只有init
- 我们要提供给
oc使用需要继承NSObject,同时属性和方法使用@objc修饰
- 我们把
@objec换成dynamic,还是不行
- 我们使用
@objc dynamic进行修饰即可
3.结论
- swift是一门
静态语言,而oc是动态语言,所以纯swift类是不具有runtime特性。是通过前面讲的方法调度:静态派发和动态派发。 - 对于
纯swift类,我们使用@objc修饰后的方法和属性具备了runtime特性,但是我们的oc中无法进行调度,只能swift中使用。 - 对于继承
NSObject的swift类,我们想要动态的获取属性和方法需要在属性和方法前面修饰@objc,否则也不具备runtime特性。 - 对于
继承自NSObject类来说,如果想要动态的获取当前属性+方法,必须在其声明前添加@objc关键字,如果想要使用方法交换,还必须在属性+方法前添加dynamic关键字,否则当前属性+方法只是暴露给OC使用,而不具备任何动态特性 - 若方法的参数或者属性类型为
swift特有的,无法映射到oc中的(比如Tuple,Character),则此方法的属性或者方法无法添加dynamic修饰(编译器报错)