前言:
上篇文章分享了类
的属性
和属性观察者
,本篇文章主要分析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
修饰参数
总结
-
值类型
就是地址中存储
的是值
-
值类型
的传递,传递的是值
,类似于深拷贝
-
引用类型
就是地址中存储的是内存中所在地址
-
内存中所在
地址
中存储的才是值
-
值类型开辟的空间在
栈
中,能快速调用 -
引用类型开辟的空间在
堆
中