美文网首页
【二】Swift-类与结构体(下)

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

作者: 曾经像素有点低 | 来源:发表于2022-01-05 19:58 被阅读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等开发工具。

    • 得到的关键SIL文件信息如下:
    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

    • @inout 输入输出参数

    不同的地方就是:第二个方法的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
    
    • 使用swift代码解释
    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是合法的。

    相关文章

      网友评论

          本文标题:【二】Swift-类与结构体(下)

          本文链接:https://www.haomeiwen.com/subject/zekiqrtx.html