先通过main函数查autoreleasepool是个什么东西?
#import
#import "NSPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSPerson *person = [[[NSPerson alloc] init] autorelease];
}
return 0;
}
通过终端
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
查看C++文件
int main(int argc, const char * argv[]) {
__autoreleasepool;
//定义__AtAutoreleasePool __autoreleasepool变量
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
NSPerson *person = ((NSPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((NSPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((NSPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSPerson"), sel_registerName("alloc")), sel_registerName("init")), sel_registerName("autorelease"));
}
return 0;
}
可以看出 @autoreleasepool 被转成了__AtAutoreleasePool __autoreleasepool;
__AtAutoreleasePool是什么?
//AtAutoreleasePool结构体
struct __AtAutoreleasePool {
__AtAutoreleasePool() {
atautoreleasepoolobj = objc_autoreleasePoolPush();
}
~__AtAutoreleasePool() {
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
void * atautoreleasepoolobj;
};
__AtAutoreleasePool() {} 构造函数,在创建结构体变量的时候调用
~__AtAutoreleasePool() {} 析构函数,在结构体销毁的时候调用
@autoreleasepool {//大括号开始
NSPerson *person = [[[NSPerson alloc] init] autorelease];
}//大括号结束
上面这段代码相当于下面这段代码
//大括号开始
atautoreleasepoolobj = objc_autoreleasePoolPush();
NSPerson *person = [[[NSPerson alloc] init] autorelease];
//大括号结束
objc_autoreleasePoolPop(atautoreleasepoolobj);
想要知道什么时候会被释放,就要搞清 objc_autoreleasePoolPush 和objc_autoreleasePoolPop都做了什么?
查看NSObjct.mm源码
- 找到objc_autoreleasePoolPush的方法
_objc_autoreleasePoolPush(void)
{
return objc_autoreleasePoolPush();
}
objc_autoreleasePoolPush内部
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
static inline void *push()
{
id *dest;
if (DebugPoolAllocation) {//如果没有page创建一个新的page
// Each autorelease pool starts on a new pool page.
// 在创建表时会插入一个POOL_BOUNDARY
dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {
//如果已经创建了page表,进入将POOL_BOUNDARY传进去
dest = autoreleaseFast(POOL_BOUNDARY);
}
assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
return dest;
}
查看 dest = autoreleaseNewPage(POOL_BOUNDARY);
static __attribute__((noinline))
id *autoreleaseNewPage(id obj)
{
AutoreleasePoolPage *page = hotPage();
//调用autoreleaseFullPage方法并将POOL_BOUNDARY和page传入
if (page) return autoreleaseFullPage(obj, page);
else return autoreleaseNoPage(obj);
}
static __attribute__((noinline))
id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
{
// The hot page is full.
// Step to the next non-full page, adding a new page if necessary.
// Then add the object to that page.
assert(page == hotPage());
assert(page->full() || DebugPoolAllocation);
do {
//查看child有没有,如果没有,创建一个page
if (page->child) page = page->child;
else page = new AutoreleasePoolPage(page);
} while (page->full());
setHotPage(page);
//将POOL_BOUNDARY添加到page表中
return page->add(obj);
}
总结
- 查看objc_autoreleasePoolPop的方法
_objc_autoreleasePoolPop(void *ctxt)
{
objc_autoreleasePoolPop(ctxt);
}
objc_autoreleasePoolPop内部
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
- 从C++代码可以看出__AtAutoreleasePool结构体中会调用objc_autoreleasePoolPush 和objc_autoreleasePoolPop
而objc_autoreleasePoolPush 和 objc_autoreleasePoolPop内部又会用到AutoreleasePoolPage数据结构
- 可以看autoreleasePool底层是由AutoreleasePoolPage来管理
- 调用autorelease的对象最终都是由AutoreleasePoolPage来管理的
- 下面来研究一下AutoreleasePoolPage是如何管理调用autorelease对象的
//简化后的AutoreleasePoolPage
class AutoreleasePoolPage
{
magic_t const magic;
id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;//指向父结点,第一个结点的 parent 值为 nil ;
AutoreleasePoolPage *child;//指向下一个AutoreleasePoolPage的指针
uint32_t const depth;
uint32_t hiwat;
}
- magic 用来校验 AutoreleasePoolPage 的结构是否完整;
- next 指向最新添加的 autoreleased 对象的下一个位置,初始化时指向 begin() ;
- thread 指向当前线程;
- parent 指向父结点,第一个结点的 parent 值为 nil ;
- child 指向子结点,最后一个结点的 child 值为 nil ;
- depth 代表深度,从 0 开始,往后递增 1;
- hiwat 代表 high water mark 。
当 next == begin() 时,表示 AutoreleasePoolPage 为空;当 next == end() 时,表示 AutoreleasePoolPage 已满。
push 操作
static inline void *push()
{
id *dest;
//查看是否存在page对象
if (DebugPoolAllocation) {
//如果没有创建一个新的
dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {
//如果有直接调用autoreleaseFast
dest = autoreleaseFast(POOL_BOUNDARY);
}
assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
return dest;
}
- 在调用push方法会先检查是否存在page对象,如果没有创建一个新有page,如果存在直接调用autoreleaseFast,不管存不存在都会将一个POOL_BOUNDARY入栈
不存在page的情况
- 如果没有创建新的page
id *autoreleaseNewPage(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page) return autoreleaseFullPage(obj, page);
else return autoreleaseNoPage(obj);
}
- autoreleaseFullPage方法
id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
{
// The hot page is full.
// Step to the next non-full page, adding a new page if necessary.
// Then add the object to that 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);//将POOL_BOUNDARY添加到page表里面去
}
- 先查看DebugPoolAllocation是否存在page对象,如果没有就autoreleaseNewPage创建个新的对象并在next指针的位置插入POOL_BOUNDARY
存在page的情况
- 直接调用autoreleaseFullPage
static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {
//将POOL_BOUNDAR添加到page里面
return page->add(obj);
} else if (page) {
return autoreleaseFullPage(obj, page);
} else {
return autoreleaseNoPage(obj);
}
}
当前 page 存在且没有满时,直接将对象添加到当前 page 中,即 next 指向的位置;
当前 page 存在且已满时,创建一个新的 page ,并将对象添加到新创建的 page 中;
当前 page 不存在时,即还没有 page 时,创建第一个 page ,并将对象添加到新创建的 page 中。
每调用一次 push 操作就会创建一个新的 autoreleasepool ,即往 AutoreleasePoolPage 中插入一个 POOL_SENTINEL ,并且返回插入的 POOL_SENTINEL 的内存地址。
autorelease 操作
-autorelease 方法的实现:
_objc_rootAutorelease(id obj)
{
assert(obj);
return obj->rootAutorelease();
}
通过查看 ((id)self)->rootAutorelease() 的方法调用,我们发现最终调用的就是 AutoreleasePoolPage 的 autorelease 函数。
objc_object::rootAutorelease()
{
if (isTaggedPointer()) return (id)this;
if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;
return rootAutorelease2();
}
id
objc_object::rootAutorelease2()
{
assert(!isTaggedPointer());
return AutoreleasePoolPage::autorelease((id)this);
}
AutoreleasePoolPage 的 autorelease 函数的实现对我们来说就比较容易理解了,它跟 push 操作的实现非常相似。只不过 push 操作插入的是一个 POOL_SENTINEL ,而 autorelease 操作插入的是一个具体的 autoreleased 对象。
static inline id autorelease(id obj)
{
assert(obj);
assert(!obj->isTaggedPointer());
id *dest __unused = autoreleaseFast(obj);
assert(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj);
return obj;
}
pop 操作
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
static inline void pop(void *token)
{
AutoreleasePoolPage *page;
id *stop;
if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
// Popping the top-level placeholder pool.
if (hotPage()) {
// Pool was used. Pop its contents normally.
// Pool pages remain allocated for re-use as usual.
pop(coldPage()->begin());
} else {
// Pool was never used. Clear the placeholder.
setHotPage(nil);
}
return;
}
pop 函数的入参就是 push 函数的返回值,也就是 POOL_SENTINEL 的内存地址,即 pool token 。当执行 pop 操作时,内存地址在 pool token 之后的所有 autoreleased 对象都会被 release 。直到 pool token 所在 page 的 next 指向 pool token 为止。