结构体
通常写法
//第一种写法
struct Teacher {
var age: Int = 18
func teach(){
print("teach")
}
}
var t = Teacher()
//第二种写法
struct Teacher {
var age: Int
func teach(){
print("teach")
}
}
var t = Teacher(age: 18)
- 在结构体中,如果不给属性默认值,编译是不会报错的。即在结构体中属性可以赋值,也可以不赋值
-
init
方法可以重写,也可以使用系统默认的
image.png
结构体的SIL分析
如果没有init
,系统会提供不同的默认初始化方法
struct Teacher {
@_hasStorage @_hasInitialValue var age: Int { get set }
func teach()
init()
init(age: Int = 20)
}
如果提供了自定义的init
,就只有自定义的
struct Teacher {
@_hasStorage var age: Int { get set }
func teach()
init(age: Int)
}
为什么结构体是值类型?
image.png- 打印t:po t,可以发现,t的打印直接就是值,没有任何与地址有关的信息
- 获取t的内存地址,并查看其内存情况
- 获取地址:
po withUnsafePointer(to: &t, {print($0)})
- 查看内存情况:
x/8g 0x0000000100008048
image.png
- 获取地址:
那么思考一下:此时将t赋值给t1,如果修改了t1,t会发生改变吗?
- 直接打印
t
和t1
,可以发现t
并没有因为t1
的改变而改变,主要是因为因为t1
和t
之间是值传递,即t1
和t
是不同内存空间,是直接将t
中的值拷贝至t1
中。t1
修改的内存空间,是不会影响t
的内存空间的
image.png
也可以通过SIL
来分析结构体是值类型
- 在
SIL
文件中,我们查看结构体的初始化方法,可以发现只有init
,而没有malloc
,在其中看不到任何关于堆区的分配
image.png
总结:
结构体是值类型
,且结构体的地址就是第一个成员的内存地址
值类型
在内存中直接存储值
值类型
的赋值,是一个值传递的过程,即相当于拷贝了一个副本
,存入不同的内存空间,两个空间彼此间并不共享状态
值传递
其实就是深拷贝
引用类型
类
//类的常用写法
class Teacher {
var age: Int = 18
func teach(){
print("teach")
}
init(_ age: Int) {
self.age = age
}
}
var t = Teacher.init(20)
//写法二
class Teacher {
var age: Int?
func teach(){
print("teach")
}
init(_ age: Int) {
self.age = age
}
}
var t = Teacher.init(20)
- 在类中,如果属性没有赋值,也不是可选项,编译会报错
- 类需要自己实现 init()方法
为什么类是引用类型?
看如下代码
struct Teacher {
var age: Int = 18
var age2: Int = 20
func teach(){
print("teach")
}
}
class Teacher1 {
var age: Int = 20
func teach(){
print("teach")
}
init(_ age: Int) {
self.age = age
}
}
var t = Teacher()
var t1 = Teacher1.init(20)
类初始化的对象t1,存储在全局区
-
打印
image.pngt1
、t
:po t1
,从图中可以看出,t1
内存空间中存放的是地址,t
中存储的是值
-
获取
t1
变量的地址,并查看其内存情况- 获取t1指针地址:
po withUnsafePointer(to: &t1){print($0)}
- 查看t1全局区地址内存情况:
x/8g 0x00000001000081f0
- 查看t1地址中存储的堆区地址内存情况:
x/8g 0x00000001010485e0
image.png
- 获取t1指针地址:
引用类型特点:
-
地址中存储的是堆区地址
-
堆区地址中存储的是值
那么此时将t1
赋值给t2
,如果修改了t2
,会导致t1
修改吗?
- 通过
lldb
调试得知,修改了t2
,会导致t1
改变,主要是因为t2
、t1
地址中都存储的是 同一个堆区地址,如果修改,修改是同一个堆区地址,所以修改t2
会导致t1
一起修改,即浅拷贝
image.png
如果结构体中包含类对象,此时如果修改t1中的实例对象属性,t会改变吗?
代码如下:
class Teacher1 {
var age: Int = 20
func teach(){
print("teach")
}
}
struct Teacher {
var age: Int = 18
var age2: Int = 20
var teacher: Teacher1 = Teacher1()
func teach(){
print("teach")
}
}
var t = Teacher()
var t1 = t
t1.teacher.age = 30
print("t1.teacher.age = \(t1.teacher.age)")
print("t.teacher.age = \(t.teacher.age)")
*****打印结果:*****
t1.teacher.age = 30
t.teacher.age = 30
从打印结果中可以看出,如果修改t1
中的实例对象属性,会导致t
中实例对象属性的改变。虽然在结构体中是值传递,但是对于teacher
,由于是引用类型,所以传递的依然是地址
- 通过
lldb
调制,获取t
变量的地址,并查看其内存情况- 获取t指针地址:
po withUnsafePointer(to: &t1){print($0)}
- 查看t全局区地址内存情况:
x/8g 0x0000000100008208
- 查看t地址中存储的堆区地址内存情况:
x/8g 0x0000000100706050
- 获取t指针地址:
【注意】:因此在编写代码过程中应避免值类型包含引用类型
打印teacher的引用计数:
因为:
-
main中retain一次
-
teacher.getter方法中retain一次
-
teacher.setter方法中retain一次
通过以上总结:
-值类型
,相当于一个本地excel
,当我们通过QQ传给你一个excel时,就相当于一个值类型,你修改了什么我们这边是不知道的
-
引用类型
,相当于一个在线表格
,当我们和你共同编辑一个在先表格时,就相当于一个引用类型,两边都会看到修改的内容
网友评论