总结
结构体是值类型,存放在栈空间中
类是引用类型,指针存放在栈空间,指向堆空间。
针对 64bit 环境:
在 Point 中 一个 Int 占8个字节
0x90000 指针在栈中占用8个字节,指向 Size 在堆中的地址,占用32个字节,其中前2个8字节有特殊的用处,第一个存放指向类型的信息,第二个是引用计数。
结构体是值类型
值类型赋值给 var、let或者给函数传参,是直接将所有的内容拷贝一份。
类似于对文件进行copy,产生了全新的文件副本。属于深拷贝。
从反编译的结果来看,p2 = p1 实际上是开辟了新的内存地址,证实是深拷贝,所以修改 p2 的值不会影响 p1。
类是引用类型
引用赋值给 var let 或者给函数传参,是将内存地址拷贝一份
类似于制作一个文件的替身(快捷方式、链接),指向的是同一个文件。属于浅拷贝。
对象的堆空间申请过程
在MAC、iOS中的
malloc
函数分配的内存大小总是16的倍数
在Swift中,创建类的实例对象,要向堆控件申请内存,大概流程如下:
Class.__allocating_init()
libswiftCore.dylib: \_swift_allocObject
libswiftCore.dylib: swift_slowAlloc
libsystem_malloc.dylib: malloc
通过class_getInstanceSize
可以得知:类的对象至少需要占用多少内存
赋值过程
找到初始化函数,返回值一般存于 rax 中,类的初始化方法返回值应为申请堆空间的地址
register read rax
找到堆空间地址,在 View Memery 中查看内存分配情况
第1个8位存的是指向类型信息,第2个8位存的是引用计数信息,之后2个8位存的是初始值10,11
之后找立即数12,13 也就是找$0xc,$0xd
能看出分别是存于 rax+0x10 和 rax+0x18,能推出分别对应 s2.width、s2.height,均是 rax 为地址的第一位,所以得出结论 s1 和 s2存的是同一份地址。
值与引用类型的 let
p = Point(x:3, y:4)
p.x = 10 p.y = 10
- 错误的原因为 p 是值类型,p 的内存占16个字节,存储 x,y
let 代表这16个字节的内容无法变更。
s = Size(20, 21)
- 错误的原因为 s 是引用类型常量,存储的是指向堆空间的指针
let 代表这个指针无法变更。
s.width = 20 s.height = 20
- 正确的原因是,修改的是指针所指向的堆空间内容,可以变更