AutoreleasePool(自动释放池)是OC中的一种内存自动回收机制,它能够延迟加入AutoreleasePool中的变量release的时机。在正常状况下,建立的变量会在超出其做用域的时候release,可是若是将变量加入AutoreleasePool,那么release将延迟执行。bash
也就是说AutoreleasePool建立是在一个RunLoop事件开始以前(push),AutoreleasePool释放是在一个RunLoop事件即将结束以前(pop)。 AutoreleasePool里的Autorelease对象的加入是在RunLoop事件中,AutoreleasePool里的Autorelease对象的释放是在AutoreleasePool释放时。less
在终端中使用clang -rewrite-objc命令将下面的OC代码重写成C++的实现:函数
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"Hello, World!");
}
return 0;
}
复制代码
在cpp文件代码中咱们找到main函数代码以下:oop
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_kb_06b822gn59df4d1zt99361xw0000gn_T_main_d39a79_mi_0);
}
return 0;
}
复制代码
能够看到苹果经过声明一个__AtAutoreleasePool类型的局部变量__autoreleasepool实现了@autoreleasepool{}。 __AtAutoreleasePool
的定义以下:ui
extern "C" __declspec(dllimport) void * objc_autoreleasePoolPush(void);
extern "C" __declspec(dllimport) void objc_autoreleasePoolPop(void *);
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
复制代码
根据构造函数和析构函数的特色(自动局部变量的构造函数是在程序执行到声明这个对象的位置时调用的,而对应的析构函数是在程序执行到离开这个对象的做用域时调用),咱们能够将上面两段代码简化成以下形式:this
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ {
void *atautoreleasepoolobj = objc_autoreleasePoolPush();
NSLog((NSString *)&__NSConstantStringImpl__var_folders_kb_06b822gn59df4d1zt99361xw0000gn_T_main_d39a79_mi_0);
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
return 0;
}
复制代码
至此,咱们能够分析出,单个自动释放池的执行过程就是objc_autoreleasePoolPush()
—> [object autorelease]
—> objc_autoreleasePoolPop(void *)
。spa
来看一下objc_autoreleasePoolPush 和 objc_autoreleasePoolPop 的实现:线程
void *objc_autoreleasePoolPush(void) {
return AutoreleasePoolPage::push();
}
void objc_autoreleasePoolPop(void *ctxt) {
AutoreleasePoolPage::pop(ctxt);
}
复制代码
上面的方法看上去是对 AutoreleasePoolPage 对应静态方法 push 和 pop 的封装。 下面分析一下AutoreleasePoolPage的实现,揭开AutoreleasePool的实现原理。debug
AutoreleasePoolPage 是一个 C++ 中的类,它在 NSObject.mm 中的定义是这样的:指针
class AutoreleasePoolPage {
# define EMPTY_POOL_PLACEHOLDER ((id*)1)
# define POOL_BOUNDARY nil
static pthread_key_t const key = AUTORELEASE_POOL_KEY;
static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing
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);
magic_t const magic;
id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
};
复制代码
AutoreleasePool并无单独的结构,而是由若干个AutoreleasePoolPage以双向链表的形式组合而成的栈结构(分别对应结构中的parent指针和child指针)
parent和child就是用来构造双向链表的指针。parent指向前一个page, child指向下一个page。 一个AutoreleasePoolPage的空间被占满时,会新建一个AutoreleasePoolPage对象,链接链表,后来的autorelease对象在新的page加入。
每当自动释放池调用objc_autoreleasePoolPush时都会把边界对象放进栈顶,而后返回边界对象,用于释放。
atautoreleasepoolobj = objc_autoreleasePoolPush();
复制代码
atautoreleasepoolobj就是返回的边界对象(POOL_BOUNDARY)
push实现以下:
void *objc_autoreleasePoolPush(void) {
return AutoreleasePoolPage::push();
}
复制代码
它调用AutoreleasePoolPage的类方法push:
static inline void *push() {
return autoreleaseFast(POOL_BOUNDARY);
}
复制代码
在这里会进入一个比较关键的方法autoreleaseFast,并传入边界对象(POOL_BOUNDARY):
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->add(obj) 将对象添加到自动释放池中。 hotPage 能够理解为当前正在使用的 AutoreleasePoolPage。
autorelease方法的实现,先来看一下方法的调用栈:
- [NSObject autorelease]
└── id objc_object::rootAutorelease()
└── id objc_object::rootAutorelease2()
└── static id AutoreleasePoolPage::autorelease(id obj)
└── static id AutoreleasePoolPage::autoreleaseFast(id obj)
├── id *add(id obj)
├── static id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
│ ├── AutoreleasePoolPage(AutoreleasePoolPage *newParent)
│ └── id *add(id obj)
└── static id *autoreleaseNoPage(id obj)
├── AutoreleasePoolPage(AutoreleasePoolPage *newParent)
└── id *add(id obj)
复制代码
在autorelease方法的调用栈中,最终都会调用上面提到的 autoreleaseFast方法,将当前对象加到AutoreleasePoolPage 中。
这一小节中这些方法的实现都很是容易,只是进行了一些参数上的检查,最终还要调用autoreleaseFast方法:
inline id objc_object::rootAutorelease() {
if (isTaggedPointer()) return (id)this;
if (prepareOptimizedReturn(ReturnAtPlus1)) return (id)this;
return rootAutorelease2();
}
__attribute__((noinline,used)) id objc_object::rootAutorelease2() {
return AutoreleasePoolPage::autorelease((id)this);
}
static inline id autorelease(id obj) {
id *dest __unused = autoreleaseFast(obj);
return obj;
}
复制代码
autorelease函数和push函数同样,关键代码都是调用autoreleaseFast函数向自动释放池的链表栈中添加一个对象, 不过push函数的入栈的是一个边界对象,而autorelease函数入栈的是须要加入autoreleasepool的对象。
自动释放池释放是传入 push 返回的边界对象,
objc_autoreleasePoolPop(atautoreleasepoolobj);
复制代码
而后将边界对象指向的这一页 AutoreleasePoolPage 内的对象释放 atautoreleasepoolobj就是返回的边界对象(POOL_BOUNDARY)
AutoreleasePoolPage::pop()实现:
static inline void pop(void *token) // token指针指向栈顶的地址
{
AutoreleasePoolPage *page;
id *stop;
page = pageForPointer(token); // 经过栈顶的地址找到对应的page
stop = (id *)token;
if (DebugPoolAllocation && *stop != POOL_SENTINEL) {
// This check is not valid with DebugPoolAllocation off
// after an autorelease with a pool page but no pool in place.
_objc_fatal("invalid or prematurely-freed autorelease pool %p; ",
token);
}
if (PrintPoolHiwat) printHiwat(); // 记录最高水位标记
page->releaseUntil(stop); // 从栈顶开始操做出栈,并向栈中的对象发送release消息,直到遇到第一个哨兵对象
// memory: delete empty children
// 删除空掉的节点
if (DebugPoolAllocation && page->empty()) {
// special case: delete everything during page-per-pool debugging
AutoreleasePoolPage *parent = page->parent;
page->kill();
setHotPage(parent);
} else if (DebugMissingPools && page->empty() && !page->parent) {
// special case: delete everything for pop(top)
// when debugging missing autorelease pools
page->kill();
setHotPage(nil);
}
else if (page->child) {
// hysteresis: keep one empty child if page is more than half full
if (page->lessThanHalfFull()) {
page->child->kill();
}
else if (page->child->child) {
page->child->child->kill();
}
}
}
复制代码
该过程主要分为两步:
// hysteresis: keep one empty child if this page is more than half full
// special case: delete everything for pop(0)
除非是pop(0)方式调用,这样会清理掉全部page对象;
不然,在当前page存放的对象大于一半时,会保留一个空的子page,
这样估计是为了可能立刻须要新建page节省建立page的开销。
复制代码