1. 异变方法
Swift中class和struct都能定义方法。但是默认情况下sturct(值类型属性)不能被自身的实例方法修改。
mutating为什么添加了
mutating就可以了呢,下面我们通过SIL来瞧一瞧。
传入的参数有两个,一个是Int,一个是默认参数当前的实例对象。带有mutating关键字的方法中多了
inout关键字,并且传入的”self“是指针类型。用伪代码表示的是下面的形式:
let self = Person
var self = &Person
这样我们就可以得到一个结论
对于变异方法, 传入的实例对象(默认参数)被标记为 inout 参数。所以无论在 mutating 方法内部发生什么,都会影响外部依赖类型的一切。
inout函数的形参都是let类型的,这样我们就修改不了,这时候我们就需要添加inout关键字。
需要注意的是这里传入的参数是取地址
2. 方法调度
众所周知OC中是objc_msgsend消息机制来调度方法,那么swift中是怎么实现的呢。
class Person{
func eat(){
print("吃")
}
func drink(){
print("喝")
}
func play(){
print("玩")
}
}
class ViewController: UIViewController{
overrid* func viewDidLoad() {
let p = Person()
p.eat()
p.drink()
p.play()
}
}
汇编代码如下
7658这个x8就是
eat()了。总结一下。
函数的调用过程:首先找到metadata,然后找到函数的地址(metadataAddress + 偏移量),执行函数。 而且我们还看到每个方法相差了8字节,说明他们在内存地址中是连续的。接下来我们看一下
SIL文件。
可以看到函数都保存在vtable(函数表)中,所以方法调度是
基于函数表的调度。那vtable保存在哪呢?上篇文章我们说到了Metadata的结构
struct Metadata{
var kind: Int
var superClass: Any.Type
var cacheData: (Int, Int)
var data: Int
var classFlags: Int32
var instanceAddressPoint: UInt32
var instanceSize: UInt32
var instanceAlignmentMask: UInt16
var reserved: UInt16
var classSize: UInt32
var classAddressPoint: UInt32
var typeDescriptor: UnsafeMutableRawPointer
var iVarDestroyer: UnsafeRawPointer
}
这里我们有一个东西需要关注typeDescriptor不管是Class、Struct、Enum 都有自己的 Descriptor,就是对类的一个详细描述。我们在swift源码中还原一下typeDescriptor的结构。
在Metadata.h 文件中找到 Description ,可以判断出是TargetClassDescriptor(ClassDescriptor)类型,然后在GenMeta.cpp文件中找到layout,从而推断出TargetClassDescriptor的结构体大致是
struct TargetClassDescriptor{
var flags: UInt32
var parent: UInt32
var name: Int32
var accessFunctionPointer: Int32
var fieldDescriptor: Int32
var superClassType: Int32
var metadataNegativeSizeInWords: UInt32
var metadataPositiveSizeInWords: UInt32
var numImmediateMembers: UInt32
var numFields: UInt32
var fieldOffsetVectorOffset: UInt32
var Offset: UInt32
var size: UInt32
//V-Table
}