因为inout
关键字比较简单,因此该文章篇幅相对比较短小。
我们直到在swift
中inout
通常用来在函数内修改外部变量使用。那么其实我们也很容易联想到在函数内去修改class
对象的某个属性,修改完成后我们在函数外部去访问该属性即是最新的值。
但如果我们需要修改的是一个局部变量或者我们需要修改的变量是一个私有变量,该变量并未声明未public
那么此时我们在另一个类中需要修改其值时就比较麻烦了。那么此时我们可以通过inout
来实现,这样在不暴露私有变量的情况下即可走相同的逻辑。
class A {
private var a: Int = 1
func start() {
B.test(&a)
}
}
class B {
static func test(_ num: inout Int) {
num = 20
}
}
其实通过之前的汇编分析我们大致也可以直到inout
本质上应该是将变量对应的地址传入到了函数中,从而修改变量的值。 举个:
var age = 10
func test(_ num: inout Int) {
num = 20
}
test(&age)
此时断点在test(&age)
处,进入到汇编代码中。
可以看到在调用函数前往
rdi
中写入的是地址信息,而rdi
常用作传递函数参数。这里就不多赘述了,相信根据前面的文章理解这里相对来说是很容易的。
下面我们主要来探究一下属性的inout
使用
struct Test {
var a: Int
var b: Int {
willSet {
print("willset")
}
didSet {
print("didset")
}
}
var c: Int {
get {
return a * b
}
set {
b = a + 1
}
}
}
func test(_ num: inout Int) {
num = 20
}
let p = Test(a: 10, b: 20)
test(&p.a)
这里我们可以直接得到结论这边函数传入的就是属性a
的地址。
我们可以看到第
15
行的说明信息已经是很名曲额的将对象p
的地址放入到了rdi
中也就是将属性a
的地址(注意这里的对象是个结构体)传入到了函数中。
此时我们再将函数调用改为test(&p.c)
,也就是重写属性的getter
和setter
方法时。同样直接看汇编。
直接看第
18
,21
,24
行,可以看到现执行getter
获取到数据后并保存在一个地址中,然后调用test
函数时将该地址传入,后续调用setter
方法时在传入该地址。
再去通过汇编看属性观察器的实现其实也是大同小异,不同点则是在函数调用完成后再去调用属性的setter
方法,从而触发属性观察器的方法。