Autorelease Pool-自动释放池
Autorelease Pool是Objective-C中的内存管理方式之一,它与线程和NSAutorelease类有关。每一个线程都拥有自己的Autorelease Pool栈,这个栈底层是由双向链表构成,而双向链表由类AutoreleasePoolPage表示。AutoreleasePoolPage是一个C++类,这个类的结构如下(只给出了部分相关成员变量):
class AutoreleasePoolPage {
static pthread_key_t const key = AUTORELEASE_POOL_KEY; //和线程相关的key值
static size_t const SIZE =
#if PROTECT_AUTORELEASEPOOL
PAGE_MAX_SIZE; // must be multiple of vm page size
#else
PAGE_MAX_SIZE; // size and alignment, power of 2
#endif
static size_t const COUNT = SIZE / sizeof(id); //每一个page的大小
id *next; //加入当前page中的Objective-C对象地址会传给next
pthread_t const thread; //线程的引用
AutoreleasePoolPage * const parent; //指向父节点
AutoreleasePoolPage *child; //指向子节点
};
线程与Autorelease Pool栈的关系如下图所示:

上图中,加颜色部分表示加入到自动释放池中的对象,而next总是指向下一个可用位置。当前线程总是可以通过AutoreleasePoolPage中的key属性获取到位于栈最顶端(图中就是最右边)的AutoreleasePoolPage对象,这个对象被称为hot page。
需要注意的是,并不是一个AutoreleasePoolPage对象就代表一个自动释放池,一个自动释放池可以横跨多个AutoreleasePage对象。自动释放池之间是通过哨兵(Sentinel)来分隔的。那么什么是哨兵,NSAutoreleasePool对象和AutoreleasePoolPage对象之间又有什么关系呢?
当我们使用如下方法创建NSAutoreleasePool对象时:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSAutoreleasePool对象的init方法实际上会调用AutoreleasePoolPage类的static push方法:
static inline void *push()
{
id *dest = autoreleaseFast(POOL_SENTINEL);
assert(*dest == POOL_SENTINEL);
return dest;
}
AutoreleasePoolPage::push方法中的autoreleaseFast方法传入的就是一个哨兵,其实就是一个nil值。而AutoreleasePoolPage::autoreleaseFast方法会做3件事:
1 通过AutoreleasePoolPage的key属性获取当前线程对应的hot page,如果hot page存在并且未满,那么就向当前page中加入哨兵;
2 如果当前hot page已满,那么就创建一个新的AutoreleasePoolPage对象,加入到链表最顶端使成为hot page,并将哨兵加入到新的hot page中;
3 如果hot page根本不存在,那么也会创建一个新的AutoreleasePoolPage对象,使之成为hot page,并将哨兵加入到新hot page中。
当加入了一个哨兵之后,就代表一个新自动释放池开始。AutoreleasePoolPage::push方法会将这个哨兵地址返回回去,而NSAutoreleasePool对象会持有这个哨兵地址:
@interface NSAutoreleasePool : NSObject {
@private
void *_token; //保存哨兵地址
void *_reserved3;
void *_reserved2;
void *_reserved;
}
NSAutoreleasePool对象当中的_token成员,就是用来保存哨兵地址的。因此,线程,AutoreleaesPoolPage对象,NSAutoreleasePool对象之间的关系可以表示成:

上图中有两个哨兵,因此有两个自动释放池,第一个自动释放池由NSAutoreleasePool对象_1表示,范围从第一个哨兵开始到第二个哨兵下面,第二个自动释放池由NSAutoreleasePool对象_2表示,范围从第二个哨兵开始直到结束。这也是自动释放池嵌套时的情形。
当自动释放池释放,即调用NSAutoreleasePool对象的release或者drain时会发生什么情况呢?
当调用NSAutoreleasePool对象的release或者drain方法时,它们将NSAutoreleasePool对象保存的哨兵地址传递会给AutoreleasePoolPoage的static pop方法,这个AutoreleasePoolPage::pop方法主要做了如下2件事:
1 通过相应算法,根据哨兵地址找到哨兵所在的AutoreleasePoolPage对象;
2 从hot page开始,直到哨兵对象为止,依次弹出加入到里面的Objective-C对象,并且向Objective-C对象发送release消息
假设释放了上面的NSAutoreleasePool对象_2,那么结果如下图所示:

从Apple关于NSAutoreleasePool的官方文档我们可以知道,NSAutoreleasePool对象无法被retain,因此,当AutoreleasePoolPage::pop方法调用完成之后,NSAutoreleasePool对象的release或者drain方法就会释放掉这个NSAutoreleasePool对象本身。到这里为止,我们又可以向上一个自动释放池当中添加新的Objective-C对象了。
Autorelease Pool-自动释放池的更多相关文章
- objective-C 的内存管理之-自动释放池(autorelease pool)
如果一个对象的生命周期显而易见,很容易就知道什么时候该new一个对象,什么时候不再需要使用,这种情况下,直接用手动的retain和release来判定其生死足矣.但是有些时候,想知道某个对象在什么时候 ...
- Autorelease自动释放池的使用
Autorelease自动释放池的使用 使用ARC开发,只是在编译时,编译器会根据代码结构自动添加了retain.release和autorelease. MRC内存管理原则:谁申请,谁释放 遇到al ...
- OC中对象元素的引用计数 自动释放池的相关概念
OC中数组对象在是如何处理对象元素的引用计数问题的,同时介绍一下自动释放池的相关概念 一.数组对象是如何处理对象元素的引用计数问题[objc] view plaincopy 1. // 2. / ...
- C++模拟OC的多重自动释放池
使用过OC的都知道,OC的引用计数机制用起来还比较方便.于是就仿照OC的形式搞了个C++引用计数. 支持多重自动释放池,每次autorelease都会放到栈顶的自动释放池中. 自动释放池也可以像变量一 ...
- autoreleasepool 自动释放池的理解
常见的面试题:以下代码存在什么样的问题?应该如何改进? for (int i = 0; i < 100000; i++) { NSString *str = @"abc"; ...
- 自动释放池的前世今生 ---- 深入解析 autoreleasepool
http://draveness.me/autoreleasepool.html 关注仓库,及时获得更新:iOS-Source-Code-Analyze Follow: Draveness · Git ...
- Objective-C中的自动释放池
自动释放池块@autoreleasepool 自动释放池块在MRC和ARC下都可以使用.在MARC下,为了将自动释放池块内部的变量放入自动释放池,需要手动调用autorelease方法:在ARC下,只 ...
- 63 (OC)* NSAutoreleasePool 自动释放池
目录 0:ARC 1: 自动释放池 2:NSAutoreleasePool实现原理 3:autorelease 方法 4: Runloop和Autorelease的关系 5: Using Autore ...
- OC学习篇之---数组对象的引用计数问题和自动释放池的概念
之前一片文章中我们介绍了OC中的两个关键字@property和@synthesize的使用的使用: http://blog.csdn.net/jiangwei0910410003/article/de ...
随机推荐
- C++智能指针初学小结
本篇随笔仅作为个人学习<C++ Primer>智能指针一节后的部分小结,抄书严重,伴随个人理解.主要介绍shared_ptr.make_shared.weak_ptr的用法和联系. C++ ...
- 搭建lamp环境
虚拟机始终是虚拟机,还是linux用起来舒服得多.话不多说,回到我们的老本行,linux下进行lamp环境搭建吧. 一.安装 1.Apache sudo apt-get install apache2 ...
- apache用户认证、默认主机、301跳转
我更正论坛一个同学帖子(今天坑我一下午):原文http://www.apelearn.com/bbs/foru ... 3%BB%A7%C8%CF%D6%A4 apache用户认证.默认主机.301跳 ...
- [0] Tornado Todo 开篇
参考自: python: tornado例子 Github地址:tornado_todo 开发环境: Python包的安装: 首先安装 pip: sudo apt-get install python ...
- [原]Sublime Text3 搭建16位汇编环境(windows)
最近在学习王爽的<汇编程序>,参考<简单OS开发前奏<一>EDITPLUS+MASM32搭建汇编开发环境(16位+32位)>http://www.cnblogs.c ...
- Android添加横线和竖线分割界面
竖线 <View android:layout_width="1dip" android:layout_height="match_parent ...
- php安装pear、pecl
安装pear.pecl特别简单,只需要两步. wget http://pear.php.net/go-pear.phar php go-pear.phar [root@localhost bin]# ...
- java设计模式--结构型模式--桥接模式
桥接模式 概述 将抽象部分与它的实现部分分离,使它们都可以独立地变化. 适用性 1.你不希望在抽象和它的实现部分之间有一个固定的绑定关系. 例如这种情况可能是因为,在程序运行时刻实现部分应可以被选择或 ...
- 11g Rac 切换
<pre name="code" class="sql">[grid@devrac1 ~]$ crsctl status res -t ------ ...
- bzoj1753 [Usaco2005 qua]Who's in the Middle
Description FJ is surveying his herd to find the most average cow. He wants to know how much milk th ...