结构体
多个初始化器:一个原则保证所有成员都有初始值
// 一旦在定义结构时,自定义了结构体初始化器,编译器就不会再帮他自动生成其他初始化器
struct Point {
var x: Int = 0
var y: Int = 0
init(x: Int,y: Int) { // init构造器不用写func 不用写函数返回值是一种特殊的写法
self.x = x;
self.y = y
}
}
var p = Point(x: 10, y: 20)
var p1 = Point(x: 10)// 报错
var p2 = Point(y: 20)// 报错
var p3 = Point()// 报错
查看初始化器的本质(汇编)
//第一种
func testfunc (){
struct Point {
var x: Int = 5
var y: Int = 6
}
var p = Point()
}
testfunc()
image.png
// 第二种
func testfunc (){
struct Point {
var x: Int
var y: Int
init() {
x = 5
y = 6
}
}
var p = Point()
print(MemoryLayout<Point>.size) // 16
print(MemoryLayout<Point>.stride) // 16
print(MemoryLayout<Point>.alignment) //8
}
testfunc()
第一种和第二种对比发现,不仅方法一致汇编指令一致,甚至连内存地址也是一致的所以这两种是完全等价的.构造器的实现,就是我们所写的这样.
结构体的内存大小
func testfunc (){
struct Point {
var x: Int = 1
var y: Int = 2
var z: Bool = true
}
var p = Point()
print(MemoryLayout<Point>.size) // 17
print(MemoryLayout<Point>.stride) // 24
print(MemoryLayout<Point>.alignment) //8
}
testfunc()
类
// 如果所有成员都有初始值,编译器会为类生成一个无参的初始化器
// 但是没有初始值的时候,无参的初始化器就不能使用
类和结构体本质区别
func testClassAndStruct() {
class Size {
var width: Int = 3
var height: Int = 4
}
struct Point {
var x: Int = 10
var y: Int = 20
}
var point = Point()
// var size = Size()
}
可以观察出结构体Point只是callq了init构造方法,并没有任何molloc申请堆空间的地方
下面观察类Size:
// 经过不断的si往下查找可以发现如图
// 最终调用系统级别的分配堆空间的函数
// 继续进入
综上所述:结构体是不需要分配堆空间的,而类是需要系统分配堆空间的
查看类实例和结构体变量的内存
func testClassAndStruct() {
class Size {
var width: Int = 3
var height: Int = 4
}
struct Point {
var x: Int = 10
var y: Int = 20
}
var size = Size()
var point = Point()
print("变量size的内存地址:\(Mems.ptr(ofVal: &size))")
print("变量size的内存数据\(Mems.memStr(ofVal: &size))") //打印出来是一个地址,指向另一块堆空间
print("size所指向内存的地址:\(Mems.ptr(ofRef: size))")//获取变量所指向的内存的内存地址
print("size所指向内存的地址的内容:\(Mems.memStr(ofRef: size))")
print("---------------------")
print("变量point的内存地址:\(Mems.ptr(ofVal: &point))")
print("变量point的内存数据\(Mems.memStr(ofVal: &point))")
//变量size的内存地址:0x00007ffeefbff0b8
//变量size的内存数据0x0000000100406030
//size所指向内存的地址:0x0000000100406030
//size所指向内存的地址的内容:0x000000010000b340 0x0000000200000002 0x0000000000000003 0x0000000000000004
//---------------------
//变量point的内存地址:0x00007ffeefbff0c0
//变量point的内存数据0x000000000000000a 0x0000000000000014
// 我们分别可以找到 3 4 10 20 在哪里,
// 值得注意的是变量size占8个字节,而其地址指向的内存空间占32个字节
// 虽然size创建比point要晚,但是变量size的内存地址:0x00007ffeefbff0b8 要比变量point的内存地址:0x00007ffeefbff0c0 靠前一些相差16个字节 刚好是变量point的内存长度
}
testClassAndStruct()
注意:
结构体(值类型)的内存取决于结构体变量放在哪里
1.如果放在函数里,就在栈空间
2.如果放在上面,就是数据区,因为这时候是全局变量
3.放在类里面,就是在堆空间
类(引用类型)的内存放在哪里
1.size指针变量取决于放在哪里
2.类对象(实例)永远是在堆空间
func testClassAndStruct() {
class Size {
var width: Int = 3
var height: Int = 4
func test() {
var p = Point()// 此时结构体还是在栈空间(函数不管在哪里,栈空间是不变的,在类里面和全局函数都是一样的)
}
}
struct Point {
var x: Int = 10
var y: Int = 20
}
var point = Point()
var size = Size()
}
值类型 汇编代码分析
深拷贝
寄存器的兼容性我们知道%edi存储在%rdi里,%esi存储在%rsi里
init函数里的操作是把%rdi给了%rax,%rsi给了%rdx
网友评论