mirror of
https://github.com/NohamR/knowledge-kit.git
synced 2026-05-24 20:00:37 +00:00
update: 动态库、静态库的编译链接细节
This commit is contained in:
@@ -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%">
|
||||
|
||||
实验2:struct 内不自己加 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 Byte,Bool 占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-Write(COW,写时复制) 技术,这是一种内存优化策略,旨在提升性能并减少不必要的内存复制**
|
||||
|
||||
对于标准库值类型的赋值操作,Swift 能确保最佳性能,所以没必要为了保证最佳性能来避免赋值。
|
||||
核心思想:
|
||||
|
||||
- **延迟复制**:当多个变量引用同一份数据时,它们共享底层存储,直到某个变量尝试修改数据时,才会真正复制一份独立的副本。
|
||||
- **节省资源**:避免对不可变数据进行冗余复制,减少内存占用和计算开销
|
||||
|
||||
仅当有“写”操作时,才会真正执行拷贝操作:
|
||||
|
||||
- 对于标准库值类型的赋值操作,Swift 能确保最佳性能,所以没必要为了保证最佳性能来避免赋值
|
||||
- 自定义的值类型,比如结构体,在赋值的时候,就会立马发生深拷贝
|
||||
|
||||
举个例子
|
||||
|
||||
```swift
|
||||
var array1 = [1, 2, 3]
|
||||
var array2 = array1 // 此时共享底层存储
|
||||
|
||||
array2.append(4) // 触发 COW:array1 和 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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user