前言
Objective-C 對象的聲明周期取決于其引用計數(shù)。釋放對象有兩種方式:
- 調(diào)用 release 方法坚洽,使其保留計數(shù)立即遞減
- 調(diào)用 autorelease 方法糜工,將其加入自動釋放池中。清空自動釋放池時夫壁,系統(tǒng)會向其中的對象發(fā)送 release 消息。
那么問題來了庆械,自動釋放池什么時候清空呢薇溃。下面我們詳細(xì)介紹下 Autorelease pool。
autorelease 對象何時釋放缭乘。
autorelease 對象會在 autoreleasepool 清空時收到 release 消息沐序。
- 系統(tǒng)創(chuàng)建的線程中(如主線程和global queue),默認(rèn)都有自動釋放池堕绩,當(dāng)前的 runloop 迭代結(jié)束時策幼,會將其清空。
- 手動添加 autoreleasepool 逛尚,會在當(dāng)前作用域大括號結(jié)束時清空垄惧。
手動添加 autoreleasepool
下面這段代碼中刁愿,花括號定義了自動釋放池的范圍绰寞,自動釋放池于左括號處創(chuàng)建,于右括號處自動清空铣口。位于此范圍內(nèi)的對象滤钱,將在此范圍末尾處收到 release 消息。
@autoreleasepool{
// ...
}
自動釋放池可以嵌套脑题,本例中的兩個對象都是由類的工廠方法所創(chuàng)建件缸,這樣創(chuàng)建的對象會自動釋放,NSString 類型的對象放在外圍的釋放池中叔遂,NSNumber 類型的對象則放在里層的釋放池中他炊。
@autoreleasepool{
NSString *string = [NSString stringWithFormat:@"1 = %i",1];
@autoreleasepool {
NSNumber *number = [NSNumber numberWithInt:1];
}
}
autoreleasepool 用法
一般情況下無須手動創(chuàng)建自動釋放池。
系統(tǒng)會自動創(chuàng)建一些線程已艰,比如主線程和GCD機制中的線程痊末,這些線程默認(rèn)都有自動釋放池,每次開始一次新的事件循環(huán)時會創(chuàng)建自動釋放池哩掺,事件循環(huán)(event loop)結(jié)束時凿叠,就會將其清空,所以嚼吞,一般不需要自己創(chuàng)建自動釋放池盒件。
以下兩種情況需要手動創(chuàng)建
- main 函數(shù)中。
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
從技術(shù)角度看舱禽,不是非得有個自動釋放池塊炒刁。因為塊的末尾剛好是應(yīng)用程序的終止處,此時操作系統(tǒng)會把程序的全部內(nèi)存釋放掉誊稚。
但是翔始,如果不寫這個塊的話飒筑,那么由 UIApplication 函數(shù)所自動釋放的那些對象,就沒有自動釋放池可以容納了绽昏。系統(tǒng)也會發(fā)出警告消息來表明這一情況协屡。
- for 循環(huán)中創(chuàng)建大量對象時
如果 for 循環(huán)里創(chuàng)建大量臨時對象,自動釋放池要等下一次事件循環(huán)才會清空全谤。這就意味著在執(zhí)行for循環(huán)時肤晓,會持續(xù)有新對象創(chuàng)建出來,并加入釋放池中认然。所有對象要等for循環(huán)執(zhí)行完才會釋放补憾,會導(dǎo)致內(nèi)存持續(xù)上漲。
NSArray *databaseRecords = /* ... */
NSMutableArray *people = [NSMutableArray new];
for (NSDictionary *record in databaseRecords) {
@autoreleasepool {
WYJPerson *person = [WYJPerson alloc] initWithRecord: record];
[people addObject:person];
}
}
用自動釋放池將循環(huán)中的語句包起來卷员,那么循環(huán)中自動釋放的對象會放在這個池盈匾,而不是主線程的自動釋放池。這樣毕骡,每次循環(huán)時都會建立并清空釋放池削饵。
Autorelease pool 實現(xiàn)原理
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.
上面這段是蘋果官方對 autorelease pool 實現(xiàn)的一段描述。
大概意思是:每個線程的 autorelease pool 都是一個存放指針的棧未巫。每個指針都指向一個需要釋放的對象窿撬,或者是 autorelease pool 的一個邊界。pool token 是一個指向釋放池邊界的指針叙凡。當(dāng)釋放池被彈出時劈伴,哨兵對象之后加入的對象都會被釋放。棧是由page組合成雙向鏈表握爷,需要時添加或刪除page跛璧。
AutoreleasePoolPage
void *context = objc_autoreleasePoolPush();
// {}中的代碼
objc_autoreleasePoolPop(context);
這兩個函數(shù)都是對AutoreleasePoolPage的簡單封裝。