异变方法
值类型属性不能被自身的实例方法修改
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 脚本语言
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")
}
}
汇编基本指令
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中
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
4. @objc + dynamic: 消息派发的方式 , 支持runtime,方法转换