inout 与属性
inout 的本质就是引用传递(地址传递)
存储型属性是传递属性地址,计算型属性和设置了属性观察器的存储型属性是传递副本的地址。
- 如果实参有物理内存地址,且没有设置属性观察器
- 直接将实参的内存地址传入函数(实参进行引用传递)
- 如果实参是计算属性 或者 设置了属性观察器
- 采用了 copy in copy out 的做法
- 调用该函数时。先复制实参的值,产生副本(get)
- 将副本的内存地址传入函数(副本进行引用传递),在函数内部可以修改副本的值
- 函数返回后,再将副本的值覆盖实参的值(set)
- 采用了 copy in copy out 的做法
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 与带有观察器的存储型属性
test(&s.side)
// willSetSide 20
// didSet
-
汇编分析:并不是直接将 s.side 的地址传递,因为直接传递地址覆盖地址中的值,只是 mov 操作。如果要触发观察器,需要 call setter方法,触发其中的 willSet、didSet
将 s.side 的值取出来赋予一个局部变量,将局部变量的地址传入 inout,进行修改。之后拿到修改过的值调用 Shape.side.setter,在 setter 方法中触发观察器 willSet、didSet。
进入 setter 方法,rdi并无做更改,里面存储的值为 20,作为 willSet的入参传入,所以 willSet 中 newValue 为20
inout 与计算型属性
test(&s.girth)
/**
先调用 get,将返回值地址记录到一个临时的局部比变量
调用 test 将保存的局部存储空间地址传递,修改其中的值为 num
调用 set,newValue 为 num,width = num / side
*/
// getGirth
// test
// setGirth 20
-
汇编分析: