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-自动释放池的更多相关文章

  1. objective-C 的内存管理之-自动释放池(autorelease pool)

    如果一个对象的生命周期显而易见,很容易就知道什么时候该new一个对象,什么时候不再需要使用,这种情况下,直接用手动的retain和release来判定其生死足矣.但是有些时候,想知道某个对象在什么时候 ...

  2. Autorelease自动释放池的使用

    Autorelease自动释放池的使用 使用ARC开发,只是在编译时,编译器会根据代码结构自动添加了retain.release和autorelease. MRC内存管理原则:谁申请,谁释放 遇到al ...

  3. OC中对象元素的引用计数 自动释放池的相关概念

    OC中数组对象在是如何处理对象元素的引用计数问题的,同时介绍一下自动释放池的相关概念 一.数组对象是如何处理对象元素的引用计数问题[objc]  view plaincopy 1. //   2. / ...

  4. C++模拟OC的多重自动释放池

    使用过OC的都知道,OC的引用计数机制用起来还比较方便.于是就仿照OC的形式搞了个C++引用计数. 支持多重自动释放池,每次autorelease都会放到栈顶的自动释放池中. 自动释放池也可以像变量一 ...

  5. autoreleasepool 自动释放池的理解

    常见的面试题:以下代码存在什么样的问题?应该如何改进? for (int i = 0; i < 100000; i++) { NSString *str = @"abc"; ...

  6. 自动释放池的前世今生 ---- 深入解析 autoreleasepool

    http://draveness.me/autoreleasepool.html 关注仓库,及时获得更新:iOS-Source-Code-Analyze Follow: Draveness · Git ...

  7. Objective-C中的自动释放池

    自动释放池块@autoreleasepool 自动释放池块在MRC和ARC下都可以使用.在MARC下,为了将自动释放池块内部的变量放入自动释放池,需要手动调用autorelease方法:在ARC下,只 ...

  8. 63 (OC)* NSAutoreleasePool 自动释放池

    目录 0:ARC 1: 自动释放池 2:NSAutoreleasePool实现原理 3:autorelease 方法 4: Runloop和Autorelease的关系 5: Using Autore ...

  9. OC学习篇之---数组对象的引用计数问题和自动释放池的概念

    之前一片文章中我们介绍了OC中的两个关键字@property和@synthesize的使用的使用: http://blog.csdn.net/jiangwei0910410003/article/de ...

随机推荐

  1. ​C语言数组作为函数参数

    数组可以作为函数的参数使用,进行数据传送. 数组用作函数参数有两种形式,一种是把数组元素(下标变量)作为实参使用:另一种是把数组名作为函数的形参和实参使用. 数组元素作函数实参 数组元素就是下标变量, ...

  2. C# 获取当前路径

    // 获取程序的基目录.System.AppDomain.CurrentDomain.BaseDirectory  F:\广告编辑系统新\taxi_edit\taxi_form\bin\Debug\ ...

  3. node.js + socket.io实现聊天室一

    前段时间,公司打算在社区做一个聊天室.决定让我来做.本小白第一次做聊天类功能,当时还想着通过ajax请求来实现.经过经理提示,说试试当前流行的node.js 和socket.io来做.于是就上网学习研 ...

  4. 转载: jQuery事件委托( bind() \ live() \ delegate()) [委托 和 绑定的故事]

    转载:http://blog.csdn.net/zc2087/article/details/7287429 随着DOM结构的复杂化和Ajax等动态脚本技术的运用,事件委托自然浮出了水面.jQuery ...

  5. php 序列化储存和转化 json_encode() json_decode($q,true)

    序列化是将变量转换为可保存或传输的字符串的过程:反序列化就是在适当的时候把这个字符串再转化成原来的变量使用.这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性. 例如:当需要数据库只有一个 ...

  6. Python核心编程(第九章)--文件和输入输出

    文件内建函数: open()函数提供了初始化输入/输出操作的通用接口 open()基本语法:file_object = open(filename,access_mode='r',buffering= ...

  7. Excel——使用VLOOKUP函数关联跨工作薄数据

    实验环境 有两个工作簿,一个是<花名册>,另一个是<入离职表>,<花名册>上有所有员工的详细信息,包括员工的姓名.部门.出生日期等,<入离职表>上有离职 ...

  8. 开源日志系统 log4c 使用心得+总结

    http://blog.csdn.net/sky_qing/article/details/7208645 一.安装: 我看网上好多人介绍log4c安装的时候都说有两个步骤:先下载expat安装包并安 ...

  9. explorer.exe进程简单介绍

    explorer.exe是Windows程序管理器或者Windows资源管理器,它用于管理Windows图形壳,包括开始菜单.任务栏.桌面和文件管理.不过也发现有大量的恶意病毒木马插入到explore ...

  10. grok 添加字段

    filter { grok { match =>[ "message","%{IPORHOST:clientip} \[%{HTTPDATE:time}\] \&q ...