13
13
## 从 main 函数开始
14
14
15
15
` main ` 函数可以说是在整个 iOS 开发中非常不起眼的一个函数,它很好地隐藏在 ` Supporting Files ` 文件夹中,却是整个 iOS 应用的入口。
16
- ![ objc-autorelease-main] ( media/14628459785637 /objc-autorelease-main.png)
16
+ ![ objc-autorelease-main] ( ../images /objc-autorelease-main.png)
17
17
18
18
` main.m ` 文件中的内容是这样的:
19
19
@@ -39,7 +39,7 @@ $ clang -rewrite-objc main.m
39
39
40
40
在生成了一大堆警告之后,当前目录下多了一个 ` main.cpp ` 文件
41
41
42
- ![ objc-autorelease-main-cpp] ( media/14628459785637 /objc-autorelease-main-cpp.png)
42
+ ![ objc-autorelease-main-cpp] ( ../images /objc-autorelease-main-cpp.png)
43
43
44
44
> 这里删除了 ` main ` 函数中其他无用的代码。
45
45
@@ -53,7 +53,7 @@ $ clang -rewrite-objc main.m
53
53
54
54
在 ` main.cpp ` 中查找名为 ` __AtAutoreleasePool ` 的结构体:
55
55
56
- ![ objc-autorelease-main-cpp-struct] ( media/14628459785637 /objc-autorelease-main-cpp-struct.png)
56
+ ![ objc-autorelease-main-cpp-struct] ( ../images /objc-autorelease-main-cpp-struct.png)
57
57
58
58
``` objectivec
59
59
struct __AtAutoreleasePool {
@@ -108,7 +108,7 @@ void objc_autoreleasePoolPop(void *ctxt) {
108
108
109
109
` AutoreleasePoolPage ` 是一个 C++ 中的类:
110
110
111
- ![ objc-autorelease-AutoreleasePoolPage] ( media/14628459785637 /objc-autorelease-AutoreleasePoolPage.png)
111
+ ![ objc-autorelease-AutoreleasePoolPage] ( ../images /objc-autorelease-AutoreleasePoolPage.png)
112
112
113
113
它在 ` NSObject.mm ` 中的定义是这样的:
114
114
@@ -138,23 +138,23 @@ class AutoreleasePoolPage {
138
138
139
139
自动释放池中的 `AutoreleasePoolPage` 是以**双向链表**的形式连接起来的:
140
140
141
- 
141
+ 
142
142
143
143
> `parent` 和 `child` 就是用来构造双向链表的指针。
144
144
145
145
#### 自动释放池中的栈
146
146
147
147
如果我们的一个 `AutoreleasePoolPage` 被初始化在内存的 `0x100816000 ~ 0x100817000` 中,它在内存中的结构如下:
148
148
149
- 
149
+ 
150
150
151
151
其中有 56 bit 用于存储 `AutoreleasePoolPage` 的成员变量,剩下的 `0x100816038 ~ 0x100817000` 都是用来存储**加入到自动释放池中的对象**。
152
152
153
153
> `begin()` 和 `end()` 这两个类的实例方法帮助我们快速获取 `0x100816038 ~ 0x100817000` 这一范围的边界地址。
154
154
155
155
`next` 指向了下一个为空的内存地址,如果 `next` 指向的地址加入一个 `object`,它就会如下图所示**移动到下一个为空的内存地址中**:
156
156
157
- 
157
+ 
158
158
159
159
> 关于 `hiwat` 和 `depth` 在文章中并不会进行介绍,因为它们并不影响整个自动释放池的实现,也不在关键方法的调用栈中。
160
160
@@ -187,7 +187,7 @@ int main(int argc, const char * argv[]) {
187
187
188
188
而当方法 `objc_autoreleasePoolPop` 调用时,就会向自动释放池中的对象发送 `release` 消息,直到第一个 `POOL_SENTINEL`:
189
189
190
- 
190
+ 
191
191
192
192
### <a id='objc_autoreleasePoolPush'></a>objc_autoreleasePoolPush 方法
193
193
@@ -315,7 +315,7 @@ void objc_autoreleasePoolPop(void *ctxt) {
315
315
316
316
我们一般都会在这个方法中传入一个哨兵对象 `POOL_SENTINEL`,如下图一样释放对象:
317
317
318
- 
318
+ 
319
319
320
320
#### 对 objc_autoreleasePoolPop 行为的测试
321
321
@@ -339,11 +339,11 @@ int main(int argc, const char * argv[]) {
339
339
340
340
在代码的这一行打一个断点,因为这里会调用 ` autorelease ` 方法,将字符串加入自动释放池:
341
341
342
- ![ objc-autorelease-breakpoint-main] ( media/14628459785637 /objc-autorelease-breakpoint-main.png)
342
+ ![ objc-autorelease-breakpoint-main] ( ../images /objc-autorelease-breakpoint-main.png)
343
343
344
344
当代码运行到这里时,通过 lldb 打印出当前 ` hotPage ` 中的栈内容:
345
345
346
- ![ objc-autorelease-print-pool-content] ( media/14628459785637 /objc-autorelease-print-pool-content.png)
346
+ ![ objc-autorelease-print-pool-content] ( ../images /objc-autorelease-print-pool-content.png)
347
347
348
348
1 . 通过 ` static ` 方法获取当前 ` hotPage `
349
349
2 . 打印 ` AutoreleasePoolPage ` 中的内容
@@ -352,7 +352,7 @@ int main(int argc, const char * argv[]) {
352
352
353
353
然后将字符串 ` @"Draveness-Suffix" ` 的指针传入 ` pop ` 方法,测试 ` pop ` 方法能否传入非哨兵参数。
354
354
355
- ![ objc-autorelease-pop-string] ( media/14628459785637 /objc-autorelease-pop-string.png)
355
+ ![ objc-autorelease-pop-string] ( ../images /objc-autorelease-pop-string.png)
356
356
357
357
再次打印当前 ` AutoreleasePoolPage ` 的内容时,字符串已经不存在了,这说明** 向 ` pop ` 方法传入非哨兵参数是可行的** ,只是我们一般不会传入非哨兵对象。
358
358
0 commit comments