【二】Swift-类与结构体(下)

2022-01-05  本文已影响0人  曾经像素有点低
code_小马swift

........................................................

目录:

........................................................

一、异变方法;

上一篇已经讲过:Swift中class和struct都能定义方法。但有一点区别的是默认情况下,值类型属性不能被自身的实例方法修改。

'self' is immutable.png

当前结构体中的self里存放的就是属性x和y的值。
虽然我们在moveBy中没有使用self.x、self.y方法,
但是当我们修改x或者y的时候,就相当于修改self本身
由于默认情况下,值类型属性不能被自身的实例方法修改。所以系统编译的时候会爆红警告。

//比如我们实例化一个对象p,调用moveBy方法的时候:
var p = Point()
p.moveBy(x: 8, y: 13)
//p修改x、y的时候,内部函数的修改,也修改了外部的p自身。

那么我们如果想要在结构体内部的函数中修改自身的值,就要在方法前边加上关键词mutating修饰。

增加关键词mutating
a.使用SIL探查mutating具体原理

SIL的具体使用方法已经在上一篇:Swift-类与结构体(下)中有详细的介绍。

//示例代码:
struct YGPoint {
    var x = 0.0, y = 0.0;
//普通方法
    func test() {
        let tmp = self.x
        print(tmp)
    }
//使用了mutating修饰的方法
    mutating func moveBy(x deltaX: Double , y deltaY: Double) {
        x += deltaX
        y += deltaY
    }
}

使用如下命令

swiftc -emit-sil ${SRCROOT}/xiaomaTest/main.swift > ./main.sil && open -a 'Visual Studio Code' 'main.sil'

xiaomaTest是项目根目录下的文件名,
Visual Studio Code 是用来打开main.sil文件的工具,这里也可以用Xcode等开发工具。

struct YGPoint {
  @_hasStorage @_hasInitialValue var x: Double { get set }
  @_hasStorage @_hasInitialValue var y: Double { get set }
  func test()
  mutating func moveBy(x deltaX: Double, y deltaY: Double)
  init()
  init(x: Double = 0.0, y: Double = 0.0)
}

/***************test()方法******************/
// YGPoint.test()
sil hidden @$s4main7YGPointV4testyyF : $@convention(method) (YGPoint) -> () {
// %0 "self"                                      // users: %2, %1
bb0(%0 : $YGPoint):
  debug_value %0 : $YGPoint, let, name "self", argno 1 // id: %1
  %2 = struct_extract %0 : $YGPoint, #YGPoint.x   // users: %11, %3
...
/*************** moveBy()方法******************/

// YGPoint.moveBy(x:y:)
sil hidden @$s4main7YGPointV6moveBy1x1yySd_SdtF : $@convention(method) (Double, Double, @inout YGPoint) -> () {
// %0 "deltaX"                                    // users: %10, %3
// %1 "deltaY"                                    // users: %20, %4
// %2 "self"                                      // users: %16, %6, %5
bb0(%0 : $Double, %1 : $Double, %2 : $*YGPoint):
  debug_value %0 : $Double, let, name "deltaX", argno 1 // id: %3
  debug_value %1 : $Double, let, name "deltaY", argno 2 // id: %4
  debug_value_addr %2 : $*YGPoint, var, name "self", argno 3 // id: %5
  %6 = begin_access [modify] [static] %2 : $*YGPoint // users: %15, %7
...

test()方法的 名称混编后变成s4main5PointV4testyyF
返回值是@convention(method)
test()方法的self参数对应的是Point

moveBy(x:y:)方法的 名称混编后变成s4main5PointV6moveBy1x1yySd_SdtF
返回值是@convention(method)
moveBy(x:y:)方法的参数x、y、self参数对应的是Double, Double, @inout Point

不同的地方就是:第二个方法的self参数多了一个@inout参数,
SIL文档中介绍《SIL基本语法介绍》
An @inout parameter is indirect. The address must be of an initialized object. (当前参数类型是间接的,传递的是已经初始化的地址)

也就是说,moveBy方法中的 @inout Point接收的是一个地址,
test()方法中的Point接收的是一个结构体的实例(self)也就是结构体具体的值

//Test()方法的第一行SIL代码
  debug_value %0 : $Point, let, name "self", argno 1 // id: %1
//翻译过来相当于:
let self = Point

//moveBy(x:y:)方法的第三行SIL代码
debug_value_addr %2 : $*Point, var, name "self", argno 3 // id: %5
//翻译过来相当于:
var self = *Point
var p = YGPoint()
//取值 -- 相当于没有加mutating的方法
let te1 = p    
//取地址相当于mutating修饰后的方法,有了@inout修饰
var te2 = withUnsafePointer(to: &p){return $0}
//取值
var te3 = p
//给P的x赋值,查看te1、te2、te3的x变化
p.x = 30.0

print("te1=",te1.x,"\nte2=",te2.pointee.x,"\nte3=",te3.x)
输出结果如下图所示: 1421641377880_.pic.jpg

我们可以看出修改实例变量p的里的x值,只有te2跟着变化了。

所以没有添加mutating的方法相当于取值,而mutating修饰的方法相当于取地址。(@inout关键词的作用就是传入当前的地址)所以此时我们修改self是合法的。

上一篇下一篇

猜你喜欢

热点阅读