Swift 类和结构体(3)

异变方法

值类型属性不能被自身的实例方法修改

struct Point {

    var x = 0.0

    var y = 0.0

    func test() {

        print(x)

    }

    mutating func moveBy(x deltaX: Double, y deltaY: Double) {

        x += deltaX

        y += deltaY

    }

}

mutating关键字解析

转化为中间语言


shell 脚本执行工程

shell 脚本语言

swiftc -emit-sil -Onone -target x86_64-apple-ios14.2-simulator -sdk $(xcrun --show-sdk-path --sdk iphonesimulator) ${SRCROOT}/swiftDemo2/ViewController.swift > ./ViewController.sil && open ViewController.sil

ViewController.sil

还原sīl的混编字符串

xcrun swift-demangle s14ViewController5PointV4testyyF

// Point.test()

sil hidden @$s14ViewController5PointV4testyyF : $@convention(method) (Point) -> ()

debug_value %0 : $Point, let, name "self", argno 1 // id: %1

伪代码 let self = Point

// Point.moveBy(x:y:)

sil hidden @$s14ViewController5PointV6moveBy1x1yySd_SdtF : $@convention(method) (Double, Double, @inout Point) -> ()

debug_value_addr %2 : $*Point, var, name "self", argno 3 // id: %5

伪代码 var self = &Point

mutating是将传入的 self 被标记为 inout 参数。

方法调度

class LGTeacher {

   func teach() {

      print("teach")

   }

}


teach方法汇编断点示意图

汇编基本指令

bl 跳转到某地址(有返回)

blr 跳转到某地址(无返回)

mov x20, x0 将寄存到x0的值复制到寄存器20中

ldr x0, [x1, x2] 将寄存器x1和寄存器x2的值相加作为地址,取该地址的值放入到寄存器x0中

teach方法汇编断点分析

1. bl 0x100f3da28 返回一个LGTeacher实例对象

函数返回值是放在x0寄存器里面的,x0存放LGTeacher实例对象

2. ldr x8, [x0] 将x0的第一个8字节,放入到x8中


ldr x8, [x0]执行过

x8放入LGTeacher实例对象的 metadata

3. ldr x8, [x8, #0x50] LGTeacher的metadata + 50

4. bl x8 执行函数

teach函数的调用过程:找到 Metadata ,确定函数地址(metadata + 偏移量), 执行函数

类的方法是函数表调度

class LGTeacher {

   func teach() {

      print("teach")

   }

   func teach1() {

      print("teach1")

   }

   func teach2() {

      print("teach2")

   }

}

let t = LGTeacher()

      t.teach()

      t.teach1()

      t.teach2()


汇编示意图

 teach, teach1, teach2 方法是连续的,猜想放在函数表中的

通过sil文件验证

sil_vtable LGTeacher {

  #LGTeacher.teach: (LGTeacher) -> () -> () : @$s14ViewController9LGTeacherC5teachyyF // LGTeacher.teach()

  #LGTeacher.teach1: (LGTeacher) -> () -> () : @$s14ViewController9LGTeacherC6teach1yyF // LGTeacher.teach1()

  #LGTeacher.teach2: (LGTeacher) -> () -> () : @$s14ViewController9LGTeacherC6teach2yyF // LGTeacher.teach2()

  #LGTeacher.init!allocator: (LGTeacher.Type) -> () -> LGTeacher : @$s14ViewController9LGTeacherCACycfC // LGTeacher.__allocating_init()

  #LGTeacher.deinit!deallocator: @$s14ViewController9LGTeacherCfD // LGTeacher.__deallocating_deinit

}

类里面本身方法是函数表调度

扩展方法是静态分发

extension LGTeacher {

   func teach3() {

      print("teach3")

   }

}


汇编

直接调用内存地址

方法调度总结


方法调度总结

影响函数派发方式

1. final: 添加了 final 关键字的函数无法被重写,使用静态派发,不会在 vtable 中出现,且 对 objc 运行时不可见。 

class LGTeacher {

   final func teach() {

      print("teach")

   }

   func teach1() {

      print("teach1")

   }

}


静态派发

2. dynamic: 函数均可添加 dynamic 关键字,为非objc类和值类型的函数赋予动态性,但派发 方式还是函数表派发。 

class LGTeacher {

   dynamic func teach() {

      print("teach")

   }   

}

extension LGTeacher {

   @_dynamicReplacement(for: teach)

   func teach3() {

      print("teach3")

   }

}

3. @objc: 该关键字可以将Swift函数暴露给Objc运行时,依旧是函数表派发。 

与oc 混编

class LGTeacher: NSObject {

   @objc func teach() {

      print("teach")

   }   

}

1.继承NSObject

2. 加 @objc

查看桥接文件

1.

点击

2. ->Generated Interface


swift的LGTeacher类转化oc

4. @objc + dynamic: 消息派发的方式 , 支持runtime,方法转换

你可能感兴趣的:(Swift 类和结构体(3))