Swift runtime
[TOC]
前言
我的另一篇文章关于Objective-C
iOS Runtime简介,可以参考着阅读。
我们都知道Objective-C
是一门动态语言,其动态特性主要依赖于runtime
。但是Swift
是一门静态语言,那么我们常常说起的Swift runtime
是怎么回事呢?其实纯Swift
是没有动态性的,Swift runtime
是指Swift
中的属性或者方法在一些修饰符修饰,继承自NSObject
的时候,可以使用runtime API
进行相关的调用,下面我们就来详细的分析一下。
1. Runtime API
本章节不是介绍Runtime API
的,而是通过Runtime API
获取Swift
类中的方法和属性。
1.1 初步探索
首先我们创建一个macOS
命令行工程,语言选择Swift
,然后在main.swift
中定义一个Swift
类,代码如下:
class Teacher {
var age: Int = 18
func teach(){
print("teach")
}
}
然后编写一个test
方法,在该方法中使用Runtime API
去获取上面定义的类方法和属性列表。(注意在Swift
中使用Runtime
的时候是不需要import
的)
func test(){
var methodCount:UInt32 = 0
let methodlist = class_copyMethodList(Teacher.self, &methodCount)
for i in 0..<numericCast(methodCount) {
if let method = methodlist?[i]{
let methodName = method_getName(method);
print("方法列表:\(String(describing: methodName))")
}else{
print("not found method");
}
}
var count:UInt32 = 0
let proList = class_copyPropertyList(Teacher.self, &count)
for i in 0..<numericCast(count) {
if let property = proList?[i]{
let propertyName = property_getName(property);
print("属性列表:\(String(utf8String: propertyName)!)")
}else{
print("not found property");
}
}
print("test run")
}
test()
运行,打印结果:
image根据打印结果我们可以知道,此时并没有打印任何方法和属性的列表。
接下来我们修改一下我们的类:
class Teacher {
@objc var age: Int = 18
@objc func teach(){
print("teach")
}
}
image
此时我们可以看到即打印了方列表也打印了属性列表。但是在Swift 方法(函数)调度我们提到过,如果在Swift
类中只是添加了@objc
修饰并不能OC
中被调用。如果需要在OC
中调用还需要继承自NSObject
,那么我们修改代码如下:
class Teacher: NSObject {
var age: Int = 18
func teach(){
print("teach")
}
}
image
此处只是打印了一个init
方法,我们再次修改类
class Teacher: NSObject {
@objc var age: Int = 18
@objc func teach(){
print("teach")
}
}
运行,打印结果:
image
此时我们可以看到我们想要的都打印了,其实为了在Swift
方法中具有动态性,还可以给方法添加dynamic
关键字进行修饰。打印结果与上面是一样的。关于方法的调度加了什么关键字会有不同的调度方式,具体还是去参考我的另一篇文章Swift 方法(函数)调度
说了这些有什么用呢?其实从最开始的纯Swift
类的什么都没打印到最后打印了方法列表和属性列表,我们正在一步一步的引入Runtime
在Swift
中怎么使用,以及其关联性。
1.2 在objc源码工程中调试
下面我们打开一份可编译的objc
源码,没有的可以去LGCooci的GitHub下载objc4_debug。由于升级了Xcode
此时我使用的是objc4-881.2
新建一个SwiftTest Target
,语言选择Swift
,将刚才的代码粘贴过去。运行,查看打印结果与刚才的一致。
这里将代码修改成纯Swift
代码,然后跟一下源码,以获取方法列表为例,获取属性也差不多。首先搜索class_copyMethodList
,在objc-runtime-new.mm
文件中,添加如下断点。
注意:首先给我们的test
方法中的class_copyMethodList
那行代码添加断点,待执行到那里的时候在打开上图所示的断点。到上图所示的断点后,我们查看一下cls
然后点击data()
跳转进去,在里面我们就可以看到objc_class
的源码,这里的superclass
就是指向父类的指针,cache
是方法调度时的缓存,bits
中通过ro
和rw
存储着方法和属性。关于这些可以参考我的其他关于Objective-C
底层原理分析的文章。iOS OC 类原理
这里我们就打印一下superclass
,结果如下:
我们可以看到,在一个Swift
类中,如果什么都不继承,它的默认基类是SwiftObject
。返回class_copyMethodList
函数中,我们看获取到的方法列表。
我们可以看到这个methods
是个二维数组,至于里面为什么都是0呢?因为我修改类的时候没加@objc
所以取不到,然后就都是0了。加上@objc
就有值了。
2. SwiftObject
在上文中我们发现,在一个什么都不继承的Swift
类中,默认继承的基类是SwiftObject
,下面我们就去Swift
源码中搜索一下。
SWIFT_RUNTIME_EXPORT @interface SwiftObject<NSObject> {
@private
Class isa;
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
}
// The members of the HeapObject header that are not shared by a
// standard Objective-C instance
#define SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS \
InlineRefCounts refCounts
在SwiftObject.h
文件中我们找到了上面的代码,我们可以看到这里是遵守了NSObject
协议的。而且在Swift
源码中也有一个TargetAnyClassMetadata
有着部分与OC
中objc_class
类似的结构。
因为Swift
是一门替代Objective-C
的语音,所以有着与OC
相似的底层也是很正常的,毕竟要慢慢过渡,两者之间互通是必要的过程。
另外在objc
源码中我们也可以看到很多Swift
的身影,比如swift_class_t
就是一个很典型的例子。继承自objc_class
,又有着很多Swift
特有的属性。
struct swift_class_t : objc_class {
uint32_t flags;
uint32_t instanceAddressOffset;
uint32_t instanceSize;
uint16_t instanceAlignMask;
uint16_t reserved;
uint32_t classSize;
uint32_t classAddressOffset;
void *description;
// ...
void *baseAddress() {
return (void *)((uint8_t *)this - classAddressOffset);
}
};
那么我们不禁会有一个疑问,既然底层是能够互通的,那么为什么在OC
中使用Swift
类的时候还要集成自NSObject
?
其实就是通过NSObject
声明后,编译器才能判断当前的类是一个与OC
交互的类。
3. 总结
关注Swift Runtime
就分析这么多了,其实应用起来跟OC
的差别也不大,下面做个简单的总结:
- 对于纯
Swift
来说是没有动态特性的,因为Swift
是一门静态语言 - 方法和属性在不加任何修饰符的情况下不具备所谓的
Runtime
特性 -
Swift
中的方法调度主要是函数表调度V-Table
- 对于纯
Swift
类,我们给方法和属性添加@objc
标识的情况下,可以通过Runtime API
拿到方法和属性列表,但是还不能在OC
中调度 - 对于继承自
NSObject
的类来说,如果想要动态的获取当前属性和方法,必须在其声明前添加@objc
关键字 - 如果需要使用方法交换需要添加
dynamic
标识。如果不添加则不能通过Runtime API
进行调用 - 在
swift
底层源码中有一个SwiftObject
类有着与OC
类似的底层结构,在objc
源码中也有一个swift_class_t
结构体,继承自objc_class
,因此swift
与oc
能够有良好的交互
网友评论