Swift之类和结构体(下)

154 阅读2分钟

1. 异变方法

Swift中classstruct都能定义方法。但是默认情况下sturct(值类型属性)不能被自身的实例方法修改。

image.png

  • mutating image.png 为什么添加了mutating就可以了呢,下面我们通过SIL来瞧一瞧。

image.png

image.png 传入的参数有两个,一个是Int,一个是默认参数当前的实例对象。带有mutating关键字的方法中多了inout关键字,并且传入的”self“是指针类型。用伪代码表示的是下面的形式:

let self = Person

var self = &Person

这样我们就可以得到一个结论

对于变异方法, 传入的实例对象(默认参数)被标记为 inout 参数。所以无论在 mutating 方法内部发生什么,都会影响外部依赖类型的一切。

  • inout image.png 函数的形参都是let类型的,这样我们就修改不了,这时候我们就需要添加inout关键字。

image.png 需要注意的是这里传入的参数是取地址

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()
    }
}

汇编代码如下 image.png 7658这个x8就是eat()了。总结一下。

函数的调用过程:首先找到metadata,然后找到函数的地址(metadataAddress + 偏移量),执行函数。 而且我们还看到每个方法相差了8字节,说明他们在内存地址中是连续的。接下来我们看一下SIL文件。

image.png 可以看到函数都保存在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不管是ClassStructEnum 都有自己的 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
}