在 iOS 中,autoreleasepool
的底层实现基于 Objective-C 运行时(runtime)和内存管理机制。
3 个核心概念:AutoreleasePoolStack,AutoreleasePool,AutoreleasePoolPage
AutoreleasePoolStack 的概念
线程独立性:
每个线程都有一个独立的 AutoreleasePoolStack。这意味着每个线程都能独立管理其自动释放池,而不会干扰其他线程的内存管理。
结构:
AutoreleasePoolStack 是一个栈结构,用于存储当前线程中的所有 AutoreleasePool。
工作流程:
1. 创建 AutoreleasePool:
当进入 @autoreleasepool 块时,会创建一个新的 AutoreleasePool 并压入到 AutoreleasePoolStack 栈中。
@autoreleasepool {
// 创建新的 AutoreleasePool 并压入栈中
}
----------------------
| AutoreleasePoolStack |
|----------------------|
| AutoreleasePool | <-- 新创建的 pool
|----------------------|
| AutoreleasePool | <-- 之前的 pool
----------------------
2. 销毁 AutoreleasePool:
当 @autoreleasepool 块结束时,当前 AutoreleasePool 会被弹出栈,并遍历其 AutoreleasePoolPage,释放所有对象。
理解 AutoreleasePoolPage
是个什么 ?
AutoreleasePool
实际上是由多个 AutoreleasePoolPage
组成,每个页面大小固定,用于存储对象指针,使用链表 (双向) 链接。每当一个页面填满时,会创建一个新的页面。
---------------------
| AutoreleasePoolPage|
|--------------------|
| obj1 |
| obj2 |
| ... |
|--------------------| --> Next Page
| objN |
|--------------------|
AutoreleasePoolPage 负责管理那些被标记为自动释放的对象。
被标记为自动释放的对象:MRC 模式下,对象需要手动调用 autorelease,而 ARC 模式下,ARC 会根据上下文自动插入 autorelease 调用,而不需要开发者显式地调用它。
结构是什么样的 ?
每个 AutoreleasePoolPage 是一个双向链表节点,链表头指向当前活跃的页面,尾部是最早创建的页面。新的 AutoreleasePoolPage 创建时会插入到链表头部。
每个 AutoreleasePoolPage
节点包含以下重要部分:
- 指针数组:存储需要自动释放的对象指针。
- 页的大小:一个页面可以容纳的对象数量是固定的。
- 下一个页面:指向链表中的下一个页面。
- 上一个页面:指向链表中的上一个页面。
清理过程
当 AutoreleasePool
清理时,它会遍历(从后向前)这些页面并调用每个对象的 release
方法。
多个 AutoreleasePoolPage 是按照从最后一个创建的页面到第一个页面的顺序依次销毁的。
-
遍历对象:
当AutoreleasePool
块结束时,系统会遍历当前AutoreleasePool
中所有的AutoreleasePoolPage
。AutoreleasePoolPage *page = currentAutoreleasePoolPage; while (page) { // 遍历当前页面中的对象 for (id *ptr = page->begin(); ptr < page->end(); ptr++) { // 调用对象的 release 方法 (*ptr)->release(); } // 移动到上一个页面 page = page->previous; }
-
调用 release:
每个页面中的对象指针都会接收到release
消息,从而减少其引用计数。如果某个对象的引用计数变为零,则会被立即销毁。 -
页面的销毁:
一旦页面中的所有对象都被处理完毕,该页面会被销毁,释放其占用的内存。
示例代码
以下是一个简化版的 AutoreleasePoolPage
清理过程的代码示例:
@interface AutoreleasePoolPage : NSObject {
id *next; // 指向下一个空闲的位置
AutoreleasePoolPage *previous;
AutoreleasePoolPage *nextPage;
id objects[POOL_SIZE]; // 存储对象指针的数组
}
- (void)releaseAllObjects;
@end
@implementation AutoreleasePoolPage
- (void)releaseAllObjects {
for (id *ptr = objects; ptr < next; ptr++) {
[*ptr release];
}
if (previous) {
[previous releaseAllObjects];
}
}
@end
在这个示例中,releaseAllObjects
方法遍历当前页面中的所有对象指针,并调用它们的 release
方法。遍历完成后,如果有前一个页面,则递归调用其 releaseAllObjects
方法。
通过这种机制,AutoreleasePoolPage
能够高效地清理所有需要自动释放的对象,从而实现内存管理。