我們在分析自動釋放池底層源碼前茵臭,我們先來創(chuàng)建一個新工程,查看main
函數(shù)中系統(tǒng)創(chuàng)建的自動釋放池最終轉換為底層c++代碼的情況
main
函數(shù)
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
}
return 0;
}
我們執(zhí)行命令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
將main.m
文件轉換為底層c++文件钻心,核心代碼如下:
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ {
__AtAutoreleasePool __autoreleasepool;
}
return 0;
}
從上面的轉換我們可以看出,將創(chuàng)建自動釋放池的代碼@autoreleasepool{}
轉換為了底層代碼__AtAutoreleasePool __autoreleasepool;
__AtAutoreleasePool __autoreleasepool;
聲明了一個__AtAutoreleasePool
類型的變量铅协,我們再來看看__AtAutoreleasePool
的類型捷沸,我們通過在main.cpp
文件中搜索,發(fā)現(xiàn)__AtAutoreleasePool
為一個結構體對象狐史,源碼如下:
extern "C" __declspec(dllimport) void * objc_autoreleasePoolPush(void);
extern "C" __declspec(dllimport) void objc_autoreleasePoolPop(void *);
struct __AtAutoreleasePool {
// 構造函數(shù)痒给,在初始化創(chuàng)建結構體的時候調用
__AtAutoreleasePool() {
atautoreleasepoolobj = objc_autoreleasePoolPush();
}
// 析構函數(shù)说墨,在結構體對象銷毀的時候調用
~__AtAutoreleasePool() {
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
void * atautoreleasepoolobj;
};
我們發(fā)現(xiàn)在__AtAutoreleasePool
結構體的構造函數(shù)中調用了objc_autoreleasePoolPush()
函數(shù),在析構函數(shù)中調用了objc_autoreleasePoolPop()
函數(shù)
我們知道創(chuàng)建自動釋放池除了使用@autoreleasepool{}
這種方式苍柏,我們還可以使用下面的面向對象的語法:
// 創(chuàng)建自動釋放池
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSObject *obj = [[NSObject alloc] init];
// 將對象添加到自動釋放池
[obj autorelease];
// 銷毀自動釋放池
[pool drain];
這兩種創(chuàng)建自動釋放池方式的等價操作如圖:
通過使用NSAutoreleasePool
的方式創(chuàng)建的自動釋放池和使用@autoreleasepool{}
這種方式最終生成的結構體__AtAutoreleasePool
進行對比尼斧,我們可以知道
@autoreleasepool { // 大括號作用域前調用:objc_autoreleasePoolPush()
NSObject *obj = [[NSObject alloc] init];
[obj autorelease];
} // 大括號作用域結束前調用:objc_autoreleasePoolPop()
@autoreleasepool {}
方式創(chuàng)建的自動釋放池,在作用域大括號的開始前會調用objc_autoreleasePoolPush()
函數(shù)试吁,在作用域大括號的結束前會調用objc_autoreleasePoolPop()
函數(shù)
然后類比使用NSAutoreleasePool
創(chuàng)建自動釋放池棺棵,在執(zhí)行NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]
時會調用objc_autoreleasePoolPush()
函數(shù),在執(zhí)行[pool drain]
時會調用objc_autoreleasePoolPop()
函數(shù)熄捍,等價于下面的代碼
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
/* 等同于objc_autoreleasePoolPush() */
NSObject *obj = [[NSObject alloc] init];
[obj autorelease];
/* 等同于objc_autorelease(obj) */
[pool drain];
/* 等同于objc_autoreleasePoolPop(pool) */
上面我們一直提到objc_autoreleasePoolPush
和objc_autoreleasePoolPop
函數(shù)烛恤,接下來我們再來看看這兩個函數(shù)的具體作用,源碼查看路徑:objc4 -> NSObject.mm -> void * objc_autoreleasePoolPush(void)
源碼對這兩個函數(shù)的定義如下:
void * objc_autoreleasePoolPush(void)
{
return AutoreleasePoolPage::push();
}
void objc_autoreleasePoolPop(void *ctxt)
{
AutoreleasePoolPage::pop(ctxt);
}
通過上面的源碼我們看一看到Push和Pop函數(shù)最終都是通過AutoreleasePoolPage
來調用的余耽,接下來我們再來看看AutoreleasePoolPage
的源碼缚柏,源碼查看路徑:objc4 -> NSObject.mm -> AutoreleasePoolPage
通過源碼查看我們發(fā)現(xiàn)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.
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.
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.
**********************************************************************/
class AutoreleasePoolPage
{
// ************** AutoreleasePoolPage類的七個成員變量 **************
magic_t const magic;
// 這個指針是指Page可以存放下一個autorelease對象的地址值
id *next;
// 當前所在的線程
pthread_t const thread;
// 指向上一個page對象
AutoreleasePoolPage * const parent;
// 指向下一個page對象
AutoreleasePoolPage *child;
// 自動釋放池Page的深度
uint32_t const depth;
uint32_t hiwat;
// ************** AutoreleasePoolPage類的核心函數(shù) **************
// autorelease:對象調用autorelease方法碟贾,將對象添加到自動釋放池中
public:
static inline id autorelease(id obj)
{
assert(obj);
assert(!obj->isTaggedPointer());
// 通過autoreleaseFast函數(shù)币喧,找到這個對象的內(nèi)存地址
id *dest __unused = autoreleaseFast(obj);
assert(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj);
return obj;
}
// AutoreleasePoolPage類中的 push 函數(shù)
static inline void *push()
{
id *dest;
if (DebugPoolAllocation) {
// Each autorelease pool starts on a new pool page.
// 當沒有Page時,創(chuàng)建一個Page缕陕,將POOL_BOUNDARY放入到page中入棧粱锐,然后返回這個位置的內(nèi)存地址值
dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {
// 已經(jīng)有Page
dest = autoreleaseFast(POOL_BOUNDARY);
}
assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
// 返回值,也就是Page開始存放對象時POOL_BOUNDARY入棧時對應Page中的內(nèi)存地址
return dest;
}
// AutoreleasePoolPage類中的Pop函數(shù)扛邑,這里的token就是POOL_BOUNDARY
// pop函數(shù)會從棧底開始一直往上釋放對象怜浅,直到釋放到POOL_BOUNDARY的位置截止
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;
}
page = pageForPointer(token);
// 將token(POOL_BOUNDARY)賦值給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
} else {
// Error. For bincompat purposes this is not
// fatal in executables built with old SDKs.
return badPop(token);
}
}
if (PrintPoolHiwat) printHiwat();
// 這里我們可以看到,自動釋放池釋放對象蔬崩,是通過一個終止釋放的標記來從后往前釋放池中的對象
page->releaseUntil(stop);
// 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();
}
}
}
}
從AutoreleasePoolPage
類的核心源碼可以知道恶座,當執(zhí)行objc_autoreleasePoolPush()
函數(shù)時,就是調用AutoreleasePoolPage
類的push
函數(shù)沥阳,當執(zhí)行objc_autoreleasePoolPop()
函數(shù)就是調用AutoreleasePoolPage
類的pop
函數(shù)跨琳,當調用對象的autorelease
函數(shù)時就是調用AutoreleasePoolPage
類的autorelease
函數(shù)
我們從如下Autorelease pool
的底層源碼注釋可知:
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.
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.
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.
自動釋放池是由很多個AutoreleasePoolPage
組成的一個雙向鏈表的結構,并且每一個AutoreleasePoolPage
中存放自動釋放的對象都是以棧的形式存儲的桐罕,如下圖所示:
這里有一個比較關鍵的指針POOL_BOUNDARY
例如:當我們創(chuàng)建一個自動釋放池脉让,這時就有一個POOL_BOUNDARY
指針入棧存儲在AutoreleasePoolPage
表中,我們可以通過下面的push
函數(shù)驗證:
static inline void *push()
{
id *dest;
if (DebugPoolAllocation) {
// Each autorelease pool starts on a new pool page.
// 當沒有Page時功炮,創(chuàng)建一個Page溅潜,將POOL_BOUNDARY放入到page中入棧,然后返回這個位置的內(nèi)存地址
dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {
// 已經(jīng)有Page
dest = autoreleaseFast(POOL_BOUNDARY);
}
assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
// 返回值薪伏,也就是Page開始存放對象時POOL_BOUNDARY入棧時對應Page中的內(nèi)存地址
return dest;
}
然后我們創(chuàng)建10個對象調用autorelease
方法逐一添加到自動釋放池中滚澜,autorelease
函數(shù):
public:
static inline id autorelease(id obj)
{
assert(obj);
assert(!obj->isTaggedPointer());
// 通過調用autoreleaseFast函數(shù),將obj對象添加至池中
id *dest __unused = autoreleaseFast(obj);
assert(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj);
return obj;
}
autoreleaseFast
函數(shù):
static inline id *autoreleaseFast(id obj)
{
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {
// 將obj對象添加到page中
return page->add(obj);
} else if (page) {
return autoreleaseFullPage(obj, page);
} else {
return autoreleaseNoPage(obj);
}
}
在autoreleaseFast
函數(shù)中嫁怀,我們看到设捐,不管是當前page沒有滿借浊,還是當前page滿了或者是還沒有page,最終都調用了page->add(obj)
語句將對象添加至池中
當需要銷毀這10個對象時萝招,這時需要調用pop
函數(shù)蚂斤,源碼如下:
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;
}
page = pageForPointer(token);
// 將token(POOL_BOUNDARY)賦值給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
} else {
// Error. For bincompat purposes this is not
// fatal in executables built with old SDKs.
return badPop(token);
}
}
if (PrintPoolHiwat) printHiwat();
// 這里我們可以看到,自動釋放池釋放對象即寒,是通過一個終止釋放的標記來從后往前釋放池中的對象
page->releaseUntil(stop);
// 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();
}
}
}
從上面的源碼我們看到橡淆,調用pop
函數(shù)有傳一個參數(shù)token
,這個token
是一個指針母赵,正是開始創(chuàng)建自動釋放池時函數(shù)的返回值逸爵,這個值就是POOL_BOUNDARY
,我們從編譯出的c++文件源碼可知
struct __AtAutoreleasePool {
// 構造函數(shù)凹嘲,在初始化創(chuàng)建結構體的時候調用
__AtAutoreleasePool() {
// 創(chuàng)建自動釋放池時調用objc_autoreleasePoolPush函數(shù)师倔,并返回atautoreleasepoolobj,這個就是入棧的`POOL_BOUNDARY`的地址
atautoreleasepoolobj = objc_autoreleasePoolPush();
}
// 析構函數(shù)周蹭,在結構體對象銷毀的時候調用
~__AtAutoreleasePool() {
// 銷毀自動釋放池時調用`objc_autoreleasePoolPop`函數(shù)趋艘,將`POOL_BOUNDARY`地址傳入
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
// atautoreleasepoolobj為指針類型
void * atautoreleasepoolobj;
};
我們在來看pop
函數(shù),當把token
地址賦值給stop
指針后凶朗,執(zhí)行了page->releaseUntil(stop)
語句瓷胧,我們來看看releaseUntil
函數(shù),源碼如下:
// 自動釋放池釋放對象棚愤,從后往前開始釋放搓萧,直到遇到stop地址停止釋放
void releaseUntil(id *stop)
{
// Not recursive: we don't want to blow out the stack
// if a thread accumulates a stupendous amount of garbage
// 開始循環(huán)釋放對象
while (this->next != stop) {
// Restart from hotPage() every time, in case -release
// autoreleased more objects
// 獲取到當前正在使用的Page,也就是熱Page
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();
// 取出`next`指針指向的對象地址
id obj = *--page->next;
memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
page->protect();
// 一直循環(huán)遍歷釋放對象宛畦,知道遇到POOL_BOUNDARY地址
if (obj != POOL_BOUNDARY) {
// 這里就是釋放對象的最終位置
objc_release(obj);
}
}
setHotPage(this);
#if DEBUG
// we expect any children to be completely empty
for (AutoreleasePoolPage *page = child; page; page = page->child) {
assert(page->empty());
}
#endif
}
在releaseUntil()
函數(shù)中瘸洛,我們看到了核心代碼:
// 開始循環(huán)釋放對象
while (this->next != stop) {
// 一直循環(huán)遍歷釋放對象,知道遇到POOL_BOUNDARY地址
if (obj != POOL_BOUNDARY) {
// 這里就是釋放對象的最終位置
objc_release(obj);
}
}
在while循環(huán)中判斷次和,如果地址不等于POOL_BOUNDARY反肋,就執(zhí)行objc_release()
釋放對象,直到地址為POOL_BOUNDARY停止釋放踏施,到這我們應該就明白POOL_BOUNDARY的作用了
上面Autorelease pool
源碼注釋我們知道石蔗,自動釋放池是由很多個AutoreleasePoolPage
組成的雙向鏈表結構,那每一個AutoreleasePoolPage
又分配了多大的存儲空間尼
我們通過前面的源碼static size_t const COUNT = SIZE / sizeof(id);
中的SIZE
的定義
#define I386_PGBYTES 4096 /* bytes per 80386 page */
從宏定義中可以看出每頁page
有4096字節(jié)的大小畅形,由于AutoreleasePoolPage
類中聲明了7個成員變量抓督,每個成員變量占8字節(jié),所以說每頁page
還有4040(4096-56)個字節(jié)大小用來存放對象束亏,如果這一頁數(shù)據(jù)存滿了,那么就接著存放在下一頁page
中阵具,以此類推
在上面的源碼分析過程我們也有看到hotPage
和coldPage
函數(shù)碍遍,這兩個很好理解定铜,當前正在使用的page
就是hotPage
,前面已經(jīng)存放滿對象的page
就是coldPage
怕敬,這個后面代碼打印當前自動釋放池的情況揣炕,看輸出結果就一目了然了
上面是通過Autorelease Pool
底層源碼進行的分析,下面我們再來通過測試代碼來看看自動釋放池的使用情況东跪,這里由于ARC
和MRC
使用Autorelease Pool
的情況有點不同畸陡,這里我們就分開來看測試效果
我們先來看看MRC的測試情況
我們先新建一個工程,然后將Automatic Reference Counting
改為NO虽填,然后創(chuàng)建一個Person
類丁恭,一個Dog
類,測試代碼如下:
Person
類
@interface Person : NSObject
{
Dog *_dog;
}
- (void)setDog:(Dog *)dog;
- (Dog *)dog;
@end
@implementation Person
- (void)setDog:(Dog *)dog {
if (dog != _dog) {
[_dog release];
dog = [dog retain];
_dog = dog;
}
}
- (Dog *)dog {
return _dog;
}
- (void)dealloc {
NSLog(@"%s", __func__);
self.dog = nil;
[super dealloc];
}
@end
Dog
類
@interface Dog : NSObject
@end
@implementation Dog
- (void)dealloc {
[super dealloc];
NSLog(@"%s", __func__);
}
@end
main
函數(shù)
// _objc_autoreleasePoolPrint()函數(shù)可以打印出當前自動釋放池的使用情況斋日,在ARC和MRC下都可用
extern void _objc_autoreleasePoolPrint(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
// 注意:當前是MRC環(huán)境
NSLog(@"%@", [NSThread currentThread]);
_objc_autoreleasePoolPrint();
}
return 0;
}
在main
函數(shù)中牲览,我們先來看看最簡單的情況,就是沒有任何對象添加到自動釋放池時恶守,我們使用_objc_autoreleasePoolPrint()
函數(shù)來打印池中的情況第献,自動釋放池打印如下:
objc[72048]: ##############
objc[72048]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[72048]: 1 releases pending.
objc[72048]: [0x103802000] ................ PAGE (hot) (cold)
objc[72048]: [0x103802038] ################ POOL 0x103802038
objc[72048]: ##############
從上面的打印我們可以看出,1 releases pending
兔港,表示當前池中就一個對象庸毫,這個對象就是POOL 0x103802038
,也就是上面所講的POOL_BOUNDARY
指針
接下來我們修改下main
函數(shù)衫樊,我們創(chuàng)建對象調用autorelease
添加到池中飒赃,然后創(chuàng)建對象不調用autorelease
,對比看下差別橡伞,測試代碼如下:
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
// 注意:當前是MRC環(huán)境
NSLog(@"%@", [NSThread currentThread]);
_objc_autoreleasePoolPrint();
NSLog(@"--------------------------------");
Person *p1 = [[Person alloc] init];
[p1 autorelease];
_objc_autoreleasePoolPrint();
NSLog(@"--------------------------------");
Dog *d1 = [[Dog alloc] init];
_objc_autoreleasePoolPrint();
}
return 0;
}
自動釋放池打印如下:
objc[72119]: ##############
objc[72119]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[72119]: 1 releases pending.
objc[72119]: [0x100801000] ................ PAGE (hot) (cold)
objc[72119]: [0x100801038] ################ POOL 0x100801038
objc[72119]: ##############
--------------------------------
objc[72119]: ##############
objc[72119]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[72119]: 2 releases pending.
objc[72119]: [0x100801000] ................ PAGE (hot) (cold)
objc[72119]: [0x100801038] ################ POOL 0x100801038
objc[72119]: [0x100801040] 0x10060f850 Person
objc[72119]: ##############
--------------------------------
objc[72119]: ##############
objc[72119]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[72119]: 2 releases pending.
objc[72119]: [0x100801000] ................ PAGE (hot) (cold)
objc[72119]: [0x100801038] ################ POOL 0x100801038
objc[72119]: [0x100801040] 0x10060f850 Person
objc[72119]: ##############
從上面的打印盒揉,我們可以看到,當我們不添加任何對象到池中時兑徘,池中就一個對象POOL_BOUNDARY
刚盈,當我們創(chuàng)建一個Person
對象添加到池中后,可以看到池中有了2個對象挂脑,除了最開始的POOL_BOUNDARY
藕漱,還新增了0x10060f850 Person
,然而當我們創(chuàng)建一個Dog
對象崭闲,但是沒有調用autorelease
肋联,我們發(fā)現(xiàn)Dog
對象的地址沒有添加到池中。從這個對比我們知道了刁俭,要想讓對象添加到自動釋放池橄仍,對象就需要調用autorelease
方法
接下來我們來看下自動釋放池嵌套使用的情況,測試代碼如下:
extern void _objc_autoreleasePoolPrint(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
// 注意:當前是MRC環(huán)境
NSLog(@"%@", [NSThread currentThread]);
_objc_autoreleasePoolPrint();
NSLog(@"--------------------------------");
Person *p1 = [[Person alloc] init];
[p1 autorelease];
_objc_autoreleasePoolPrint();
NSLog(@"--------------------------------");
@autoreleasepool {
Person *p2 = [[Person alloc] init];
[p2 autorelease];
Dog *d2 = [[Dog alloc] init];
[d2 autorelease];
_objc_autoreleasePoolPrint();
NSLog(@"--------------------------------");
@autoreleasepool {
Person *p3 = [[Person alloc] init];
[p3 autorelease];
Cat *cat3 = [[Cat alloc] init];
[cat3 autorelease];
NSLog(@"--------------------------------");
_objc_autoreleasePoolPrint();
}
}
}
return 0;
}
_objc_autoreleasePoolPrint()
函數(shù)打印如下:
objc[72275]: ##############
objc[72275]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[72275]: 1 releases pending.
objc[72275]: [0x105800000] ................ PAGE (hot) (cold)
objc[72275]: [0x105800038] ################ POOL 0x105800038
objc[72275]: ##############
--------------------------------
objc[72275]: ##############
objc[72275]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[72275]: 2 releases pending.
objc[72275]: [0x105800000] ................ PAGE (hot) (cold)
objc[72275]: [0x105800038] ################ POOL 0x105800038
objc[72275]: [0x105800040] 0x1023057d0 Person
objc[72275]: ##############
--------------------------------
objc[72275]: ##############
objc[72275]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[72275]: 5 releases pending.
objc[72275]: [0x105800000] ................ PAGE (hot) (cold)
objc[72275]: [0x105800038] ################ POOL 0x105800038
objc[72275]: [0x105800040] 0x1023057d0 Person
objc[72275]: [0x105800048] ################ POOL 0x105800048
objc[72275]: [0x105800050] 0x10073af00 Person
objc[72275]: [0x105800058] 0x10073d3b0 Dog
objc[72275]: ##############
--------------------------------
objc[72275]: ##############
objc[72275]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[72275]: 8 releases pending.
objc[72275]: [0x105800000] ................ PAGE (hot) (cold)
objc[72275]: [0x105800038] ################ POOL 0x105800038
objc[72275]: [0x105800040] 0x1023057d0 Person
objc[72275]: [0x105800048] ################ POOL 0x105800048
objc[72275]: [0x105800050] 0x10073af00 Person
objc[72275]: [0x105800058] 0x10073d3b0 Dog
objc[72275]: [0x105800060] ################ POOL 0x105800060
objc[72275]: [0x105800068] 0x10244f2f0 Person
objc[72275]: [0x105800070] 0x10244ee70 Cat
objc[72275]: ##############
--------------------------------
2020-02-19 10:48:27.081520+0800 aaaaaaa[72275:5367840] -[Cat dealloc]
2020-02-19 10:48:27.081564+0800 aaaaaaa[72275:5367840] -[Person dealloc]
2020-02-19 10:48:27.081606+0800 aaaaaaa[72275:5367840] -[Dog dealloc]
2020-02-19 10:48:27.081641+0800 aaaaaaa[72275:5367840] -[Person dealloc]
2020-02-19 10:48:27.081675+0800 aaaaaaa[72275:5367840] -[Person dealloc]
上面我們進行了3層自動釋放池的嵌套操作,我們從最后一次執(zhí)行_objc_autoreleasePoolPrint()
打印可以看出侮繁,每當我們使用@autoreleasepool {}
創(chuàng)建一個自動釋放池虑粥,就會有一個POOL_BOUNDARY
入棧,我們從下面打印可以看出
objc[72275]: ##############
objc[72275]: AUTORELEASE POOLS for thread 0x1000aa5c0
objc[72275]: 8 releases pending.
objc[72275]: [0x105800000] ................ PAGE (hot) (cold)
objc[72275]: [0x105800038] ################ POOL 0x105800038
objc[72275]: [0x105800040] 0x1023057d0 Person
objc[72275]: [0x105800048] ################ POOL 0x105800048
objc[72275]: [0x105800050] 0x10073af00 Person
objc[72275]: [0x105800058] 0x10073d3b0 Dog
objc[72275]: [0x105800060] ################ POOL 0x105800060
objc[72275]: [0x105800068] 0x10244f2f0 Person
objc[72275]: [0x105800070] 0x10244ee70 Cat
objc[72275]: ##############
接下來我們再來看看當嵌套的自動釋放池超出了作用域會有什么結果宪哩,測試代碼如下:
extern void _objc_autoreleasePoolPrint(void);
int main(int argc, const char * argv[]) {
@autoreleasepool { // 第一個自動釋放池
// insert code here...
// 注意:當前是MRC環(huán)境
NSLog(@"%@", [NSThread currentThread]);
Person *p1 = [[Person alloc] init];
[p1 autorelease];
@autoreleasepool { // 第二個自動釋放池
Person *p2 = [[Person alloc] init];
[p2 autorelease];
Dog *d2 = [[Dog alloc] init];
[d2 autorelease];
@autoreleasepool { // 第三個自動釋放池
Person *p3 = [[Person alloc] init];
[p3 autorelease];
Cat *cat3 = [[Cat alloc] init];
[cat3 autorelease];
_objc_autoreleasePoolPrint();
NSLog(@"--------------------------------");
}
NSLog(@"第三個自動釋放池超出了作用域");
_objc_autoreleasePoolPrint();
NSLog(@"--------------------------------");
}
NSLog(@"第二個自動釋放池超出了作用域");
_objc_autoreleasePoolPrint();
NSLog(@"--------------------------------");
}
NSLog(@"第一個自動釋放池超出了作用域");
_objc_autoreleasePoolPrint();
NSLog(@"--------------------------------");
return 0;
}
終端打印結果如下:
objc[72410]: ##############
objc[72410]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[72410]: 8 releases pending.
objc[72410]: [0x105803000] ................ PAGE (hot) (cold)
objc[72410]: [0x105803038] ################ POOL 0x105803038
objc[72410]: [0x105803040] 0x1007045c0 Person
objc[72410]: [0x105803048] ################ POOL 0x105803048
objc[72410]: [0x105803050] 0x100704170 Person
objc[72410]: [0x105803058] 0x100700be0 Dog
objc[72410]: [0x105803060] ################ POOL 0x105803060
objc[72410]: [0x105803068] 0x100702760 Person
objc[72410]: [0x105803070] 0x100700dc0 Cat
objc[72410]: ##############
--------------------------------
aaaaaaa[72410:5388081] -[Cat dealloc]
aaaaaaa[72410:5388081] -[Person dealloc]
aaaaaaa[72410:5388081] 第三個自動釋放池超出了作用域
objc[72410]: ##############
objc[72410]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[72410]: 5 releases pending.
objc[72410]: [0x105803000] ................ PAGE (hot) (cold)
objc[72410]: [0x105803038] ################ POOL 0x105803038
objc[72410]: [0x105803040] 0x1007045c0 Person
objc[72410]: [0x105803048] ################ POOL 0x105803048
objc[72410]: [0x105803050] 0x100704170 Person
objc[72410]: [0x105803058] 0x100700be0 Dog
objc[72410]: ##############
--------------------------------
aaaaaaa[72410:5388081] -[Dog dealloc]
aaaaaaa[72410:5388081] -[Person dealloc]
aaaaaaa[72410:5388081] 第二個自動釋放池超出了作用域
objc[72410]: ##############
objc[72410]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[72410]: 2 releases pending.
objc[72410]: [0x105803000] ................ PAGE (hot) (cold)
objc[72410]: [0x105803038] ################ POOL 0x105803038
objc[72410]: [0x105803040] 0x1007045c0 Person
objc[72410]: ##############
--------------------------------
aaaaaaa[72410:5388081] -[Person dealloc]
aaaaaaa[72410:5388081] 第一個自動釋放池超出了作用域
objc[72410]: ##############
objc[72410]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[72410]: 0 releases pending.
objc[72410]: [0x105803000] ................ PAGE (hot) (cold)
objc[72410]: ##############
我們從上面的打印可以看到娩贷,當?shù)谌齻€自動釋放池超出了作用域(也就是執(zhí)行完@autoreleasepool {}
的結束大括號)后,在第三個自動釋放池中創(chuàng)建的Person
和Cat
對象就已經(jīng)銷毀了锁孟,我們從_objc_autoreleasePoolPrint()
打印也可以看出彬祖,此時池中已經(jīng)沒有了第三個自動釋放池的POOL_BOUNDARY
地址,和Person
品抽,Cat
的地址了储笑,也就是說第三個自動釋放池也銷毀了。當?shù)诙€自動釋放池超出作用域桑包,原理和第三個自動釋放池一樣南蓬。我們在看下main
函數(shù)中系統(tǒng)創(chuàng)建的第一個自動釋放池超出作用域后的情況,從打印0 releases pending.
可以看出哑了,此時池中已經(jīng)沒有任何對象了赘方,第一個自動釋放池超出作用域也銷毀了。這也正好驗證了上面咱們總結的結論:
當自動釋放池超出作用域弱左,則這個自動釋放池就會銷毀窄陡,當自動釋放池銷毀時,便會向池中的每一個對象發(fā)送
release
消息來銷毀池中的對象拆火。
接下來我們再來看看當添加到自動釋放池中的對象引用計數(shù)大于1時跳夭,當自動釋放池銷毀時,引用計數(shù)大于1的對象是進行引用計數(shù)-1還是說直接釋放對象们镜,測試代碼如下:
extern void _objc_autoreleasePoolPrint(void);
int main(int argc, const char * argv[]) {
@autoreleasepool { // 第一個自動釋放池
// insert code here...
// 注意:當前是MRC環(huán)境
NSLog(@"%@", [NSThread currentThread]);
@autoreleasepool { // 第二個自動釋放池
Person *p1 = [[[Person alloc] init] autorelease];
Person *p2 = [[[Person alloc] init] autorelease];
Dog *dog = [[[Dog alloc] init] autorelease]; // dog引用計數(shù)=1
NSLog(@"%zd", [dog retainCount]); // 1
[p1 setDog:dog]; // dog引用計數(shù)=2
[p2 setDog:dog]; // dog引用計數(shù)=3
NSLog(@"%zd", [dog retainCount]); // 3
_objc_autoreleasePoolPrint();
NSLog(@"-------------------");
}
NSLog(@"---------第二個自動釋放池超出作用域---------");
_objc_autoreleasePoolPrint();
}
return 0;
}
終端打印數(shù)據(jù)如下:
13:44:18.661853+0800 aaaaaaa[72892:5442548] 1
13:44:18.661890+0800 aaaaaaa[72892:5442548] 3
objc[72892]: ##############
objc[72892]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[72892]: 5 releases pending.
objc[72892]: [0x100803000] ................ PAGE (hot) (cold)
objc[72892]: [0x100803038] ################ POOL 0x100803038
objc[72892]: [0x100803040] ################ POOL 0x100803040
objc[72892]: [0x100803048] 0x100622450 Person
objc[72892]: [0x100803050] 0x100620f20 Person
objc[72892]: [0x100803058] 0x100624990 Dog
objc[72892]: ##############
13:44:18.662157+0800 aaaaaaa[72892:5442548] -------------------
13:44:18.662209+0800 aaaaaaa[72892:5442548] -[Person dealloc]
13:44:18.662241+0800 aaaaaaa[72892:5442548] -[Person dealloc]
13:44:18.662280+0800 aaaaaaa[72892:5442548] -[Dog dealloc]
13:44:18.662342+0800 aaaaaaa[72892:5442548] -第二個自動釋放池超出作用域-
objc[72892]: ##############
objc[72892]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[72892]: 1 releases pending.
objc[72892]: [0x100803000] ................ PAGE (hot) (cold)
objc[72892]: [0x100803038] ################ POOL 0x100803038
objc[72892]: ##############
上面的測試代碼币叹,我們創(chuàng)建了一個Dog
對象,然后分別讓p1
和p2
對象持有這個dog
對象模狭,這時dog
對象的引用計數(shù)為3颈抚,當?shù)诙€自動釋放池超出作用域銷毀的時,我們通過打印可以看出dog
對象也銷毀了嚼鹉,此時再打印自動釋放池的情況贩汉,發(fā)現(xiàn)池中就剩下第一個自動釋放池的POOL_BOUNDARY
地址了,第二個自動釋放池中的所有對象全部都已釋放了
上面咱們代碼測試自動釋放池的情況一直都是使用@autoreleasepool {}
方式創(chuàng)建的自動釋放池锚赤,并沒有使用NSAutoreleasePool
類來創(chuàng)建自動釋放池匹舞,原理都是一樣的,這里就不在重復驗證了线脚。
接下來我們再來看看ARC
環(huán)境下的自動釋放池的使用情況:
由于ARC
環(huán)境中我們不能使用autorelease
和NSAutoreleasePool
赐稽,所以在ARC
環(huán)境中叫榕,如果想創(chuàng)建一個自動釋放池我們只能選擇使用@autoreleasepool {}
這種方式創(chuàng)建,如果想將對象添加到自動釋放池中又憨,我們只能選擇使用__autoreleasing
權限修飾符來修飾這個對象翠霍,測試代碼如下:
extern void _objc_autoreleasePoolPrint(void);
int main(int argc, const char * argv[]) {
@autoreleasepool { // 第一個自動釋放池
// insert code here...
// 注意:當前是ARC環(huán)境
NSLog(@"%@", [NSThread currentThread]);
_objc_autoreleasePoolPrint();
NSLog(@"---------------------------");
// 沒有使用__autoreleasing修飾的對象,不會添加到自動釋放池中
Person *p1 = [[Person alloc] init];
_objc_autoreleasePoolPrint();
// 使用了__autoreleasing的會添加到自動釋放池中
__autoreleasing Person *p2 = [[Person alloc] init];
_objc_autoreleasePoolPrint();
}
NSLog(@"-第一個自動釋放池銷毀了-");
// 第一個自動釋放池超出了作用域蠢莺,自動釋放池銷毀,池中的對象也全部釋放
_objc_autoreleasePoolPrint();
return 0;
}
對應的終端打印如下:
objc[73081]: ##############
objc[73081]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[73081]: 1 releases pending.
objc[73081]: [0x105002000] ................ PAGE (hot) (cold)
objc[73081]: [0x105002038] ################ POOL 0x105002038
objc[73081]: ##############
---------------------------
objc[73081]: ##############
objc[73081]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[73081]: 1 releases pending.
objc[73081]: [0x105002000] ................ PAGE (hot) (cold)
objc[73081]: [0x105002038] ################ POOL 0x105002038
objc[73081]: ##############
---------------------------
objc[73081]: ##############
objc[73081]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[73081]: 2 releases pending.
objc[73081]: [0x105002000] ................ PAGE (hot) (cold)
objc[73081]: [0x105002038] ################ POOL 0x105002038
objc[73081]: [0x105002040] 0x103a225e0 Person
objc[73081]: ##############
---------------------------
14:18:48.329583+0800 aaaaaaa[73081:5459344] -[Person dealloc]
14:18:48.329638+0800 aaaaaaa[73081:5459344] -[Person dealloc]
14:18:48.349366+0800 aaaaaaa[73081:5459344] -第一個自動釋放池銷毀-
---------------------------
objc[73081]: ##############
objc[73081]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[73081]: 0 releases pending.
objc[73081]: [0x105002000] ................ PAGE (hot) (cold)
objc[73081]: ##############
從上面的打印我們可以看到零如,在ARC
環(huán)境中躏将,如果對象沒有被__autoreleasing
修飾是不會被添加到自動釋放池中的,這個和MRC
中autorelease
等價考蕾,當自動釋放池超出作用域祸憋,自動釋放池會銷毀,池中的所有對象也會釋放肖卧,這個和MRC
是完全一樣的
接下來我們再來看看ARC
環(huán)境下的自動釋放池循環(huán)嵌套情況蚯窥,測試代碼如下:
extern void _objc_autoreleasePoolPrint(void);
int main(int argc, const char * argv[]) {
@autoreleasepool { // 第一個自動釋放池
// insert code here...
// 注意:當前是ARC環(huán)境
NSLog(@"%@", [NSThread currentThread]);
Person *p1 = [[Person alloc] init];
__autoreleasing Person *p2 = [[Person alloc] init];
@autoreleasepool { // 第二個自動釋放池
__autoreleasing Dog *dog1 = [[Dog alloc] init];
__autoreleasing Cat *cat1 = [[Cat alloc] init];
_objc_autoreleasePoolPrint();
NSLog(@"---------------------");
}
NSLog(@"-第二個自動釋放池銷毀了-");
_objc_autoreleasePoolPrint();
NSLog(@"---------------------");
}
NSLog(@"-第一個自動釋放池銷毀了-");
_objc_autoreleasePoolPrint();
return 0;
}
對應的終端打印如下:
objc[73141]: ##############
objc[73141]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[73141]: 5 releases pending.
objc[73141]: [0x101804000] ................ PAGE (hot) (cold)
objc[73141]: [0x101804038] ################ POOL 0x101804038
objc[73141]: [0x101804040] 0x10071cd70 Person
objc[73141]: [0x101804048] ################ POOL 0x101804048
objc[73141]: [0x101804050] 0x10071c030 Dog
objc[73141]: [0x101804058] 0x10071d150 Cat
objc[73141]: ##############
---------------------
14:34:05.417828+0800 aaaaaaa[73141:5467454] -[Cat dealloc]
14:34:05.417890+0800 aaaaaaa[73141:5467454] -[Dog dealloc]
14:34:05.417976+0800 aaaaaaa[73141:5467454] -第二個自動釋放池銷毀了-
---------------------
objc[73141]: ##############
objc[73141]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[73141]: 2 releases pending.
objc[73141]: [0x101804000] ................ PAGE (hot) (cold)
objc[73141]: [0x101804038] ################ POOL 0x101804038
objc[73141]: [0x101804040] 0x10071cd70 Person
objc[73141]: ##############
---------------------
14:34:05.421754+0800 aaaaaaa[73141:5467454] -[Person dealloc]
14:34:05.421802+0800 aaaaaaa[73141:5467454] -[Person dealloc]
14:34:05.421872+0800 aaaaaaa[73141:5467454] -第一個自動釋放池銷毀了-
objc[73141]: ##############
objc[73141]: AUTORELEASE POOLS for thread 0x1000ab5c0
objc[73141]: 0 releases pending.
objc[73141]: [0x101804000] ................ PAGE (hot) (cold)
objc[73141]: ##############
我們從上面的打印可以看到,在ARC
環(huán)境中自動釋放池嵌套使用塞帐,當自動釋放池超出作用域時便會銷毀拦赠,池中的所有對象也都會被釋放,這和MRC
是一樣的原理葵姥。
接下來我們再來探究下Autorelease Pool
和runloop
之間的關系荷鼠,我們創(chuàng)建一個新的iOS工程,打印當前的runloop
榔幸,測試代碼如下:
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"%@", [NSRunLoop currentRunLoop]);
}
@end
這里打印的信息太多允乐,我們主要關心runloop
的Observer
的信息,核心打印如下:
observers = (
"<CFRunLoopObserver 0x6000003143c0 [0x7fff805eff70]>{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff47571f14), context = <CFArray 0x600003c4f360 [0x7fff805eff70]>{type = mutable-small, count = 1, values = (\n\t0 : <0x7fcb45001038>\n)}}",
"<CFRunLoopObserver 0x6000003100a0 [0x7fff805eff70]>{valid = Yes, activities = 0x20, repeats = Yes, order = 0, callout = _UIGestureRecognizerUpdateObserver (0x7fff4712091e), context = <CFRunLoopObserver context 0x600001900fc0>}",
"<CFRunLoopObserver 0x600000314280 [0x7fff805eff70]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 1999000, callout = _beforeCACommitHandler (0x7fff475a22b6), context = <CFRunLoopObserver context 0x7fcb40c00d90>}",
"<CFRunLoopObserver 0x60000031c320 [0x7fff805eff70]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2000000, callout = _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv (0x7fff2affcc4a), context = <CFRunLoopObserver context 0x0>}",
"<CFRunLoopObserver 0x600000314320 [0x7fff805eff70]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2001000, callout = _afterCACommitHandler (0x7fff475a231f), context = <CFRunLoopObserver context 0x7fcb40c00d90>}",
"<CFRunLoopObserver 0x600000314460 [0x7fff805eff70]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff47571f14), context = <CFArray 0x600003c4f360 [0x7fff805eff70]>{type = mutable-small, count = 1, values = (\n\t0 : <0x7fcb45001038>\n)}}"
上面的observers
中削咆,我們可以看到牍疏,程序一啟動系統(tǒng)在主線程中就注冊了如下兩個Observer
// 第一個Observer
"<CFRunLoopObserver 0x6000003143c0 [0x7fff805eff70]>{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff47571f14), context = <CFArray 0x600003c4f360 [0x7fff805eff70]>{type = mutable-small, count = 1, values = (\n\t0 : <0x7fcb45001038>\n)}}",
// 第二個Observer
"<CFRunLoopObserver 0x600000314460 [0x7fff805eff70]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler (0x7fff47571f14), context = <CFArray 0x600003c4f360 [0x7fff805eff70]>{type = mutable-small, count = 1, values = (\n\t0 : <0x7fcb45001038>\n)}}"
我們發(fā)現(xiàn)第一個Observer是用來監(jiān)聽activities = 0x1
的事件,第二個Observer是用來監(jiān)聽activities = 0xa0
的事件
我們再來了解下runloop
對于監(jiān)聽事件狀態(tài)的枚舉:
// Run Loop Observer Activities
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 1:代表是進入runloop的狀態(tài)
kCFRunLoopBeforeTimers = (1UL << 1), // 2:即將處理timer的狀態(tài)
kCFRunLoopBeforeSources = (1UL << 2), // 4:即將處理source的狀態(tài)
kCFRunLoopBeforeWaiting = (1UL << 5), // 32:即將進入休眠的狀態(tài)
kCFRunLoopAfterWaiting = (1UL << 6), // 64:已休眠拨齐,等待喚醒的狀態(tài)
kCFRunLoopExit = (1UL << 7), // 128:退出runloop的狀態(tài)
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
從枚舉中對應的狀態(tài)值我們便知道了鳞陨,第一個Observer
是用來監(jiān)聽runloop
進入事件,也就是kCFRunLoopEntry
奏黑,第二個Observer對應的activities
值為0xa0=160跛溉,160正好是狀態(tài)值32加上狀態(tài)值128嗓奢,也就是說第二個Observer是用來監(jiān)聽runloop
即將進入休眠的狀態(tài)kCFRunLoopBeforeWaiting
和runloop
退出的狀態(tài)kCFRunLoopExit
我們通過上面的打印可以看到,這兩個Observer
當監(jiān)聽到事件觸發(fā),都會回調執(zhí)行callout = _wrapRunLoopWithAutoreleasePoolHandler
這個handler提针,從回調函數(shù)的名字_wrapRunLoopWithAutoreleasePoolHandler
我們可以知道這個回調是用來處理Autorelease Pool
相關操作的,那么這兩個Observer
監(jiān)聽到回調后會觸發(fā)自動釋放池的什么操作尼岛都?
第一個Observer
當監(jiān)聽到kCFRunLoopEntry
時蚤认,這時會調用自動釋放池的objc_autoreleasePoolPush
函數(shù),也就是創(chuàng)建一個自動釋放池,并且將POOL_BOUNDARY
添加到自動釋放池中
第二個Observer
當監(jiān)聽到kCFRunLoopBeforeWaiting
時忍啸,這時會調用自動釋放池的objc_autoreleasePoolPop
函數(shù)仰坦,來銷毀自動釋放池,然后再調用objc_autoreleasePoolPush
函數(shù)創(chuàng)建一個自動釋放池计雌。當監(jiān)聽到kCFRunLoopExit
時悄晃,這時會調用自動釋放池的objc_autoreleasePoolPop
函數(shù),銷毀自動釋放池
在runloop
即將進入循環(huán)之前凿滤,會創(chuàng)建自動釋放池眷蚓,runloop
退出循環(huán)會銷毀自動釋放池沙热。系統(tǒng)之所以這樣設計,其實也很好理解。因為在runloop
即將進入循環(huán)前雁芙,系統(tǒng)創(chuàng)建了大量的事件和對象,創(chuàng)建的這些事件和對象最好能夠由自動釋放池來管理以保證得到有效的釋放洞焙,然而當runloop
退出循環(huán),這時肯定也需要釋放掉之前創(chuàng)建的對象唁情,所以必然會銷毀自動釋放池
在runloop
即將進入休眠狀態(tài)時惦费,這時整個應用程序都進入了休眠狀態(tài),等待其它事件來喚醒runloop
瞧省,所以在這時也會調用objc_autoreleasePoolPop
函數(shù)來銷毀自動釋放池,以保證在休眠狀態(tài)下釋放掉無用的對象藕筋。為了保證在下一個runloop
循環(huán)過程中創(chuàng)建的事件和對象都能夠及時的釋放伍掀,所以在銷毀完自動釋放池后系統(tǒng)又創(chuàng)建了一個新的自動釋放池。
我們從Autorelease Pool
的官方文檔說明中也可以看出上面runloop
注冊兩個Observer
的作用:
The Application Kit creates an autorelease pool on the main thread at the
beginning of every cycle of the event loop, and drains it at the end, thereby
releasing any autoreleased objects generated while processing an event. If
you use the Application Kit, you therefore typically don’t have to create
your own pools. If your application creates a lot of temporary autoreleased
objects within the event loop, however, it may be beneficial to create
“l(fā)ocal” autorelease pools to help to minimize the peak memory footprint.
runloop
和Autorelease Pool
的關系如圖:
講解示例Demo地址:
https://github.com/guangqiang-liu/11-AutoreleasePool
https://github.com/guangqiang-liu/11.1-AutoreleasePool
https://github.com/guangqiang-liu/11.2-AutoreleasePool
https://github.com/guangqiang-liu/11.3-AutoreleasePool
更多文章
- ReactNative開源項目OneM(1200+star):https://github.com/guangqiang-liu/OneM:歡迎小伙伴們 star
- iOS組件化開發(fā)實戰(zhàn)項目(500+star):https://github.com/guangqiang-liu/iOS-Component-Pro:歡迎小伙伴們 star
- 簡書主頁:包含多篇iOS和RN開發(fā)相關的技術文章http://www.reibang.com/u/023338566ca5 歡迎小伙伴們:多多關注县好,點贊
- ReactNative QQ技術交流群(2000人):620792950 歡迎小伙伴進群交流學習
- iOS QQ技術交流群:678441305 歡迎小伙伴進群交流學習