前言:
上篇文章分享了类的属性和属性观察者,本篇文章主要分析swift中的数据类型:
- 值类型
- 引用类型
在分析数据类型前,先了解下内存的存储的5大区域,按照地址从高到低排列: 栈区 -> 堆区 -> 全局静态区 -> 常量区 -> 代码区
其中栈区 是从高地址往低地址分配空间,堆区 、全局静态区、 常量区、代码区 都是从低地址往高地址分配空间,开发中的溢出是指堆栈溢出,可以理解为栈区与堆区边界碰撞的情况
值类型
有了以上对内存的认知,引入什么是值类型.
在xcode中定义一个方法,并调用
查看age的内存情况,从图中可以看出,栈区直接存储的是值,断点继续运行
查看age2的情况,从下图中可以看出,age2的赋值相当于将age中的值拿出来,赋值给了age2。其中age 与 age2 的地址 相差了8字节,从这里可以说明栈空间是连续的、且是从高到低的
由此可见值类型 特点
- 地址中存储的是
值 - 值类型的传递过程中,相当于
传递了一个副本,也就是所谓的深拷贝 - 值传递过程中,并不共享状态
结构体
创建结构体
更改结构体属性的值
结构体的SIL分析
进入init方法
在SIL文件中,我们查看结构体的初始化方法,可以发现只有init,而没有malloc,在其中看不到任何关于堆区的分配.所以结构体是值类型,且结构体的地址就是第一个成员的内存地址
引用类型
创建一个结构体和类打印内存进行对比
查看内存存储情况
SIL源码分析
- 查看结构
- 查看main函数结构
- 查看初始化方法
断点内存调试
- 打印内存地址
- 查看内存存储情况
关键字mutaing
- 现象分析
通过结构体定义一个栈,主要有push、pop方法,此时我们需要动态修改栈中的数组
定义一个结构体,并添加个调用方法
查看SIL源码
修改方法
系统不报错,但是数据没有添加到数组中,原因是因为虽然我们将它转成了变量但是结构体是值传递,修改方法里面的结构体不会对外面有影响
查看SIL源码
- 查看mutating的本质
根据**错误提示**,应使用**mutaing关键字**进行修饰
查看mutating的本质
由此可看出push添加mutating(只用于值类型)后,本质上是给值类型函数添加了inout关键字,相当于在值传递的过程中,传递的是引用(即地址)
inout关键字
在函数的声明中,默认的参数都是不可变的,如果想要直接修改,需要给参数加上inout关键字
- 总结
-
结构体中的函数如果想修改其中的属性,需要在函数前加上
mutating,而类则不用 -
mutating本质也是加一个inout修饰的self -
Inout相当于取地址,可以理解为地址传递,即引用 -
mutating修饰方法,而inout修饰参数
总结
-
值类型就是地址中存储的是值 -
值类型的传递,传递的是值,类似于深拷贝 -
引用类型就是地址中存储的是内存中所在地址 -
内存中所在
地址中存储的才是值 -
值类型开辟的空间在
栈中,能快速调用 -
引用类型开辟的空间在
堆中