经过以前的分析,结构体是值类型
,类是引用类型
。那结构体和类的方法存储在哪里?咱们分析一下swift
值类型对象的函数的调用方式是静态调用
,即直接地址调用
,调用函数指针,这个函数指针在编译、连接完成后
,当前函数的地址就已经肯定了,拿在执行代码的过程当中就直接跳转到这个地址来执行当前对应的方法,存放在代码段,而结构体内部并不存放方法。所以能够直接经过地址直接调用
。数组
类中声明的方法是经过V-table
来进行调度的。 V-Table
在SIL
中的表示是这样的:安全
//声明sil vtable关键字
1 decl ::= sil-vtable
//sil vtable中包含 关键字、标识(即类名)、全部的方法
2 sil-vtable ::= 'sil_vtable' identifier '{' sil-vtable-entry* '}'
//方法中包含了声明以及函数名称
3 sil-vtable-entry ::= sil-decl-ref ':' sil-linkage? sil-function-na
me
复制代码
咱们经过一个简单的例子来看一下markdown
class LGTeacher {
func teach(){}
func teach2(){}
func teach3(){}
func teach4(){}
@objc deinit{}
init(){}
}
复制代码
经过SIL源文件
查看其在SIL中的v-table
,以下图所示 ide
由上图可知函数
sil_vtable
:关键字LGTeacher
:代表当前是LGTeacher
class的函数表数组
,声明在class内部
的方法在不加任何关键字修饰的过程当中,连续存放
在咱们当前的地址空间
中经过查看源码发现其内部是经过for循环编码
,而后offset+index
偏移,而后获取method,将其存入到偏移后的内存中,从这里能够印证函数是连续存放
的。编码
能够得出结论:对于class中函数
来讲,类的方法调度是经过V-Taable
,其本质就是一个连续的内存空间(数组结构)
。spa
其缘由是由于子类将父类的函数表所有继承
了,若是此时子类增长函数
,会继续在连续的地址中插入,假设extension函数也在函数表中
,则意味着子类也有,可是子类没法并无相关的指针记录函数是父类方法仍是子类方法,因此不知道方法该从哪里插入
,致使extension中的函数没法安全的放入子类中。因此在这里能够侧面证实extension中的方法是直接调用的,且只属于类,子类是没法继承的
。指针
子类也有其访问权限
,只是不能继承和重写
。final修饰code
final
修饰的方法是直接调度
的@objc修饰
@objc
关键字是将swift
中的方法暴露给OC,@objc
修饰的方法是函数表调度
。OC访问swift
,class须要继承NSObject
。dynamic修饰 使用dynamic
的意思是能够动态修改
,觉得着当继承自NSObject时,可使用method-swizzling
场景:swift中实现方法交换 在swift中的须要交换的函数前,使用dynamic修饰,而后经过:@_dynamicReplacement(for: 函数符号)
进行交换,以下所示
class PDTeacher: NSObject {
dynamic func teach(){ print("teach") }
func teach2(){ print("teach2") }
func teach3(){ print("teach3") }
func teach4(){ print("teach4") }
@objc deinit{}
override init(){}
}
extension PDTeacher{
@_dynamicReplacement(for: teach)
func teach5(){
print("teach5")
}
}
复制代码
将teach
方法替换成了teach5
struct
是值类型
,其中函数的调度属于直接调用地址
,即静态调度
。class
是引用类型
,其中函数的调度是经过V-Table函数表
来进行调度的,即动态调度
。extension
中的函数调度方式是直接调度
。final
修饰的函数调度方式是直接调度
。@objc
随时的函数调度方式是函数表调度
,若是OC中须要使用,class还必须继承NSObject
。dynamic
修饰的函数的调度方式是函数表调度
,是函数具备动态性。@objc + dynamic
组合修饰的函数调度,是执行的是objc_msgSend
流程,即动态消息转发
。