类与结构体-初识
在Swift标准库中,绝大多数的公开类型都是结构体,而枚举和类只占很小一部分
比如Bool、Int、Double、String、Array、Dictionary等常见类型都是结构体
所有结构都有一个编译器自动生成的初始化器(initializer,初始化方法、构造器、构造方法)
struct Point{
var x: Int
var y: Int
}
var p1 = Point(x: 10, y: 20)
在上树代码中我们并没有定义一个方法传入X和Y的值,编译器自动帮我们生成了这个构造方法
结构体的初始化器:
编译器会根据情况,可能会为结构体生成多个初始化器,宗旨是:保证所有成员都要有初始化值
在x和y都没有值的情况下编译器会帮我们生成一个需要传入x,y的初始化方法
在Y有值的情况下,编译器会帮我们生成两个初始化方法
在x和y都有值的情况下,编译会帮我们生成以上四个初始化器,编译不会报错
struct Point{
var x: Int?
var y: Int?
}
var p1 = Point(x: 10, y: 20)
var p2 = Point(x: 10)
var p3 = Point(y: 10)
var p4 = Point()
我们编译一下代码可以得到一下结果
该代码不会报错,因为可选项有个默认值nil,结构体的所有属性都已经初始化完成,以上四个初始化器都生效
自定义初始化器
一旦在定义结构体是自定义了初始化器,编译器就不会再帮她自动生成其他初始化器
窥探初始化器的本质
struct Point{
var x: Int = 0
var y: Int = 0
}
var p1 = Point()
struct Point{
var x: Int
var y: Int
init() {
x = 0
y = 0
}
}
var p1 = Point()
以上两段代码完全等价,我们在p1的位置加个断点执行程序会到在汇编callq方法出停住,我们在控制台输入si就可以进入init的方法了,我们会看到两段代码都会进入以下汇编
发现两段汇编连内存地址都一模一样,大家可以尝试一下,说明我们自己写的初始化器和系统生成的初始化器一模一样
类
类的定义和结构体类似,但编译器并没有为类自动生成可以传入成员值的初始化器
如果类的所有成员都在定义的时候指定了初始值,编译器会为类生成无参的初始化器
查看以上两张图片,如果类内部的成员是有初始化值的,编译器会生成一个无参的初始化器,如果类内部成员没有初始化值,那么连这个无参的初始化器也不会生成成功,因为这种情况下是不安全的
结构体和类的本质区别
- 结构体是值类型(枚举也是值类型),类是引用类型(也就是指针类型)
- 结构体变量是在函数内部定义的那就在账上,如果是在外部定义的,那就在数据段,全局区,引用类型存储在堆空间(直接可以看汇编是否有alloc或者malloc 有就在堆,没有就在栈)
- 值类型赋值给var、let或者给函数传参,是直接将所有内容拷贝一份;类似于对文件进行copy、paste操作,产生了全新的文件副本,属于深拷贝
- rbp减某个值的基本就是局部变量
- 内存地址是rip+某个值的基本就是全局变量,(程序内存 中固定死的有且只有一份内存的就是全局变量)
- 内存地址格式为rax+某个值的,很有可能是堆空间
Lg: 在Swift标准库中,为了提升性能,String、Array、Dictionary、Set采用了copy on write
- 比如仅当有“写”操作时,才会真正执行拷贝
- 对于标准库值类型赋值操作,Swift能确保最佳性能,所以没必要为了保证最佳性能来避免赋值
- 建议:不需要修改的,尽量定义成let
- 栈空间内存地址是从高往低走,越先分配的内存地址越大
Lg:引用类型申请堆空间的过程: __allocating_init() -> swift_allocObject -> swift_slowAlloc -> malloc_zone_malloc