值类型
引用类型
- 创建一个对象
teacher2
,并将teacher
赋值给它
- 虽然新对象的地址不同,但他们所指向的堆区内存一致,所以他们操作的是
同一片内存空间
class
对象的赋值是浅拷贝
,进而得出class
是引用类型
Mutating & inout
- 在定义结构体时,在结构体的方法中不允许修改实例变量
- 主要是在方法中有个
self
,他是let类型,也就是此时的结构体不可变,如果改变age
,实质是改变结构体本身,所以在方法中修改成员变量的值会报错。
- 在方法之前添加
mutating
,就可以修改实例变量的值
- 方法添加上
mutating
后有了变化
-
- 参数中的
WSPerson
增加了inout
修饰
-
self
访问的是地址
-
self
是var
可变类型
- inout
- 参数添加
intout
后,则传入的参数就是地址,所以此时参数可以进行修改
方法调度
结构体
struct Person {
func speak() {
print(" Hello word ")
}
}
let person = Person()
person.speak()
- 在汇编中可以看到是直接
callq
函数地址,调用speak
方法,这种调用也被称作静态调用
,方法存储在了代码段
类
class Cat {
func sleep1() { print(" sleeping 1.. ") }
func sleep2() { print(" sleeping 2.. ") }
}
let ragdoll = Cat()
ragdoll.sleep1()
ragdoll.sleep2()
- 在
Sil
中的方法都存在vtable
中.
- 在
swift
源码中通过搜索initClassVTable
,得到以下代码:

extension
class Cat {
func sleep1() { print(" sleeping 1.. ") }
func sleep2() { print(" sleeping 2.. ") }
func sleep3() { print(" sleeping 3.. ") }
func sleep4() { print(" sleeping 4.. ") }
}
extension Cat {
func sleep5() { print(" sleeping 5.. ") }
}
class Ragdoll: Cat { }
var cat = Ragdoll()
cat.sleep5()
- 在
Sil
可以看到Ragdoll
继承了Cat
中其他方法,但并没有sleep5
方法。其实这个也比较好理解,假如sleep5
也在Cat
的vtable
里,那么Ragdoll
肯定也会继承过来,但如果子类要继续添加方法时,由于方法在vtable
中是通过指针平移的方式添加,所以此时编译器无法确定是在父类添加还是子类添加,所以是不安全的,那么extension
中的方法只能是直接调用
- 在汇编代码中也可以看到:
extension
中的方法调用是直接调用
的
final
对方法调度的影响
- 如果在类的方法前面添加 final, 那么方法会变为直接调用
final
修饰的方法是直接调用
的
@objc
对方法调度的影响
- 在类的方法前面添加
@objc
,虽然vtable
中有该方法,但是调度方式与上面累的普通方法不通, 这种叫做函数表调度
- 加
@objc
的方法不一定
能被OC
调用
- 1.想要
OC
调用,类必须继承NSObject
- 2.继承自NSObject后
Sil
中有两个sleep1
方法,一个给Swift
使用,带@objc
标记的供给OC
使用
dynamic
对方法调度的影响
dynamic
修饰的函数调度方式是函数表调度
- 方法交换:如果类继承
NSObject
,且用,则它可以进行method-swizzling
Swift
中的方法交换需要使用@_dynamicReplacement(for: 调用的函数符号)
函数
@objc+dynamic
总结
struct
是值类型
,它的函数调度是直接调用
,即静态调度
- 值类型在函数中如果要修改实例变量的值,则函数前面需要添加
Mutating
修饰
class
是引用类型
,它的函数调度是通过vtable函数
,即动态调度
extension
中的函数是直接调用
,即静态调度
final
修饰的函数是直接调用
,即静态调度
@objc
修饰的函数是函数表调度
,如果方法需要在OC
中使用,则类需要继承NSObject
dynamic
修饰的函数调度方式是函数表调度
,它是动态可以修改的,可以进行method-swizzling
@objc+dynami
修饰的函数是通过objc_msgSend
来调用的
- 如果函数中的
参数想要被更改
,则需要在参数的类型前面增加inout
关键字,调用时需要传入参数的地址