结构体
在Swift
标准库中,绝大多数的公开类型都是结构体,而枚举和类只占很小一部分
比如Bool
,Int
,Double
,String
,Array
,Dictionary
等常见的类型都是接头体
我们可以自定义结构体,举个例子🌰:
struct Date {
var year: Int
var month: Int
var day: Int
}
//创建一个结构体变量,带有三个成员值
var date = Date(year: 2020, month: 4, day: 14)
所有的结构体都有一个编译器自动生成的初始化器(initializer,初始化方法、构造器、构造方法)
比如调用var date = Date(year: 2020, month: 4, day: 14)
,可以传入所有成员值,用来初始化所有成员(存储属性,Stroed Property)
结构体的初始化器
编译器会根据情况,可能会为结构体生成多个初始化器,宗旨是:保证所有成员都要初始值
struct Point {
var x: Int
var y: Int
}
var p1 = Point(x: 10, y: 10) //正确
var p2 = Point(x: 10) //报错:Missing argument for parameter 'y' in call
var p3 = Point(y: 10) //报错:Missing argument for parameter 'x' in call
var p4 = Point() //报错:Missing arguments for parameters 'x', 'y' in call
struct Point {
var x: Int = 0
var y: Int
}
var p1 = Point(x: 10, y: 10) //正确
var p2 = Point(x: 10) //报错:Missing argument for parameter 'y' in call
var p3 = Point(y: 10) //正确,因为x有初始值为0
var p4 = Point() //报错:Missing arguments for parameters 'x', 'y' in call
struct Point {
var x: Int
var y: Int = 0
}
var p1 = Point(x: 10, y: 10) //正确
var p2 = Point(x: 10) //正确,因为y有初始值0
var p3 = Point(y: 10) //报错:Missing argument for parameter 'x' in call
var p4 = Point() //报错:Missing arguments for parameters 'x', 'y' in call
struct Point {
var x: Int = 10
var y: Int = 10
}
var p1 = Point(x: 10, y: 10) //正确
var p2 = Point(x: 10) //正确,因为y有初始值0
var p3 = Point(y: 10) //正确,因为x有初始值0
var p4 = Point() //正确,因为x、y都有初始值0
下面这种写法可以通过吗?
struct Point {
var x: Int?
var y: Int?
}
var p1 = Point(x: 10, y: 10)
var p2 = Point(x: 10)
var p3 = Point(y: 10)
var p4 = Point()
解析:可以通过编译,因为可选项有初始值为nil
,满足条件。
自定义初始化器
一旦在定义结构体的时候自定义了初始化器,编译器级不会自动帮它生产初始化器
虽然x、y
都有初始化值0
,但是用户自定义了初始化器,所有只有第一个初始化例子正确
struct Point {
var x: Int = 0
var y: Int = 0
init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
var p1 = Point(x: 10, y: 10) //正确,满足需要两个参数
var p2 = Point(x: 10) //报错:Missing argument for parameter 'y' in call
var p3 = Point(y: 10) //报错:Missing argument for parameter 'y' in call
var p4 = Point() //Missing arguments for parameters 'x', 'y' in call
窥探初始化器的本质
struct Point {
var x: Int = 0
var y: Int = 0
}
var p = Point()
//上下两段结构体代码是等效的
struct Point {
var x: Int
var y: Int
init() {
x = 0
y = 0
}
}
var p = Point()
我们可以通过汇编来查看编译器实现跟自己实现是否有差别
先看第1段代码的汇编实现:
再看第2段代码的汇编实现:
你会发现它们两最终的汇编代码一模一样,连内存地址都一致,包括
x
、y
的赋值步骤,所以说明了自己实现的初始化器跟编译器自动生成的是样的,两个方法是等效的。
结构体的内存结构
struct Point {
var x: Int = 0
var y: Int = 0
}
var p = Point()
上面代码结构体变量p
内存需要占用多少字节?
通过MemoryLayout
来看看:
print(MemoryLayout<Point>.size) //16
print(MemoryLayout<Point>.stride) //16
print(MemoryLayout<Point>.alignment) //8
Point结构体变量需要16个字节,其中8字节个用来给x
,8个字节用来给y
,结构体内部成员的内存地址是挨在一起的。
通过一个第三方内存工具Mems来看看p
内部16个字节内容的排列:
struct Point1 {
var x: Int = 10
var y: Int = 20
}
var p1 = Point1()
print(Mems.memStr(ofVal: &p1))
//通过第三方内存工具,可以看出内部16个字节的排列
//0x000000000000000a 0x0000000000000014
再看一个例子🌰:
struct Point2 {
var x: Int = 0
var y: Int = 0
var origin: Bool = false //Bool占用1个字节
}
var p2 = Point2()
print(MemoryLayout<Point2>.size) //17
print(MemoryLayout<Point2>.stride) //24
print(MemoryLayout<Point2>.alignment) //8
Bool
占用1个字节,所以Point2
只需要17个字节就能存储三个成员,但是由于内存对齐是8倍数,所有最终需要占用24个字节
struct Point2_2 {
var x: Int = 10
var y: Int = 20
var b: Bool = true
}
var p2_2 = Point2_2()
print(Mems.memStr(ofVal: &p2_2))
//通过第三方内存工具,可以看出内部24个字节的排列
//0x000000000000000a 0x0000000000000014 0x0000000000000001
网友评论