异变方法
Swift中class和struct都能定义方法。但是有一点区别的是默认情况下,值类型属性不能被自身的实例方法修改。
下面代码运行时会报错的,这个是因为struct里变量xy被自身修改了。
当我们在其前面加了个mutating访问,其就可以进行自身的修改。这个我们探究源码发现,其因为加了mutating之后其传如的里面会有一个@inout Point,这个传人的是地址,不是值,所以其可以被修改。这个inout传递的就是当前的地址。
Swift中的方法调度
oc是通过objc_messagesend来的,我们来看下Swift中的调度,我们通过断点调试下,查看其内部汇编是如何操作的。
下面打印X8可以得到x0的第一个8字节,其是一个metdata,如下:
teach函数的调用过程:找到Metadata,确定函数地址(metadata+偏移量),执行函数。
swift里的方法是存在其类里面的函数表里,也就是sil_vtable,如下所示,因为我们调用了LGTeacher里的这三个方法。
如果我们把Class改成Struct,那方法是如何存储的呢,通过下面的汇编调试可以知道其就是一个地址调用(也就是直接通过bl指令调用 )。是因为值类型没有继承关系,定义的方法直接就是改结构体的,所以编译器会对其有个优化,直接进行调用。
当我们用extension去扩展一个类的话,其实其就是直接调用,不会插入到类里面,因为插入的话这样对内存消耗是十分大的,所以直接调用地址就可以,可以节约内存消耗。
下面我们写的例子也可以看出,在class里有teach,teach1,teach2,这些事连续的地址,存放在sil_vtable里的。用的extension定义的teach3,这个不是放在sil_vtable中,直接进行值存储的。
方法调度方式总结
NSObject子类和swift里的类一样,对于Struct里的结构体方法调度是值类型,所以有区别。
影响函数派发方式
1)final:添加了final关键字的函数无法被重写,使用静态派发,不会在vtable中出现,且对objc运行时不可见。
2)dynamic:函数均可添加dynamic关键字,为非objc类和值类型的函数赋予动态性,但派发方式还是函数表派发。
3)@objc:该关键字可以将Swift函数暴露给Objc运行时,依旧是函数表派发。(也就是转换成了oc的调用方式 )
4)@objc + dynamic:消息派发的方式(也就是可以调用runtime的API)