Swift类与结构体--异变方法

1,322 阅读2分钟

异变方法

上一节课我们了解到,Swift 中 class 和 struct 都能定义方法。但是有一点区别的是默认情况 下,值类型属性不能被自身的实例方法修改。

image.png 上面的代码错误,提示‘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也没关系了