什么是自動(dòng)釋放池
OC中的一種內(nèi)存自動(dòng)回收機(jī)制钩蚊,它可以延遲加入AutoreleasePool中的變量release的時(shí)機(jī)犁罩,即當(dāng)我們創(chuàng)建了一個(gè)對(duì)象,并把他加入到了自動(dòng)釋放池中時(shí)两疚,他不會(huì)立即被釋放,會(huì)等到一次runloop結(jié)束或者作用域超出{}或者超出[pool release]之后再被釋放
自動(dòng)釋放池的創(chuàng)建與銷(xiāo)毀時(shí)機(jī)
MRC:
NSAutoreleasePool *pool = [[ NSAutoreleasePool alloc]init ];//創(chuàng)建一個(gè)自動(dòng)釋放池
Person *person = [[Person alloc]init];
//調(diào)autorelease方法將對(duì)象加入到自動(dòng)釋放池
[person autorelease];
//手動(dòng)釋放自動(dòng)釋放池執(zhí)行完這行代碼是含滴,自動(dòng)釋放池會(huì)對(duì)加入他中的對(duì)象做一次release操作
[pool release];
···
自動(dòng)釋放池銷(xiāo)毀時(shí)機(jī):[pool release]代碼執(zhí)行完后.
ARC
@autoreleasepool {
//在這個(gè){}之內(nèi)的變量默認(rèn)被添加到自動(dòng)釋放池
Person *p = [[Person alloc] init];
}//除了這個(gè)括號(hào)诱渤,p被釋放
Autorelease實(shí)現(xiàn)原理
下面看一段簡(jiǎn)單的代碼
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
}
return 0;
}
然后在終端中使用clang -rewrite-objc main.m 命令將上述OC代碼重寫(xiě)成C++的實(shí)現(xiàn)
搜索main我們可以看到main函數(shù)的實(shí)現(xiàn)重寫(xiě)成了如下代碼:
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_49_sdbnp0nd07q4m_sh4_gw52r40000gn_T_main_9e48ee_mi_0);
}
return 0;
}
通過(guò)對(duì)比可以發(fā)現(xiàn),蘋(píng)果通過(guò)聲明一個(gè)__AtAutoreleasePool類(lèi)型的局部變量
@autoreleasepool被轉(zhuǎn)轉(zhuǎn)換成__AtAutoreleasePool 結(jié)構(gòu)體類(lèi)型
struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
可以看到 __AtAutoreleasePool() 構(gòu)造函數(shù)調(diào)用objc_autoreleasePoolPush(),~__AtAutoreleasePool() 析構(gòu)函數(shù)調(diào)用 objc_autoreleasePoolPop()
objc_autoreleasePoolPush 和 objc_autoreleasePoolPop 是什么呢谈况?
在 NSObject.mm 文件中:
void *objc_autoreleasePoolPush(void) {
return AutoreleasePoolPage::push();
}
void objc_autoreleasePoolPop(void *ctxt) {
AutoreleasePoolPage::pop(ctxt);
}
實(shí)際上是調(diào)用AutoreleasePoolPage的push和pop兩個(gè)類(lèi)方法
首先來(lái)看一下AutoreleasePoolPage這個(gè)類(lèi)
class AutoreleasePoolPage {
magic_t const magic;
id *next;
pthread_t const thread;
AutoreleasePoolPage * const parent;
AutoreleasePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
};
magic:用來(lái)校驗(yàn) AutoreleasePoolPage 的結(jié)構(gòu)是否完整勺美;
next:指向棧頂,也就是最新入棧的autorelease對(duì)象的下一個(gè)位置碑韵;
thread:指向當(dāng)前線(xiàn)程赡茸;
parent:指向父節(jié)點(diǎn)
child:指向子節(jié)點(diǎn)
depth:表示鏈表的深度,也就是鏈表節(jié)點(diǎn)的個(gè)數(shù)
hiwat:表示high water mark(最高水位標(biāo)記)
每一個(gè)AutoreleasePoolPage都是以雙鏈表的形式連接起來(lái)的
parent指向前一個(gè)page , child指向下一個(gè)page
push
一個(gè) push 操作其實(shí)就是創(chuàng)建一個(gè)新的 autoreleasepool 祝闻,對(duì)應(yīng) AutoreleasePoolPage 的具體實(shí)現(xiàn)就是往 AutoreleasePoolPage 中的 next 位置插入一個(gè) POOL_SENTINEL 占卧,并且返回插入的 POOL_SENTINEL 的內(nèi)存地址。
執(zhí)行一個(gè)具體的插入操作時(shí),分別對(duì)三種情況進(jìn)行了不同的處理:
- 當(dāng)前 page 存在且沒(méi)有滿(mǎn)時(shí)华蜒,直接將對(duì)象添加到當(dāng)前 page 中辙纬,即 next 指向的位置;
- 當(dāng)前 page 存在且已滿(mǎn)時(shí)叭喜,創(chuàng)建一個(gè)新的 page 贺拣,并將對(duì)象添加到新創(chuàng)建的 page 中;
- 當(dāng)前 page 不存在時(shí)捂蕴,即還沒(méi)有 page 時(shí)譬涡,創(chuàng)建第一個(gè) page ,并將對(duì)象添加到新創(chuàng)建的 page 中啥辨。
每調(diào)用一次 push 操作就會(huì)創(chuàng)建一個(gè)新的 AutoreleasePoolPage 涡匀,即往 AutoreleasePoolPage 中插入一個(gè) POOL_SENTINEL ,并且返回插入的 POOL_SENTINEL 的內(nèi)存地址委可。
pop
pop 函數(shù)的入?yún)⒕褪?push 函數(shù)的返回值渊跋,也就是 POOL_SENTINEL 的內(nèi)存地址即 pool token 。當(dāng)執(zhí)行 pop 操作時(shí)着倾,內(nèi)存地址在 pool token 之后的所有 autoreleased 對(duì)象都會(huì)被 release 拾酝。直到 pool token 所在 page 的 next 指向 pool token 為止。