Swift inout 底层探究

inout 与属性

inout 的本质就是引用传递(地址传递)
存储型属性是传递属性地址,计算型属性和设置了属性观察器的存储型属性是传递副本的地址。

  • 如果实参有物理内存地址,且没有设置属性观察器
    • 直接将实参的内存地址传入函数(实参进行引用传递)
  • 如果实参是计算属性 或者 设置了属性观察器
    • 采用了 copy in copy out 的做法
      • 调用该函数时。先复制实参的值,产生副本(get)
      • 将副本的内存地址传入函数(副本进行引用传递),在函数内部可以修改副本的值
      • 函数返回后,再将副本的值覆盖实参的值(set)
struct Shape {
    var width: Int
    var side: Int {
        willSet { print("willSetSide", newValue) }
        didSet { print("didSet") }
    }
    var girth: Int {
        set { width = newValue / side; print("setGirth", newValue) }
        get { print("getGirth"); return width * side }
    }
    func show() {
        print("width=\(width), side =\(side), girth=\(girth)")
    }
}
func test(_ num: inout Int) { print("test"); num = 20 }
var s = Shape(width: 10, side: 4)

inout 与存储型属性

  • 汇编分析:直接将 Shape 地址(也是 Shape 中第一个变量的地址)传递给 inout


    inout 与存储型属性

inout 与带有观察器的存储型属性

test(&s.side)
// willSetSide 20
// didSet
  • 汇编分析:并不是直接将 s.side 的地址传递,因为直接传递地址覆盖地址中的值,只是 mov 操作。如果要触发观察器,需要 call setter方法,触发其中的 willSet、didSet
    将 s.side 的值取出来赋予一个局部变量,将局部变量的地址传入 inout,进行修改。之后拿到修改过的值调用 Shape.side.setter,在 setter 方法中触发观察器 willSet、didSet。


    inout 与带有观察器的存储型属性-1

    进入 setter 方法,rdi并无做更改,里面存储的值为 20,作为 willSet的入参传入,所以 willSet 中 newValue 为20


    inout 与带有观察器的存储型属性-2

inout 与计算型属性

test(&s.girth) 
/**
  先调用 get,将返回值地址记录到一个临时的局部比变量
  调用 test 将保存的局部存储空间地址传递,修改其中的值为 num
  调用 set,newValue 为 num,width = num / side
*/
// getGirth
// test
// setGirth 20
  • 汇编分析:


    inout 与计算型属性

你可能感兴趣的:(Swift inout 底层探究)