update: 动态库、静态库的编译链接细节

This commit is contained in:
FantasticLBP
2025-06-23 01:18:55 +08:00
parent aca020701b
commit 1142064d28
129 changed files with 10932 additions and 2615 deletions

View File

@@ -1,6 +1,6 @@
# Swift 结构体和类的内存布局
## 结构体内存布局
## 结构体初始化器
实验1在 struct 内部自己实现 init
@@ -18,7 +18,7 @@ var point = Point()
`init` 方法内第一行处加 断点,如下所示
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/SwiftStructMemoryLayoutDemo1.png" style="zoom:25%">
<img src="./../assets/SwiftStructMemoryLayoutDemo1.png" style="zoom:25%">
实验2struct 内不自己加 init
@@ -32,11 +32,15 @@ var point = Point()
`var point = Point()`处加 断点,如下所示
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/StructMemoryLayoutDemo2.png" style="zoom:25%">
<img src="./../assets/StructMemoryLayoutDemo2.png" style="zoom:25%">
结论:结构体会有一个编译器自动生成的初始化编译器。目的是保证所有成员都有初始值
现象:可以看到加不加自定义初始化器的汇编代码基本相同
实验3
结论:**如果没有为结构体声明初始化器编译器会自动生成1个初始化器。目的是保证所有成员都有初始值。**
## 结构体内存布局
```swift
struct CustomDate {
@@ -62,6 +66,7 @@ Int 占8 ByteBool 占1 Byte共 2*8 + 1 = 17 Byte由于存在内存对
- 与类的比较:与 `class`(类)不同,`struct` 不需要额外的内存来存储类型信息、引用计数或其他元数据。这使得 `struct`通常比 `class` 更轻量级,并且在某些情况下具有更好的性能。
## 类的内存布局
类和结构体类似,但是编译器不会为类自动生成可以传入成员值的初始化器。
@@ -124,13 +129,13 @@ test()
断点打在 `var point1 = Point(x: 10, y: 20)` 处,查看汇编
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/SwiftValuePassDemo1.png" style="zoom:25%">
<img src="./../assets/SwiftValuePassDemo1.png" style="zoom:25%">
乍一看如果不认识的话,先找字面量(立即数),比如红色框内的 `0xa`,就是 10`0x14` 就是20。[之前](./109.md)学过寄存器的设计64位寄存器是兼容32位寄存器的。红色框内将 `0xa`,也就是 10 保存到 `%edi ` 寄存器内部,也就是保存到 `%rdi` 中,将 `0x14` 也就是20保存到 `%esi` 也就是保存到 `%rsi` 寄存器中。
LLDB 模式下输入 `si` 进入 init 方法内部。
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/SwiftValuePassDemo2.png" style="zoom:25%">
<img src="./../assets/SwiftValuePassDemo2.png" style="zoom:25%">
可以查看到将 `%rsi` 里的10 保存到 `%rdx` 中了;将 `%rdi` 里的20 保存到 `%rax` 中了。这也就是 struct `init` 方法做的事情。
@@ -155,11 +160,69 @@ LLDB 模式下输入 `finish` 结束 init 方法。
**Swift 标准库中为了提升性能String、Array、Dictionary、Set 采取了 Copy On Write 技术**
### COW 机制
比如仅当有“写”操作时,才会真正执行拷贝操作
**值类型的赋值操作Swift 标准库中的 String、Array、Dictionary 和 Set 确实采用了 Copy-On-WriteCOW写时复制 技术,这是一种内存优化策略,旨在提升性能并减少不必要的内存复制**
对于标准库值类型的赋值操作Swift 能确保最佳性能,所以没必要为了保证最佳性能来避免赋值。
核心思想:
- **延迟复制**:当多个变量引用同一份数据时,它们共享底层存储,直到某个变量尝试修改数据时,才会真正复制一份独立的副本。
- **节省资源**:避免对不可变数据进行冗余复制,减少内存占用和计算开销
仅当有“写”操作时,才会真正执行拷贝操作:
- 对于标准库值类型的赋值操作Swift 能确保最佳性能,所以没必要为了保证最佳性能来避免赋值
- 自定义的值类型,比如结构体,在赋值的时候,就会立马发生深拷贝
举个例子
```swift
var array1 = [1, 2, 3]
var array2 = array1 //
array2.append(4) // COWarray1 array2
```
工作过程:
- 赋值时:`array2``array1` 共享同一块内存
- 修改时:当 `array2` 被修改时,检查引用计数。如果引用计数 > 1即存在多个所有者则复制底层存储确保修改不影响其他变量
写操作触发检查机制:
- **修改前检查**:执行写操作(删除、添加、修改)时,检查缓冲区的引用计数
- **唯一性检查**若引用计数为1则直接修改缓冲区否则复制缓冲区并修改新副本
伪代码
```swift
// 伪代码逻辑
mutating func append(_ element: Element) {
if !isUniquelyReferenced(&buffer) {
buffer = buffer.copy() // 复制缓冲区
}
buffer.append(element) // 修改新副本
}
```
#### 什么是缓冲区
Array 结构体(值类型)
+-------------------+
| 指向缓冲区的指针 |-----→ Buffer 类(引用类型)
| | +----------------+
| 其他元数据(长度、容量) | | 存储元素的内存块 |
+-------------------+ | [1, 2, 3, ...] |
+----------------+
1. **结构体轻量级**
`Array` 结构体本身只包含一个指针和少量元数据(如长度、容量),占用固定大小(如 8 字节指针 + 8 字节长度 + 8 字节容量 = 24 字节)。
2. **缓冲区动态分配**
实际存储元素的连续内存块由缓冲区动态分配在堆上,容量可扩展。
3. **共享与复制**
- **赋值时**:仅复制结构体的指针(浅拷贝),多个数组共享同一缓冲区。
- **修改时**:通过 COW写时复制机制仅在需要时复制缓冲区。
@@ -188,11 +251,11 @@ testReferenceType()
下断点,可以看到下面的汇编:
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClassReferenceTypeMemoryLayoutDemo1.png" style="zoom:25%">
<img src="./../assets/ClassReferenceTypeMemoryLayoutDemo1.png" style="zoom:25%">
在调用(汇编的 call完 `allocating_init` 方法后,方法返回值用 `%rax` 保存的。然后打印出 `%rax` 寄存器的值,查看内存信息如下
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClassReferenceTypeMemoryLayoutDemo2.png" style="zoom:25%">
<img src="./../assets/ClassReferenceTypeMemoryLayoutDemo2.png" style="zoom:25%">
红色框代表类信息的地址蓝色框代表引用计数绿色框代表10黄色框代表20.