关键字:autoreleasePoolpage组成的双向链表结构,
1、enter (进入runloop) -> objc_autoreleasePoolPush()
_BeforeWaiting(休眠前) ->objc_autoreleasePoolPush()、objc_autoreleasePoolPop()
_Exit(离开) objc_autoreleasePoolPop()
2、push:page->add(obj)、autoreleaseFullPage(obj, page);、autoreleaseNoPage(obj);
pop:releaseUntil、page->kill(); setHotPage(parent);
3、 autorelaeasepool、NSRunLoop 、子线程三者的关系
1.主线程默认为我们开启 Runloop,Runloop 会自动帮我们创建Autoreleasepool,并进行Push、Pop 等操作来进行内存管理。
2.子线程默认不开启runloop,当产生autorelease对象时候,会将对象添加到最近一次创建的autoreleasepool中,一般是main函数中的autoreleasepool,由主线程runloop管理;也就是不用手动创建Autoreleasepool,线程销毁时在会在最近一次创建的autoreleasepool 中释放对象。
3.自定义的 NSOperation 和 NSThread 需要手动创建自动释放池。比如: 自定义的 NSOperation 类中的 main 方法里就必须添加自动释放池。否则出了作用域后,自动释放对象会因为没有自动释放池去处理它,而造成内存泄露。
但对于 blockOperation 和 invocationOperation 这种默认的Operation ,系统已经帮我们封装好了,不需要手动创建自动释放池。
4.AutoreleasePool是按线程一一对应的(结构中的thread指针指向当前线程),每开一个线程,会有与之对应的AutoreleasePool。
autoreleasePoolPage结构
struct AutoreleasePoolPageData
{
magic_t const magic; // 校验atoreleasepool的完整性
__unsafe_unretained id *next; //下一个被添加对象的地址
pthread_t const thread; // 当前的线程
AutoreleasePoolPage * const parent; //前驱结点
AutoreleasePoolPage *child; //后继结点
uint32_t const depth; //链表深度,也是当前page在链表的位置
uint32_t hiwat; //hiwat:最大入栈数量标记
};
1、push
void *
objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
static inline void *push()
{
id *dest;
if (slowpath(DebugPoolAllocation)) {
// Each autorelease pool starts on a new pool page.
dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {
dest = autoreleaseFast(POOL_BOUNDARY);
}
ASSERT(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
return dest;
}
static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) { // 有page,但是未满,直接添加
return page->add(obj);
} else if (page) { // 有page满了,去出现子page
return autoreleaseFullPage(obj, page);
} else { // 没有page就创建新page,添加边界守卫(越界标识)和obj
return autoreleaseNoPage(obj);
}
}
static __attribute__((noinline))
id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
{
do { // 创建并添加子page
if (page->child) page = page->child;
else page = new AutoreleasePoolPage(page);
} while (page->full());
setHotPage(page); // 把子page设置为当前的page
return page->add(obj); // 添加对象到page中
}
id *add(id obj)
{
ret = next; // faster than `return next-1` because of aliasing
*next++ = obj; // 当前对象当道next上,然后next+1,方便存放下一个释放对象
return ret; // 返回释放对象的存放地址
}
2、pop
objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
static void
popPage(void *token, AutoreleasePoolPage *page, id *stop)
{
page->releaseUntil(stop); // 删掉page中的对象
// 删掉page,有parent就将其parent设置为hotpage,没有就将nil设置为hotpage
if (allowDebug && DebugPoolAllocation && page->empty()) {
// special case: delete everything during page-per-pool debugging
AutoreleasePoolPage *parent = page->parent;
page->kill();
setHotPage(parent);
} else if (allowDebug && 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();
}
}
}
释放pool中的对象
void releaseUntil(id *stop)
{
while (this->next != stop) {
AutoreleasePoolPage *page = hotPage();
// fixme I think this `while` can be `if`, but I can't prove it
while (page->empty()) {
page = page->parent;
setHotPage(page);
}
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS // 支持删除重复数据
AutoreleasePoolEntry* entry = (AutoreleasePoolEntry*) --page->next;
// create an obj with the zeroed out top byte and release that
id obj = (id)entry->ptr;
int count = (int)entry->count; // grab these before memset
#else
id obj = *--page->next; // 获取当前的对象
#endif
memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
if (obj != POOL_BOUNDARY) {
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
// release count+1 times since it is count of the additional
// autoreleases beyond the first one
for (int i = 0; i < count + 1; i++) {
objc_release(obj);
}
#else
objc_release(obj); // 引用计数器 -1
#endif
}
}
setHotPage(this);
3、AutoReleasePool pop和push的时机
在afterWaiting和exit状态时调用 autoreleasePoolPop,objc_release(obj),对象的引用计数-1,进行释放
1、App启动后,系统在主线程RunLoop 里注册两个Observser,其回调都是_wrapRunLoopWithAutoreleasePoolHandler()。
2、第一个 Observer 监视的事件
是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其优先级最高,保证创建释放池发生在其他所有回调之前。
3、第二个 Observer 监视了两个事件
_BeforeWaiting(准备进入休眠) 时 _
调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;
_Exit(即将退出Loop) 时 _
调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 优先级最低,保证其释放池子发生在其他所有回调之后。
4、参数放入autoreleasepool的时机
autorelease->....->rootAutorelease2 -> autoreleaseFast
- (id)autorelease {
return _objc_rootAutorelease(self);
}
__attribute__((noinline,used))
id
objc_object::rootAutorelease2()
{
ASSERT(!isTaggedPointer());
return AutoreleasePoolPage::autorelease((id)this);
}
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;
}
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);
}
}
网友评论