@autoreleasepool 编译后会变成
void * atautoreleasepoolobj = objc_autoreleasePoolPush();
// 中间代码
objc_autoreleasePoolPop(atautoreleasepoolobj);
这里要记录一下,objc_autoreleasePoolPush函数的返回值 传给了objc_autoreleasePoolPop()
void *objc_autoreleasePoolPush(void) {
return AutoreleasePoolPage::push();
}
void objc_autoreleasePoolPop(void *ctxt) {
AutoreleasePoolPage::pop(ctxt);
}
push 和 pop函数其实是调用的AutoreleasePoolPage的push 和 pop
class AutoreleasePoolPage {
magic_t const magic;
id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
};
上面是AutoreleasePoolPage类代码
1.AutoreleasePoolPage本质上是一个双向链表 分别有parent 和 child 指针。
2.每一个AutoreleasePoolPage大小固定为4096个字节 (虚拟内存一页的大小)
3.4096字节除了上面包含的各种变量(magic、next、thread、parent、child、depth、hiwat)外,剩下就用于存储autorealease对象指针
4.next指针记录下一个为空的地址, 每加入一个对象 next指针就会偏移一次。
5.介绍一下POOL_SENTINEL,其实就是一个宏定义
#define POOL_SENTINEL nil
有了上面的概念后 分析一下push函数
static inline void *push() {
return autoreleaseFast(POOL_SENTINEL);
}
push 函数 调用了 autoreleaseFast函数 并传入了 标兵对象 ,也就是nil ,接着来看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没有满,将obj添加到page
如果page满了,新建一个newpage并将 page的child指向newchild, newchild的parent指向page (双向链表的基本操作),然后将obj加入到newpage里- 将newpage 设置为hotPage
如果压根儿就没有page, 那就新建一个page,也就是表头,此时有个操作是判断obj是不是标兵如果过是直接插入,否则在obj加入之前先加入一个标兵 - 防止pop时崩溃(这也是autoreleaseFullPage 和 autoreleaseNoPage的区别)。
static id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page) { do { if (page->child) page = page->child; else page = new AutoreleasePoolPage(page); } while (page->full()); setHotPage(page); return page->add(obj); } static id *autoreleaseNoPage(id obj) { AutoreleasePoolPage *page = new AutoreleasePoolPage(nil); setHotPage(page); if (obj != POOL_SENTINEL) { page->add(POOL_SENTINEL); } return page->add(obj); }
接下来 看page->add(obj )做了啥
id *add(id obj) { id *ret = next; *next = obj; next++; return ret; }
将新加的对象存储地址返回
还记得一开始记录一下的地方吗,将push函数的返回值 作为参数传给了 pop
也就是将一个标兵(nil)的地址传给了 pop()
static inline void pop(void *token) {
AutoreleasePoolPage *page = pageForPointer(token);//获取token所在page
id *stop = (id *)token;
page->releaseUntil(stop); // 释放
if (page->child) { // 移除后面链表
if (page->lessThanHalfFull()) {
page->child->kill();
} else if (page->child->child) {
page->child->child->kill();
}
}
}
第一步、pageForPointer 获取token所在的page 【通过offse来计算】
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指针进行PoolPage大小SIZE(4096)取模->offset,然后用p减去offset就是AutoreleasePoolPage的首地址,也就是page的地址。fastcheck()是一个快速检测result是否是Page的方法,不再深入看。
第二步、releaseUntil 释放对象
void releaseUntil(id *stop) { // this 为当前标兵坐在的page while (this->next != stop) { AutoreleasePoolPage *page = hotPage();// 获取最新的page while (page->empty()) { // 如果是空的 往上遍历 找到非空的 并设置为hotpage page = page->parent; setHotPage(page); } page->unprotect(); // 解锁 id obj = *--page->next; // 得到当前page的最新指针 拿到对象 next指针-1 memset((void*)page->next, SCRIBBLE, sizeof(*page->next)); // autorealease对象指针置空 page->protect(); if (obj != POOL_SENTINEL) { // 对obj发送release objc_release(obj); } } setHotPage(this); }
总结一下就是从hotpage的next指针开始释放到传入的对象,也就一开始说的标兵对象。
这也就是和一开始的代码对应起来了,push函数传入一个标兵并将地址传入pop函数,pop函数开始Realease对象到 标兵停止 行程一个闭合。代码粘过来 回顾一下
void * atautoreleasepoolobj = objc_autoreleasePoolPush();// 开始 // 中间代码 在这里写autoRealease 代码 objc_autoreleasePoolPop(atautoreleasepoolobj); // 结束
这种实现方式也能实现 释放池的嵌套
第三步、所有的autorealease指针置空并对obj发送realease消息以后,接下来要是释放token之后的page链表,也就是kill函数,这里做了一步判断,如果当前容量还没存储够一半,剩下的page全部清空,如果当前存储大于当前page的一半,留下childpage,将childpage的所有子page全部删除
if (page->child) { // 移除后面链表 if (page->lessThanHalfFull()) { // 如果当前存储还不到一半 删除所有的child page->child->kill(); } else if (page->child->child) { // 大于一半,保留childpage ,删除后面的page page->child->child->kill(); } }
kill 函数
void kill() { AutoreleasePoolPage *page = this; while (page->child) page = page->child; // 也就是链表的最后一个 AutoreleasePoolPage *deathptr; do { deathptr = page; page = page->parent; if (page) { // 父page 的child 指向nil page->unprotect(); page->child = nil; page->protect(); } delete deathptr; // 释放当前page } while (deathptr != this); // 遍历条件 deathptr == this的时候跳出 }
以上就是全部了。。。