一、异变方法
我们知道,不管是Class还是Struct都能够定义方法,那么它们有区别吗?
分别定义一个Class和Struct,并给它们添加相同的方法,代码如下:
struct Person {
var age = 3
func modify(age: Int) {
self.age = age
}
}
class Teacher {
var age = 3
func modify(age: Int) {
self.age = age
}
}
编译时会发现Struct中的modify方法会报错,而class中并没有,这是因为值类型的属性不能被自身的实例方法修改。根据提示自动修复后即可编译成功,像这样:
很明显,
modify方法前多了一个关键字mutating,那么它的作用是什么?
二、mutating的作用
- 为了方便对比,我们继续在
Struct中添加一个test方法
struct Person {
var age = 3
mutating func modify(age: Int) {
self.age = age
}
func test() {
print(age)
}
}
-
分析SIL文件
从声明来看
modify方法多了一个关键字mutating -
分析
test方法的实现从这里可以看出test方法携带一个入参
Person,该入参就是Person实例的值。 注意debug_value %0 : $Person, let, name "self", argno 1是声明一个常量self存贮Person实例。 -
分析
modify方法的实现与
test相比,modify入参为@inout Person,该入参其实是Person实例的地址。 再看debug_value_addr %0 : $*Person, var, name "self", argno 1是在声明变量self存储Person实例的地址。从这不难看出modify是在对实例地址进行操作,而test仅仅是取值。
SIL 文档的解释 An @inout parameter is indirect. The address must be of an initialized object.(当前参数 类型是间接的,传递的是已经初始化过的地址)
三、演示代码
struct Person {
var age = 3
}
var p = Person()
let p1 = p
let p2 = withUnsafePointer(to: &p) { $0 }
p.age = 10
print("p1.age = ", p1.age)
print("p2.age = ", p2.pointee.age)
执行结果
p1.age = 3
p2.age = 10
Program ended with exit code: 0
由此可以看出,修改p的属性时,通过取地址的p2也被修改了,而直接取值的p1并不受影响。
四、总结
使用关键字mutating的方法称为异变方法,该方法会将入参中携带的当前实例self标记inout,后续该方法中任何对self的操作都是取self的地址进行操作,它会直接影响到外界依赖。
inout在swift中也是关键字,表示输入输出参数。该键字标记的参数可以在方法内部被修改(地址传递)。