结构体
- 在 Swift 标准库中,绝大多数的公开类型都是结构体,而枚举和类只占很小一部分
- 比如Bool、Int、Double、 String、Array、Dictionary等常见类型都是结构体
- 在第6行调用的,可以传入所有成员值,用以初始化所有成员(存储属性,Stored Property)
结构体的初始化器
编译器会根据情况,可能会为结构体生成多个初始化器,宗旨是:保证所有成员都有初始值
上面的Point在声明时没有为先x, y初始化值,p1给x和y赋值了,p2,p3,p4没有为x,y赋全值
x有初始化值,y没有初始化值
y有初始化值,x没有初始化值
x,y都有初始化值
- 下面的代码能编译通过吗?
因为可选项都有个默认值nil,因此可以编译通过
结构体的初始化器
- 一旦在定义结构体时自定义了初始化器,编译器就不会自动再帮它自动生成其他初始化器
这时不会默认初始化值,需要传入,否则报错
- 以下两段代码完全等效
- 结构体的内存结构
类
- 类的定义和结构体类似,但编译器并没有为类自动生成可以传入成员值的初始化器
由上可得:类的属性一定要有初始化值
- 如果类的所有成员都在定义的时候指定了初始值,编译器会为类生成无参的初始化器,成员的初始化是在这个初始化器中完成的
- 上面2段代码是完全等效的
结构体与类的本质区别
结构体是值类型(枚举也是值类型),类是引用类型(指针类型)
由上可以看出:point也就是结构体存储在栈空间,size也就是类存储在堆空间
上图都是针对64bit环境
值类型
- 值类型赋值给var、let或者给函数传参,是直接将所有内容拷贝一份, 类似于对文件进行copy、paste操作,产生了全新的文件副本。属于深拷贝(deep copy)
由于是值类型,互不影响,所以p1.x = 10, p1.y = 20
值类型的赋值
- 在Swift标准库中,为了提升性能,String、Array、Dictionary、Set采取了Copy On Write的技术
- 比如仅当有“写”操作时,才会真正执行拷贝操作
- 对于标准库值类型的赋值操作,Swift 能确保最佳性能,所有没必要为了保证最佳性能来避免赋值
- Swift标准库中的结构体为保持性能,只有在结构体被赋值并且有写入的时候才进行深拷贝,但是自己定义的结构体,只要结构体有被赋值操作就会深拷贝。
- 建议:不需要修改的,尽量定义成let
引用类型
- 引用赋值给var、let或者给函数传参,是将内存地址拷贝一份
- 类似于制作一个文件的替身(快捷方式、链接),指向的是同一个文件。属于浅拷贝(shallow copy)
属于浅拷贝,s1.width = 11, s2.height = 22
对象的堆空间的申请过程
- 在Swift中,创建类的实例对象,要向堆空间申请内存,大概流程如下
- Class.__allocating_init()
- libswiftCore.dylib:_swift_allocObject_
- libswiftCore.dylib:swift_slowAlloc
- libsystem_malloc.dylib:malloc
- 在Mac、iOS中的malloc函数分配的内存大小总是16的倍数
- 通过class_getInstanceSize可以得知类的对象真正使用的内存大小
具体解析内存占用:
import Foundation
func testInstance() {
class Point {
//引用类型前面会有16个字节
var x : Int = 11 //8
var test = true //1
var y : Int = 22 //8
}
let p = Point()
print(class_getInstanceSize(Point.self)) //40
print(class_getInstanceSize(type(of: p))) //40
print(Mems.size(ofRef:p)) //48
//33 类对象真正利用,真正存具体数据的内存空间
//40 类的对象真正使用的内存大小
//48 类对象实际分配的内存大小
}
引用类型的赋值操作
值类型、引用类型的let
p表示值类型赋值的内存数据是常量,不可变,也就是10和20不可变,下面对p的三个赋值操作都改变了栈空间的内存数据,所以报错
s表示引用类型赋值的内存数据是常量,这里的栈空间存储的内存数据是指向堆内存空间的地址,第一个属于重新赋一个新值,指向的内存地址改变,所以报错,但是后两个没有改变指向的堆内存的地址,所以没有报错
嵌套类型
枚举、类、结构体都可以定义方法
一般把定义在枚举、结构体、类内部的函数,叫做方法
枚举:
类:
结构体:
注意;方法不占用实力对象内存的,方法的本质就是函数,方法实际上是放在代码段的.