AutoReleasePoolPage
类的定义
class AutoreleasePoolPage
{
static pthread_key_t const key = AUTORELEASE_POOL_KEY;
static uint8_t const SCRIBBLE = 0xA3;
static size_t const SIZE = PAGE_MAX_SIZE;
static size_t const COUNT = SIZE / sizeof(id);
magic_t const magic;
id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
}
成员变量解析:
- key:用于创建线程的标识
- SCRIBBLE:刷子,可以不需要关心
- SIZE:4096Bytes
- COUNT:4096/8
- magic:用于验证
AutoReleasePoolPage
的完整性 - next:栈顶指针
- thread: 当前线程
- parent: 父节点,
AutoReleasePoolPage
是一个双向链表,这个parent
也就是指向上一个节点 - child: 子节点(下一个节点)
- depth: 深度,标识这是第几个节点,从0开始计数
- hiwat: high water mark 高水位线,用来报警内存占用
就如前面所说,AutoReleasePoolPage
他是一个双向链表,每个节点大小为4096Bytes,而且前面56Bytes是成员函数所占有的。而且每个节点中有一个堆栈,其中next
指针是指向栈顶。
begin()
和end()
两个方法分辨指向栈底和对象的结尾:
begin
id * begin() {
return (id *) ((uint8_t *)this+sizeof(*this));
}
end
id * end() {
return (id *) ((uint8_t *)this+SIZE);
}
标准的进栈操作
add
id *add(id obj)
{
assert(!full());
unprotect();
id *ret = next;
*next++ = obj;
protect();
return ret;
}
出栈
releaseUntil
void releaseUntil(id *stop)
{
while (this->next != stop) {
AutoreleasePoolPage *page = hotPage();
while (page->empty()) {
page = page->parent;
setHotPage(page);
}
page->unprotect();
id obj = *--page->next;
memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
page->protect();
if (obj != POOL_BOUNDARY) {
objc_release(obj);
}
}
setHotPage(this);
#if DEBUG
// we expect any children to be completely empty
for (AutoreleasePoolPage *page = child; page; page = page->child) {
assert(page->empty());
}
#endif
}
这里的出栈跟普通出栈不太一样,因为这里的出栈会一直递归出栈到stop
才会停止,这里的SCRIBBLE
就是用来填写空白内存区的。
以上基本上算这个类的定义内容的解释。
那么这个类有什么作用呢?
用来管理实现垃圾自动回收。
怎么实现垃圾回收?
void autoReleaseTest(void)
{
void *pool = objc_autoreleasePoolPush();
........
objc_autoreleasePoolPop(pool);
}
每次有一个加一个AutoReleasePool
,编译器自动回报我们解析成以上的形式。那么看一下objc_autoreleasePoolPush()
和objc_autoreleasePoolPop(pool)
方法的实现
void *
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
void
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
也就是要先看看:
push
static inline void *push()
{
id *dest;
if (DebugPoolAllocation) {
dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {
dest = autoreleaseFast(POOL_BOUNDARY);
}
assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
return dest;
}
这里可以看到Debug
实现的方式不太一样,
Debug
是每次push
都会创建一个新的page
然而非Debug
却不是
这应该是为了Release
节省内存设计的。
所以Debug
环境会比Release
环境内存耗费的多一点吧。
至于这里的POOL_BOUNDARY
是被define
成一个nil
对象的,用处就是一个哨兵对象的作用。
说一下哨兵的作用:
哨兵节点通常被用在链表和遍历树中,他并不拥有或引用任何被数据结构管理的数据。常常用哨兵节点来替代null
,这样的好处有:
- 增加操作的速度
- 降低算法的复杂性和代码的大小
- 增加数据结构的鲁棒性
简单的说哨兵就是为了简化边界条件的处理而存在的。
autoreleaseFast
static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {
return page->add(obj);
} else if (page) {
return autoreleaseFullPage(obj, page);
} else {
return autoreleaseNoPage(obj);
}
}
根据不同的page
状态来进行不同的操作
- 当
page
存在且未满的时候:直接add object
- 当
page
存在但是已经满了的时候:先创建一个page
,且当前page
的child
指向新page
,新page
的parent
指向当前page
,然后add object
- 当
page
不存在的时候:创建一个新page
,再add object
autoreleaseFullPage
static __attribute__((noinline))
id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
{
assert(page == hotPage());
assert(page->full() || DebugPoolAllocation);
do {
if (page->child) page = page->child;
else page = new AutoreleasePoolPage(page);
} while (page->full());
setHotPage(page);
return page->add(obj);
}
hotpage()
这个是当前正在使用的page
autoreleaseNoPage
static __attribute__((noinline))
id *autoreleaseNoPage(id obj)
{
assert(!hotPage());
bool pushExtraBoundary = false;
if (haveEmptyPoolPlaceholder()) {
pushExtraBoundary = true;
}
else if (obj != POOL_BOUNDARY && DebugMissingPools) {
_objc_inform("MISSING POOLS: (%p) Object %p of class %s "
"autoreleased with no pool in place - "
"just leaking - break on "
"objc_autoreleaseNoPool() to debug",
pthread_self(), (void*)obj, object_getClassName(obj));
objc_autoreleaseNoPool(obj);
return nil;
}
else if (obj == POOL_BOUNDARY && !DebugPoolAllocation) {
return setEmptyPoolPlaceholder();
}
AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
setHotPage(page);
if (pushExtraBoundary) {
page->add(POOL_BOUNDARY);
}
return page->add(obj);
}
add
id *add(id obj)
{
assert(!full());
unprotect();
id *ret = next;
*next++ = obj;
protect();
return ret;
}
这里的next是指向一个空指针的,注意一下!
以上就是push操作了。
pop
static inline void pop(void *token)
{
AutoreleasePoolPage *page;
id *stop;
if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
if (hotPage()) {
pop(coldPage()->begin());
} else {
// Pool was never used. Clear the placeholder.
setHotPage(nil);
}
return;
}
page = pageForPointer(token);
stop = (id *)token;
if (*stop != POOL_BOUNDARY) {
if (stop == page->begin() && !page->parent) {
} else {
return badPop(token);
}
}
if (PrintPoolHiwat) printHiwat();
page->releaseUntil(stop);
if (DebugPoolAllocation && page->empty()) {
AutoreleasePoolPage *parent = page->parent;
page->kill();
setHotPage(parent);
} else if (DebugMissingPools && page->empty() && !page->parent) {
page->kill();
setHotPage(nil);
}
else if (page->child) {
if (page->lessThanHalfFull()) {
page->child->kill();
}
else if (page->child->child) {
page->child->child->kill();
}
}
}
pop
操作比push
要麻烦一些!
简单的说就是:
- 找到
token
所在的page
- 对
token
之后的所有对象,发送release消息 - 杀掉多余的
page
pageForPointer
static AutoreleasePoolPage *pageForPointer(const void *p)
{
return pageForPointer((uintptr_t)p);
}
static AutoreleasePoolPage *pageForPointer(uintptr_t p)
{
AutoreleasePoolPage *result;
uintptr_t offset = p % SIZE;
assert(offset >= sizeof(AutoreleasePoolPage));
result = (AutoreleasePoolPage *)(p - offset);
result->fastcheck();
return result;
}
- 先获得偏移量
- 通过当前
p
减去偏移量获取page
指针 - 检测完整性
- 返回
page
releaseUntil
void releaseUntil(id *stop)
{
while (this->next != stop) {
AutoreleasePoolPage *page = hotPage();
while (page->empty()) {
page = page->parent;
setHotPage(page);
}
page->unprotect();
id obj = *--page->next;
memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
page->protect();
if (obj != POOL_BOUNDARY) {
objc_release(obj);
}
}
setHotPage(this);
#if DEBUG
for (AutoreleasePoolPage *page = child; page; page = page->child) {
assert(page->empty());
}
#endif
}
- 获取
hotpage
- 判断是否为空,非空进入下一步,为空获取
parent
,并且设置parent
为hotpage
,然后进入上一步 - 获取栈顶对象,清空栈顶指针,向栈顶对象发送
release
,下移next
指针 - 判断
next
是否等于stop
,相等进入下一步,不相等回到第一步 - 设置
hotpage
在debug环境中因为每次push
会创建一个新的page
,每次pop
会把空的page
kill掉,所以不存在空的page
。
kill
if (page->child) {
if (page->lessThanHalfFull()) {
page->child->kill();
}
else if (page->child->child) {
page->child->child->kill();
}
}
- 当
page
使用量小于一半,杀掉page
的child
之后的所有page
- 当
page
的使用量大于一半且存在孙子的时候,杀掉孙子之后的所有page
- 当
page
的使用量大于一半且不存在孙子的时候,保留child
这里为什么要保留一个child
,可能大家觉得很奇怪,其实是为了节省开销,因为如果大于一半的栈被占用了之后很有可能,马上需要新建新的page
对象。
void kill()
{
// Not recursive: we don't want to blow out the stack
// if a thread accumulates a stupendous amount of garbage
AutoreleasePoolPage *page = this;
while (page->child) page = page->child;
AutoreleasePoolPage *deathptr;
do {
deathptr = page;
page = page->parent;
if (page) {
page->unprotect();
page->child = nil;
page->protect();
}
delete deathptr;
} while (deathptr != this);
}
- 先找到双向链表最后一个节点
- 然后从最后一个节点开始删除
- 直到删除到当前节点(包括当前节点)
总结
那么到目前为止AutoReleasePoolPage
里面主要的流程已经讲完了。其他的一些方法都是无关大雅的东西了。
实际上AutoReleasePoolPage
只是做一个导师的角色,他并不会去销毁对象(自己除外),但是他回去告诉他管理的对象现在该release
一下。
有问题的地方望各位大能指出来!看到我会改正的哦!~