Swift进阶杂谈2:值类型

864 阅读3分钟

本文主要介绍值类型

内存分区模型

为了更好的理解值类型,首先了解内存分区模型,在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。其中ageage2的地址 相差了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的改变而改变,主要是因为t1t之间是值传递,即t1和t是不同的内存空间,是直接将t中的值拷贝t1中。修改t1中的值,是修改的t1的内存空间,是不会影响t的内存空间的。

SIL验证

同样的,我们也可以通过分析SIL来验证结构体是值类型

  • SIL文件中,我们查看结构体的初始化方法,可以发现只有init,而没有malloc,在其中看不到任何关于堆区的分配。

总结

  • 结构体是值类型,且结构体的地址就是第一个成员的内存地址。
  • 值类型在内存中直接存储值
  • 值类型的赋值,是一个值传递的过程,即相当于拷贝了一个副本,存入不同的内存空间,两个空间彼此间并不共享状态
  • 值传递其实就是深拷贝