Swift基础-值类型、引用类型、方法

224 阅读3分钟

值类型

    1. 结构体开辟的内存在栈区
    1. 结构体的赋值是深拷贝

引用类型

  • 创建一个对象teacher2,并将teacher赋值给它
    • 虽然新对象的地址不同,但他们所指向的堆区内存一致,所以他们操作的是同一片内存空间
    • class对象的赋值是浅拷贝,进而得出class引用类型

Mutating & inout

  • 在定义结构体时,在结构体的方法中不允许修改实例变量
    • 主要是在方法中有个self,他是let类型,也就是此时的结构体不可变,如果改变age,实质是改变结构体本身,所以在方法中修改成员变量的值会报错。
    • 在方法之前添加 mutating,就可以修改实例变量的值
      • 方法添加上mutating后有了变化
        1. 参数中的WSPerson增加了inout修饰
        1. self访问的是地址
        1. selfvar可变类型
  • inout
    • 参数添加intout后,则传入的参数就是地址,所以此时参数可以进行修改

方法调度

结构体

struct Person {
    func speak() {
        print(" Hello word ")
    }
}

let person = Person()
person.speak()
  • 在汇编中可以看到是直接callq函数地址,调用speak方法,这种调用也被称作静态调用,方法存储在了代码段

class Cat {
    func sleep1() { print(" sleeping 1.. ") }
    func sleep2() { print(" sleeping 2.. ") }
}

let ragdoll = Cat()
ragdoll.sleep1()
ragdoll.sleep2()

  • Sil中的方法都存在vtable中.
  • swift源码中通过搜索initClassVTable,得到以下代码:
    • 主要是通过指针平移获取方法名,并关联imp

image.png

extension

class Cat {
    func sleep1() { print(" sleeping 1.. ") }
    func sleep2() { print(" sleeping 2.. ") }
    func sleep3() { print(" sleeping 3.. ") }
    func sleep4() { print(" sleeping 4.. ") }
}

extension Cat {
    func sleep5() { print(" sleeping 5.. ") }
}
class Ragdoll: Cat { }

var cat = Ragdoll()
cat.sleep5()
  • Sil可以看到Ragdoll继承了Cat中其他方法,但并没有sleep5方法。其实这个也比较好理解,假如sleep5也在Catvtable里,那么Ragdoll肯定也会继承过来,但如果子类要继续添加方法时,由于方法在vtable中是通过指针平移的方式添加,所以此时编译器无法确定是在父类添加还是子类添加,所以是不安全的,那么extension中的方法只能是直接调用
  • 在汇编代码中也可以看到:extension中的方法调用是直接调用

final对方法调度的影响

  • 如果在类的方法前面添加 final, 那么方法会变为直接调用
  • final修饰的方法是直接调用

@objc对方法调度的影响

  • 在类的方法前面添加@objc,虽然vtable中有该方法,但是调度方式与上面累的普通方法不通, 这种叫做函数表调度
  • @objc的方法不一定能被OC调用
    • 1.想要OC调用,类必须继承NSObject
    • 2.继承自NSObject后Sil中有两个sleep1方法,一个给Swift使用,带@objc标记的供给OC使用

dynamic对方法调度的影响

  • dynamic修饰的函数调度方式是函数表调度
  • 方法交换:如果类继承NSObject,且用,则它可以进行method-swizzling
    • Swift中的方法交换需要使用@_dynamicReplacement(for: 调用的函数符号)函数

@objc+dynamic

  • 方法的调用方法变成了objc_msgSend

总结

  • struct值类型,它的函数调度是直接调用,即静态调度
    • 值类型在函数中如果要修改实例变量的值,则函数前面需要添加Mutating修饰
  • class引用类型,它的函数调度是通过vtable函数,即动态调度
  • extension中的函数是直接调用,即静态调度
  • final修饰的函数是直接调用,即静态调度
  • @objc修饰的函数是函数表调度,如果方法需要在OC中使用,则类需要继承NSObject
  • dynamic修饰的函数调度方式是函数表调度,它是动态可以修改的,可以进行method-swizzling
    • @objc+dynami修饰的函数是通过objc_msgSend来调用的
  • 如果函数中的参数想要被更改,则需要在参数的类型前面增加inout关键字,调用时需要传入参数的地址