本文主要介绍值类型
内存分区模型
为了更好的理解值类型,首先了解内存分区模型,在iOS中,内存主要分为 栈区、 堆区、 全局区、 常量区、 代码区五大区域。如下图所示
如图所示
- 栈区的地址比堆区的地址大。
- 栈是从高地址
->低地址,向下延伸,由系统自动管理,是一片连续的内存地址。 - 堆是从低地址
->高地址,向上延伸,由程序员管理,堆空间结构类似于链表,是不连续的。 - 日常开发中的溢出是指
堆栈溢出,可以理解为栈区与堆区边界碰撞的情况。 - 全局区、常量区都存储在__TEXT cString段。
值类型
值类型的特点
- 1、地址中存储的是
值。 - 2、值类型的传递过程中,相当于
传递了一个副本,也就是所谓的深拷贝。 - 3、值传递过程中,并不共享状态
举个例子
func test() {
var age = 18
var age2 = age
age = 30
age2 = 45
print("age=\(age),age2=\(age2)")
}
test()
断点查看age的栈区地址与内存情况
- 获取age的栈区地址:
po withUnsafePointer(to: &age){print($0)} - 查看age内存情况:
x/8g 0x00007ffeefbff410同样查看age2的栈区地址与内存情况
可以看出,age2的赋值相当于将age中的值拿出来,赋值给了age2。其中age与 age2的地址 相差了8字节,从这里可以说明栈空间是连续的、且是从高到低的。
结构体是值类型
定义一个结构体,并进行分析
struct PDTeacher {
var age : Int = 16
var age2: Int = 20
}
var t = PDTeacher()
print("end")
- 打印t:
po t,可知,打印出来t就是值,没有任何与地址有关的信息 - 获取t的内存地址,并查看其内存情况
此时将t赋值给t1,如果修改了t1,t会发生改变吗?
- 直接打印t及t1,可以发现t并没有因为t1的改变而改变,主要是因为
t1和t之间是值传递,即t1和t是不同的内存空间,是直接将t中的值拷贝至t1中。修改t1中的值,是修改的t1的内存空间,是不会影响t的内存空间的。
SIL验证
同样的,我们也可以通过分析SIL来验证结构体是值类型
- 在
SIL文件中,我们查看结构体的初始化方法,可以发现只有init,而没有malloc,在其中看不到任何关于堆区的分配。
总结
结构体是值类型,且结构体的地址就是第一个成员的内存地址。- 值类型在内存中直接
存储值。 - 值类型的赋值,是一个
值传递的过程,即相当于拷贝了一个副本,存入不同的内存空间,两个空间彼此间并不共享状态。 值传递其实就是深拷贝。