值类型
引用类型
- 创建一个对象
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关键字,调用时需要传入参数的地址