AutoreleasePool

387 阅读2分钟

AutoReleasePool

AutoreleasePool的实现原理

@autoreleasePool = __AtAutoreleasePool __autoreleasePool

__AtAutoreleasePool 结构体

AutoreleasePool 是 oc 的一种内存回收机制,正常情况下变量在超出作用域的时候 release,但是如果将变量加入到 pool 中,那么release 将延迟执行

AutoreleasePool 并没有单独的结构,而是由若干个 AutoreleasePoolPage 以**双向链表**形式组成

1. PAGE_MAX_SIZE :4KB,虚拟内存每个扇区的大小,内存对齐
2. 内部 thread ,page 当前所在的线程,AutoreleasePool是按线程一一对应的
3. 本身的成员变量占用56字节,剩下的内存存储了调用 autorelease 的变量的对象的地址,同时将一个哨兵插入page中
4. pool_boundry 哨兵标记,哨兵其实就是一个空地址,用来区分每一个page 的边界
5. 当一个Page被占满后,会新建一个page,并插入哨兵标记

单个自动释放池的执行过程就是objc_autoreleasePoolPush() —> [object autorelease] —> objc_autoreleasePoolPop(void *)

具体实现如下:

void *objc_autoreleasePoolPush(void) {
    return AutoreleasePoolPage::push();
}

void objc_autoreleasePoolPop(void *ctxt) {
    AutoreleasePoolPage::pop(ctxt);
}

内部实际上是对 AutoreleasePoolPage 的调用

objc_autoreleasePoolPush

每当自动释放池调用 objc_autoreleasePoolPush 时,都会把边界对象放进栈顶,然后返回边界对象,用于释放。

AutoreleasePoolPage::push(); 调用👇

static inline void *push() {
   return autoreleaseFast(POOL_BOUNDARY);
}

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);
   }
}

👆上述方法分三种情况选择不同的代码执行:

- 有 hotPage 并且当前 page 不满,调用 page->add(obj) 方法将对象添加至 AutoreleasePoolPage 的栈中
- 有 hotPage 并且当前 page 已满,调用 autoreleaseFullPage 初始化一个新的页,调用 page->add(obj) 方法将对象添加至 AutoreleasePoolPage 的栈中
- 无 hotPage,调用 autoreleaseNoPage 创建一个 hotPage,调用 page->add(obj) 方法将对象添加至 AutoreleasePoolPage 的栈中

最后的都会调用 page->add(obj) 将对象添加到自动释放池中。 hotPage 可以理解为当前正在使用的 AutoreleasePoolPage。

AutoreleasePoolPage

是以栈的形式存在,并且内部对象通过进栈、出栈对应着 objc_autoreleasePoolPush 和 objc_autoreleasePoolPop
  
当我们对一个对象发送一条 autorelease 消息时,实际上是将这个对象地址加入到 autoreleasePoolPage 的栈顶 next 指针的指向的位置

Runloop 与 Autorelease

iOS 在主线程注册了两个 observer

__第一个observer __

监听了 kCFRunloopEntry, 会调用 objc_autoreleasePool_push()

第二个 observer

监听了 kCFRunloopBeforeWaiting 会调用 objc_autoreleasePool_pop() 、objc_autoreleasePool_push()

监听了 kCFRunloopExit 事件,会调用 objc_autoreleasePool_pop()