iOS底层原理探索 ----- 内存管理(三)
资源准备
自动释放池
概述
AutoReleasePool
:自动释放池
OC
中的一种内存自动回收机制,它可以将加入AutoreleasePool
中的变量release
的时机延迟。
简单来说,当创建一个对象,在正常情况下,变量会在超出其作用域时立即release
。如果将其加入到自动释放池中,这个对象并不会立即释放,而会等到runloop
休眠/超出autoreleasepool
作用域之后进行释放。
-
从程序启动到加载完成,主线程对应的
Runloop
会处于休眠状态,等待用户交互来唤醒Runloop
; -
用户每次交互都会启动一次
Runloop
,用于处理用户的所有点击、触摸等事件; -
Runloop
在监听到交互事件后,就会创建自动释放池,并将所有延迟释放的对象添加到自动释放池中; -
在一次完整的
Runloop
结束之前,会向自动释放池中所有对象发送release
消息,然后销毁自动释放池。
结构
使用cpp
文件探索
创建一个Mac
工程,在main.m
中,自动生成以下代码:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"Hello, World!");
}
return 0;
}
转化生成cpp
文件:
clang -rewrite-objc main.m -o main.cpp
打开cpp
文件,来到main
函数:
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ {
__AtAutoreleasePool __autoreleasepool;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_jl_d06jlfkj2ws74_5g45kms07m0000gn_T_main_da0d58_mi_0);
}
return 0;
}
-
autoreleasepool
被注释掉了,但作用域还在; -
作用域中生成对
__AtAutoreleasePool
类型声明的代码。
找到__AtAutoreleasePool
定义:
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
-
__AtAutoreleasePool
是一个结构体,包含构造函数和析构函数; -
结构体声明,触发构造函数,调用
objc_autoreleasePoolPush
函数; -
当结构体出作用域空间,触发析构函数,调用
objc_autoreleasePoolPop
函数。
使用汇编代码探索
打开项目,在main
函数中autoreleasepool
处设置断点,查看汇编代码:
-
调用
objc_autoreleasePoolPush
函数; -
调用
objc_autoreleasePoolPop
函数。
进入objc_autoreleasePoolPush
函数:
- 源码来自于
libobjc
框架。
源码探索
打开objc4-818.2
源码,找到objc_autoreleasePoolPush
函数:
void
* objc_autoreleasePoolPush(void) {
return AutoreleasePoolPage::push();
}
- 调用
AutoreleasePoolPage
命名空间下的push
函数。
AutoreleasePoolPage
找到AutoreleasePoolPage
的定义,首先看到这样一段注释:
/*********************************************************************** Autorelease pool implementation
A thread's autorelease pool is a stack of pointers.
线程的自动释放池是一个指针堆栈
Each pointer is either an object to release, or POOL_BOUNDARY which is an autorelease pool boundary.
每个指针要么是一个要释放的对象,要么是POOL_BOUNDARY自动释放池边界
A pool token is a pointer to the POOL_BOUNDARY for that pool. When the pool is popped, every object hotter than the sentinel is released.
池令牌是指向该池的POOL_BOUNDARY的指针。当池被弹出,每个比哨兵热的对象都被释放
The stack is divided into a doubly-linked list of pages. Pages are added and deleted as necessary.
堆栈被分成一个双链接的页面列表。根据需要添加和删除页面
Thread-local storage points to the hot page, where newly autoreleased objects are stored.
线程本地存储指向热页,其中存储新自动释放的对象 **********************************************************************/
通过注释我们可以了解到以下几点:
-
自动释放池和线程有关系;
-
自动释放池是一个存储指针的栈结构;
-
指针要么是一个要释放的对象,要么是
POOL_BOUNDARY
自动释放池边界,俗称:哨兵对象:- 哨兵对象的作用:当自动释放池将对象进行
pop
操作时,需要知道边界在哪里,否则会破坏别人的内存空间。而哨兵对象,就是作为边界的标识而存在。
- 哨兵对象的作用:当自动释放池将对象进行
-
自动释放池的栈空间被分成一个双链接结构的页面列表,可添加和删除页面:
- 双向链表的特别,一个页中同时存在父节点和子节点。可向前找到父页面,也可向后找到子页面。
-
线程本地存储指向热页,其中存储新自动释放的对象:
- 栈原则,先进后出,可以理解为最后一个页面就是热页。里面的对象最后被
push
,最先被pop
。
- 栈原则,先进后出,可以理解为最后一个页面就是热页。里面的对象最后被
AutoreleasePoolPage
继承于AutoreleasePoolPageData
:
class AutoreleasePoolPage : private AutoreleasePoolPageData {
friend struct thread_data_t;
public:
static size_t const SIZE =
#if PROTECT_AUTORELEASEPOOL
PAGE_MAX_SIZE; // must be multiple of vm page size
#else
PAGE_MIN_SIZE; // size and alignment, power of 2
#endif
private:
static pthread_key_t const key = AUTORELEASE_POOL_KEY;
static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing
static size_t const COUNT = SIZE / sizeof(id);
static size_t const MAX_FAULTS = 2;
...
}
AutoreleasePoolPageData
找到AutoreleasePoolPageData
的定义:
class AutoreleasePoolPage;
struct AutoreleasePoolPageData {
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
struct AutoreleasePoolEntry {
uintptr_t ptr: 48;
uintptr_t count: 16;
static const uintptr_t maxCount = 65535; // 2^16 - 1
};
static_assert((AutoreleasePoolEntry) {.ptr = MACH_VM_MAX_ADDRESS }.ptr == MACH_VM_MAX_ADDRESS, "MACH_VM_MAX_ADDRESS doesn't fit into AutoreleasePoolEntry::ptr!");
#endif
magic_t const magic;
__unsafe_unretained id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
AutoreleasePoolPageData(__unsafe_unretained id* _next, pthread_t _thread, AutoreleasePoolPage* _parent, uint32_t _depth, uint32_t _hiwat) :
magic(), next(_next), thread(_thread),
parent(_parent), child(nil),
depth(_depth), hiwat(_hiwat) { }
};
结构体中,包含以下成员变量:
-
magic
:用来校验AutoreleasePoolPage
的结构是否完整; -
next
:指向最新添加的autoreleased
对象的下一个位置,初始化时执行begin()
; -
thread
:指向当前线程; -
parent
:指向父节点,第一个节点的parent
值为nil
; -
child
:指向子节点,最后一个节点的child
值为nil
; -
depth
:代表深度,从0
开始,往后递增1
; -
hiwat
:代表high water mark
最大入栈数量标记。
打印结构
搭建测试项目,关闭ARC
模式:
打开main.m
文件,写入以下代码:
extern void _objc_autoreleasePoolPrint(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *objc = [[[NSObject alloc] init] autorelease];
_objc_autoreleasePoolPrint();
}
return 0;
}
-
导入
_objc_autoreleasePoolPrint
函数,用于打印自动释放池的结构; -
创建NSObject实例对象,加入自动释放池;
-
调用
_objc_autoreleasePoolPrint
函数,打印结构。
输出以下内容:
##############
AUTORELEASE POOLS for thread 0x1000ebe00
2 releases pending.
[0x10700b000] ................ PAGE (hot) (cold)
[0x10700b038] ################ POOL 0x10700b038
[0x10700b040] 0x100705f60 NSObject
##############
-
打印出当前自动释放池所属线程
-
有
2
个需要释放的对象 -
当前的
Page
信息,占56字节
。因为只有一页,即是冷页面,也是热页面 -
哨兵对象
POOL
-
NSObject
对象
_objc_autoreleasePoolPrint
官方用于对自动释放池内容调试打印的函数:
void
_objc_autoreleasePoolPrint(void) {
AutoreleasePoolPage::printAll();
}
进入printAll
函数
static void printAll() {
_objc_inform("##############");
_objc_inform("AUTORELEASE POOLS for thread %p", objc_thread_self());
AutoreleasePoolPage *page;
ptrdiff_t objects = 0;
for (page = coldPage(); page; page = page->child) {
objects += page->next - page->begin();
}
_objc_inform("%llu releases pending.", (unsigned long long)objects);
if (haveEmptyPoolPlaceholder()) {
_objc_inform("[%p] ................ PAGE (placeholder)", EMPTY_POOL_PLACEHOLDER);_objc_inform("[%p] ################ POOL (placeholder)", EMPTY_POOL_PLACEHOLDER);
} else {
for (page = coldPage();
page; page = page->child) {
page->print();
}
}
_objc_inform("##############");
}
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
__attribute__((noinline, cold)) unsigned sumOfExtraReleases() {
unsigned sumOfExtraReleases = 0;
for (id *p = begin(); p < next; p++) {
if (*p != POOL_BOUNDARY) {
sumOfExtraReleases += ((AutoreleasePoolEntry *)p)->count;
}
}
return sumOfExtraReleases;
}
#endif
__attribute__((noinline, cold)) static void printHiwat() {
// Check and propagate high water mark
// Ignore high water marks under 256 to suppress noise.
AutoreleasePoolPage *p = hotPage();
uint32_t mark = p->depth*COUNT + (uint32_t)(p->next - p->begin());
if (mark > p->hiwat + 256) {
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
unsigned sumOfExtraReleases = 0;
#endif
for( ; p; p = p->parent) {
p->unprotect();
p->hiwat = mark;
p->protect();
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
sumOfExtraReleases += p->sumOfExtraReleases();
#endif
}
_objc_inform("POOL HIGHWATER: new high water mark of %u "
"pending releases for thread %p:",
mark, objc_thread_self());
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
if (sumOfExtraReleases > 0) {
_objc_inform("POOL HIGHWATER: extra sequential autoreleases of objects: %u", sumOfExtraReleases);
}
#endif
void *stack[128];
int count = backtrace(stack, sizeof(stack)/sizeof(stack[0]));
char **sym = backtrace_symbols(stack, count);
for (int i = 0; i < count; i++) {
_objc_inform("POOL HIGHWATER: %s", sym[i]);
}
free(sym);
}
}
#undef POOL_BOUNDARY
};
- 按照自动释放池的结构,通过双向链表遍历
page
,依次读取page
中的内容并进行打印。
对象压栈
进入objc_autoreleasePoolPush
函数:
void *
objc_autoreleasePoolPush(void) {
return AutoreleasePoolPage::push();
}
进入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;
}
-
DebugPoolAllocation
:当自动释放池按顺序弹出时停止,并允许堆调试器跟踪自动释放池; -
不存在,调用
autoreleaseNewPage
函数,从一个新的池页开始创建; -
否则,调用
autoreleaseFast
函数,将哨兵对象压栈。
autoreleaseFast
进入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);
}
}
-
如果存在
page
,并且没有存满,调用add
函数; -
如果存在
page
,但存储已满,调用autoreleaseFullPage
函数; -
否则,不存在
page
,调用autoreleaseNoPage
函数。
autoreleaseNoPage
进入autoreleaseNoPage
函数:
static __attribute__((noinline))
id *autoreleaseNoPage(id obj) {
// "No page" could mean no pool has been pushed
// or an empty placeholder pool has been pushed and has no contents yet
ASSERT(!hotPage());
bool pushExtraBoundary = false;
...
// We are pushing an object or a non-placeholder'd pool.
// Install the first page.
AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
setHotPage(page);
// Push a boundary on behalf of the previously-placeholder'd pool.
if (pushExtraBoundary) {
page->add(POOL_BOUNDARY);
}
// Push the requested object or pool.
return page->add(obj);
}
-
调用
AutoreleasePoolPage
构造函数,创建新页; -
设置为热页面;
-
pushExtraBoundary
为YES
,哨兵对象压栈; -
对象压栈。
进入AutoreleasePoolPage
构造函数:
AutoreleasePoolPage(AutoreleasePoolPage *newParent) :
AutoreleasePoolPageData(begin(),
objc_thread_self(),
newParent, newParent ?
1+newParent->depth : 0, newParent ?
newParent->hiwat : 0) {
if (objc::PageCountWarning != -1) {
checkTooMuchAutorelease();
}
if (parent) {
parent->check();
ASSERT(!parent->child);
parent->unprotect();
parent->child = this;
parent->protect();
}
protect();
}
-
通过父类
AutoreleasePoolPageData
进行初始化; -
begin
:获取对象压栈的起始位置; -
objc_thread_self
:通过tls
获取当前线程; -
链接双向链表。
进入begin
函数:
id * begin() {
return (id *) ((uint8_t *)this+sizeof(*this));
}
-
sizeof(*this)
:大小取决于自身结构体中的成员变量; -
返回对象可压栈的真正开始地址,在成员变量以下。
进入AutoreleasePoolPageData
的定义:
struct AutoreleasePoolPageData {
...
magic_t const magic;
__unsafe_unretained id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
...
};
-
magic
:16字节
; -
next
、thread
、parent
、child
:各占8字节
; -
depth
、hiwat
:各占4字节
; -
共占
56字节
。
进入magic_t
的定义:
struct magic_t {
static const uint32_t M0 = 0xA1A1A1A1;
# define M1 "AUTORELEASE!"
static const size_t M1_len = 12;
uint32_t m[4];
...
# undef M1
};
-
结构体的创建在堆区申请内存,而静态成员存储在静态区,不占结构体大小;
-
16字节
来自于uint32_t
数组。
进入objc_thread_self
函数:
static inline pthread_t objc_thread_self() {
return (pthread_t)tls_get_direct(_PTHREAD_TSD_SLOT_PTHREAD_SELF);
}
- 通过
tls
获取当前线程。
autoreleaseFullPage
进入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);
}
-
遍历链表,找到最后一个空白的子页面;
-
对其进行创建新页;
-
设置为热页面;
-
添加对象。 形成以下数据结构:
add
进入add
函数:
id *add(id obj) {
ASSERT(!full());
unprotect();
id *ret;
...
ret = next;
// faster than `return next-1` because of aliasing
*next++ = obj;
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
// Make sure obj fits in the bits available for it
ASSERT(((AutoreleasePoolEntry *)ret)->ptr == (uintptr_t)obj);
#endif
done:
protect();
return ret;
}
-
使用
*next++
进行内存平移; -
将对象压栈。
autoreleaseNewPage
进入autoreleaseNewPage
函数:
id
*autoreleaseNewPage(id obj) {
AutoreleasePoolPage *page = hotPage();
if (page)
return autoreleaseFullPage(obj, page);
else
return autoreleaseNoPage(obj);
}
-
获取热页面;
-
存在,调用
autoreleaseFullPage
函数; -
否则,不存在
page
,调用autoreleaseNoPage
函数。
autorelease
进入objc_autorelease
函数:
id
objc_autorelease(id obj) {
if (obj->isTaggedPointerOrNil()) return obj;
return obj->autorelease();
}
-
当前对象为
TaggedPointer
或nil
,直接返回; -
否则,调用对象的
autorelease
函数。
进入autorelease
函数:
inline id
objc_object::autorelease() {
ASSERT(!isTaggedPointer());
if (fastpath(!ISA()->hasCustomRR())) {
return rootAutorelease();
}
return ((id(*)(objc_object *, SEL))objc_msgSend)(this, @selector(autorelease));
}
- 如果是自定义类,调用
rootAutorelease
函数。
进入rootAutorelease
--> rootAutorelease2
--> AutoreleasePoolPage::autorelease
函数:
inline id
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);
}
static inline id autorelease(id obj) {
ASSERT(!obj->isTaggedPointerOrNil());
id *dest __unused = autoreleaseFast(obj);
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
ASSERT(!dest || dest == EMPTY_POOL_PLACEHOLDER || (id)((AutoreleasePoolEntry *)dest)->ptr == obj);
#else
ASSERT(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj);
#endif
return obj;
}
- 调用
autoreleaseFast
函数,对象压栈。
池页容量
自动释放池采用分页的方式存储对象,因为对象在频繁压栈和出栈的过程中,产生异常,值会影响当前页面,不会影响到整个自动释放池。
并且自动释放池分页管理,每页之前的地址可以不连续,它们可以使用双向链表找到父页面和子页面。如果所有对象都使用一页存储,为了保证地址的连续性,每次扩容会相对繁琐和耗时:
int main(int argc, const char * argv[]) {
@autoreleasepool {
for (int i = 0; i < 505; i++) {
NSObject *objc = [[[NSObject alloc] init] autorelease];
}
_objc_autoreleasePoolPrint();
}
return 0;
}
打印结果:
objc[1804]: ##############
objc[1804]: AUTORELEASE POOLS for thread 0x1000ebe00
objc[1804]: 506 releases pending.
objc[1804]: [0x10200c000] ................ PAGE (full) (cold)
objc[1804]: [0x10200c038] ################ POOL 0x10200c038
objc[1804]: [0x10200c040] 0x100638420 NSObject
objc[1804]: [0x10200c048] 0x100637a40 NSObject
objc[1804]: [0x10200c050] 0x100636970 NSObject
...
objc[1804]: [0x100809000] ................ PAGE (hot)
objc[1804]: [0x100809038] 0x10063a0b0 NSObject
objc[1804]: ##############
-
将
505
个NSObject
对象循环加入自动释放池,当存储504
个对象时,池页已满。第505
个对象创建新池页存储; -
一页的容量:
504 * 8 = 4032
,加上56字节
成员变量和8字节
哨兵对象,共计4096
字节; -
每一页都存在
56字节
的成员变量; -
一个自动释放池,只会压栈一个哨兵对象。
在源码中查看:
class AutoreleasePoolPage : private AutoreleasePoolPageData {
friend struct thread_data_t;
public:
static size_t const SIZE =
#if PROTECT_AUTORELEASEPOOL
PAGE_MAX_SIZE; // must be multiple of vm page size
#else
PAGE_MIN_SIZE; // size and alignment, power of 2
#endif
...
}
来到PAGE_MIN_SIZE
的定义:
#define PAGE_MIN_SHIFT 12
#define PAGE_MIN_SIZE (1 << PAGE_MIN_SHIFT)
1
左移12
位,相当于2 ^ 12 = 4096
。
对象出栈
进入objc_autoreleasePoolPop
函数:
void
objc_autoreleasePoolPop(void *ctxt) {
AutoreleasePoolPage::pop(ctxt);
}
进入pop
函数:
static inline void
pop(void *token) {
AutoreleasePoolPage *page;
id *stop;
//判断当前对象是否为空占位符
if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
// Popping the top-level placeholder pool.
//获取热页面
page = hotPage();
if (!page) {
// Pool was never used. Clear the placeholder.
//不存在热页面,将标记设置为nil
return setHotPage(nil);
}
// Pool was used. Pop its contents normally.
// Pool pages remain allocated for re-use as usual.
//存在热页面,通过双向链表循环向上找到最冷页面
page = coldPage();
//将token设置为起始位置
token = page->begin();
} else {
//获取token所在的页
page = pageForPointer(token);
}
//赋值给stop
stop = (id *)token;
//当前位置不是哨兵对象
if (*stop != POOL_BOUNDARY) {
if (stop == page->begin() && !page->parent) {
// Start of coldest page may correctly not be POOL_BOUNDARY:
// 1. top-level pool is popped, leaving the cold page in place
// 2. an object is autoreleased with no pool
//最冷页面的起始可能不是POOL_BOUNDARY:
//1. 弹出顶级池,保留冷页面
//2. 对象在没有池的情况下被自动释放
} else {
// Error. For bincompat purposes this is not
// fatal in executables built with old SDKs.
//出现异常情况
return badPop(token);
}
}
if (slowpath(PrintPoolHiwat || DebugPoolAllocation || DebugMissingPools)) {
return popPageDebug(token, page, stop);
}
//出栈
return popPage<false>(token, page, stop);
}
popPage
进入popPage
函数:
static void
popPage(void *token, AutoreleasePoolPage *page, id *stop) {
if (allowDebug && PrintPoolHiwat) printHiwat();
//当前页中对象出栈,到stop位置停止
page->releaseUntil(stop);
// memory: delete empty children
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
//特殊情况:删除所有的pop
//销毁当前页面
page->kill();
//将热页面标记设置为nil
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();
}
}
}
releaseUntil
进入releaseUntil
函数:
void releaseUntil(id *stop) {
// Not recursive: we don't want to blow out the stack
// if a thread accumulates a stupendous amount of garbage
//向下遍历,到stop停止
while (this->next != stop) {
// Restart from hotPage() every time, in case -release
// autoreleased more objects
//获取热页面
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);
}
page->unprotect();
#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));
page->protect();
//当前对象不是哨兵对象
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);
#endif
}
}
//将当前页面标记为热页面
setHotPage(this);
#if DEBUG
// we expect any children to be completely empty
for (AutoreleasePoolPage *page = child; page; page = page->child) {
ASSERT(page->empty());
}
#endif
}
kill
进入kill
函数:
void kill() {
// Not recursive: we don't want to blow out the stack
// if a thread accumulates a stupendous amount of garbage
AutoreleasePoolPage *page = this;
//循环找到最后一个子页面
while (page->child) page = page->child;
AutoreleasePoolPage *deathptr;
do {
deathptr = page;
//找到父页面
page = page->parent;
if (page) {
//将子页面设置为nil
page->unprotect();
page->child = nil;
page->protect();
}
//销毁子页面
delete deathptr;
//遍历销毁到this为止
} while (deathptr != this);
}
嵌套使用
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *objc = [[[NSObject alloc] init] autorelease];
@autoreleasepool {
NSObject *objc = [[[NSObject alloc] init] autorelease];
}
_objc_autoreleasePoolPrint();
}
return 0;
}
打印结果:
objc[2511]: ##############
objc[2511]: AUTORELEASE POOLS for thread 0x1000ebe00
objc[2511]: 4 releases pending.
objc[2511]: [0x10680d000] ................ PAGE (hot) (cold)
objc[2511]: [0x10680d038] ################ POOL 0x10680d038
objc[2511]: [0x10680d040] 0x101370c40 NSObject
objc[2511]: [0x10680d048] ################ POOL 0x10680d048
objc[2511]: [0x10680d050] 0x101365fb0 NSObject
objc[2511]: ##############
-
自动释放池可以嵌套使用,存储结构和常规使用没有区别;
-
子释放池存在自己的哨兵对象。
ARC
模式
将项目设置为ARC
模式:
创建LGPerson
类:
#import <Foundation/Foundation.h>
@interface LGPerson : NSObject
+ (LGPerson *)person;
@end
@implementation LGPerson
+ (LGPerson *)person {
LGPerson *p = [[LGPerson alloc] init];
return p;
}
@end
在main
函数中,写入以下代码:
extern void _objc_autoreleasePoolPrint(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *p1 = [[LGPerson alloc] init];
LGPerson *p2 = [LGPerson person];
_objc_autoreleasePoolPrint();
}
return 0;
}
打印结果:
objc[2699]: ##############
objc[2699]: AUTORELEASE POOLS for thread 0x1000ebe00
objc[2699]: 2 releases pending.
objc[2699]: [0x10200e000] ................ PAGE (hot) (cold)
objc[2699]: [0x10200e038] ################ POOL 0x10200e038
objc[2699]: [0x10200e040] 0x1013658b0 LGPerson
objc[2699]: ##############
- 只有
p2
对象压栈到自动释放池。
ARC
模式,使用alloc
、new
、copy
、mutableCopy
前缀开头的方法进行对象创建,不会加入到自动释放池。它们的空间开辟由开发者申请,释放也由开发者进行管理。
转载自:https://juejin.cn/post/7007703060046675982