异变方法
上一节课我们了解到,Swift 中 class 和 struct 都能定义方法。但是有一点区别的是默认情况 下,值类型属性不能被自身的实例方法修改。
上面的代码错误,提示‘self is immutable’, 结构体self是值类型immutable,所以是不可以修改的。
如果需要修改需要加mutating 关键字。
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
self.x += deltaX
self.y += deltaY
}
}
mutating的作用是什么呢? 添加以下代码
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
func moveByTest(x deltaX: Double, y deltaY: Double) {
print("--\(self.x)")
print("--\(self.x)")
}
}
编译SIL,我们查看一下SIL文件,发现moveBy方法和moveByTest方法有如下区别:
// Point.moveBy(x:y:)
sil hidden @$s4main5PointV6moveBy1x1yySd_SdtF : $@convention(method) (Double, Double, @inout Point) -> ()
// Point.moveByTest(x:y:)
sil hidden @$s4main5PointV10moveByTest1x1yySd_SdtF : $@convention(method) (Double, Double, Point) -> ()
这两个方法,参数相同,区别是moveBy方法添加了mutating关键字,Point参数前有@inout标记,而moveByTest则没有。
SIL 文档的解释
An @inout parameter is indirect. The address must be of an initialized object.(当前参数类型是间接的,传递的是已经初始化过的地址)
异变方法的本质:对于变异方法, 传入的 self 被标记为 inout 参数。无论在 mutating 方法 内部发生什么,都会影响外部依赖类型的一切。
输入输出参数:
如果我们想函数能够修改一个形式参数的值,而且希望这些改变在函数结束之后 依然生效,那么就需要将形式参数定义为 输入输出形式参数 。在形式参数定义开始的时候在前边 添加一个 inout关键字可以定义一个输入输出形式参数
依照上面的解释,我们使用inout关键字自己实现一个mutating方法如下:
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
func moveByTest(x deltaX: Double, y deltaY: Double) {
print("--\(self.x)")
print("--\(self.x)")
}
func inoutTest(value: Double, point: inout Point) {
point.x += value
}
}
var p1 = Point()
var x1:Double = 100
p1.inoutTest(value: x1, point: &p1)
print(p1.x)
输出 100
最后做一个习题:
var p = Point()
let xx1 = p
var xx2 = withUnsafePointer(to: &p){ return $0 }
var xx3 = p
p.x = 999
print("xx1 = \(xx1.x)")
print("xx2 = \(xx2.pointee.x)")
print("xx3 = \(xx3.x)")
输出
xx1 = 0.0 // xx1为另一个值常量,和p没关系了
xx2 = 999.0 //xx2和p的内存指针,p修改,就是修改了xx2
xx3 = 0.0 // xx3为深度拷贝赋值,新的栈内存,和p也没关系了