每創(chuàng)建一個池子氯析,會在首部創(chuàng)建一個 哨兵 對象,作為標記
最外層池子的頂端會有一個next指針登失。當鏈表容量滿了哈打,就會在鏈表的頂端塔逃,并指向下一張表。
Autorelease對象什么時候釋放料仗?
這個問題拿來做面試題湾盗,問過很多人,沒有幾個能答對的立轧。很多答案都是“當前作用域大括號結束時釋放”格粪,顯然木有正確理解Autorelease機制。
在沒有手加Autorelease Pool的情況下氛改,Autorelease對象是在當前的runloop迭代結束時釋放的帐萎,而它能夠釋放的原因是系統(tǒng)在每個runloop迭代中都加入了自動釋放池Push和Pop
例子:
__weak id reference = nil;
- (void)viewDidLoad {
[super viewDidLoad]; NSString *str = [NSString stringWithFormat:@"sunnyxx"]; // str是一個autorelease對象,設置一個weak的引用來觀察它
reference = str;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"%@", reference);
// Console: sunnyxx
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"%@", reference);
// Console: (null)
}
當然胜卤,我們也可以手動干預Autorelease對象的釋放時機:
- (void)viewDidLoad
{
[super viewDidLoad];
@autoreleasepool { NSString *str = [NSString stringWithFormat:@"sunnyxx"];
} NSLog(@"%@", str);
// Console: (null)
}
Autorelease原理
AutoreleasePoolPage
ARC下疆导,我們使用@autoreleasepool{}來使用一個AutoreleasePool,隨后編譯器將其改寫成下面的樣子:
void *context = objc_autoreleasePoolPush();
// {}中的代碼objc_autoreleasePoolPop(context);
而這兩個函數(shù)都是對AutoreleasePoolPage的簡單封裝葛躏,所以自動釋放機制的核心就在于這個類澈段。
AutoreleasePoolPage是一個C++實現(xiàn)的類
- AutoreleasePool并沒有單獨的結構悠菜,而是由若干個
AutoreleasePoolPage以雙向鏈表
的形式組合而成(分別對應結構中的parent指針和child指針)。 - AutoreleasePool是按
線程一一對應
的(結構中的thread指針指向當前線程)败富。 - AutoreleasePoolPage每個對象會開辟4096字節(jié)內存(也就是虛擬內存一頁的大谢诖住),除了上面的實例變量所占空間囤耳,剩下的空間全部用來儲存autorelease對象的
地址
篙顺。 - 上面的id *next指針作為游標指向棧頂最新add進來的autorelease對象的
下一個位置
。 - 一個AutoreleasePoolPage的空間被占滿時充择,會新建一個AutoreleasePoolPage對象德玫,連接鏈表,后來的autorelease對象在新的page加入椎麦。
所以宰僧,若當前線程中只有一個AutoreleasePoolPage對象,并記錄了很多autorelease對象地址時內存如下圖:
圖中的情況观挎,這一頁再加入一個autorelease對象就要滿了(也就是next指針馬上指向棧頂)琴儿,這時就要執(zhí)行上面說的操作,建立下一頁page對象嘁捷,與這一頁鏈表連接完成后造成,新page的next指針被初始化在棧底(begin的位置),然后繼續(xù)向棧頂添加新對象雄嚣。
所以晒屎,向一個對象發(fā)送- autorelease消息,就是將這個對象加入到當前AutoreleasePoolPage的棧頂next指針指向的位置
釋放時刻
每當進行一次objc_autoreleasePoolPush調用時缓升,runtime向當前的AutoreleasePoolPage中add進一個哨兵對象鼓鲁,值為0(也就是個nil),那么這一個page就變成了下面的樣子:
objc_autoreleasePoolPush的返回值正是這個哨兵對象的地址港谊,被objc_autoreleasePoolPop(哨兵對象)作為入?yún)⒑Э裕谑牵?/p>
1.根據(jù)傳入的哨兵對象地址找到哨兵對象所處的page
2.在當前page中,將晚于哨兵對象插入的所有autorelease對象都發(fā)送一次- release消息歧寺,并向回移動next指針到正確位置
3.補充2:從最新加入的對象一直向前清理燥狰,可以向前跨越若干個page,直到哨兵所在的page(在一個page中成福,是從高地址向低地址清理)
剛才的objc_autoreleasePoolPop執(zhí)行后碾局,最終變成了下面的樣子:
嵌套的AutoreleasePool
知道了上面的原理,嵌套的AutoreleasePool就非常簡單了奴艾,pop的時候總會釋放到上次push的位置為止,多層的pool就是多個哨兵對象而已内斯,就像剝洋蔥一樣蕴潦,每次一層像啼,互不影響。
更多:iOS面試題合集