我們先來看一個經(jīng)典的性能問題:
for (int i = 0; i <= 1000; i ++) {
UIImage *image = [UIImage imageNamed:@"test"];
//Do something...
}
按照C語言局部變量的定義,image超出了作用域就會被釋放,可是在測試的時候發(fā)現(xiàn)這里內(nèi)存一直在增加,這是為什么呢?
解析:
[UIImage imageNamed:] 創(chuàng)建的對象是Autorelease的篷店,Autorelease的對象會添加到最近的一個AutoreleasePool里面,等AutoreleasePool結(jié)束的時候統(tǒng)一釋放臭家。
好了船庇,那什么是Autorelease呢吭产?
想知道什么是Autorelease,我們先回顧一下MRC下的retain鸭轮,release機制臣淤。retain表示持有對象,release表示釋放對象窃爷。
使用以下名稱開頭的方法名意味著自己生成的對象自己持有:
- alloc
- new
- copy
- mutableCopy
id obj = [[NSObject alloc] init];//自己持有
[obj release];//自己釋放
除了以上四種方式創(chuàng)建的對象邑蒋,都不是自己持有的
id obj = [NSMutableArray array];//自己不持有
[obj retain]; //自己持有對象
[obj release]; //釋放對象
我們來看看array的實現(xiàn):
+ (id)array {
id obj = [[NSMutableArray alloc] init];//創(chuàng)建對象
[obj autorelease];//延遲釋放對象(誰創(chuàng)建誰釋放)
return obj;
}
使用[NSMutableArray array]
創(chuàng)建對象時,因為本身不持有對象按厘,又想取得對象存在医吊。Autorelease就是提供了這樣的功能,使對象在超出指定的生存范圍時能夠自動并正確地釋放逮京。
Autorelease實際上只是把對release的調(diào)用延遲了卿堂,對于每一個Autorelease,系統(tǒng)只是把該Object放入了當(dāng)前的AutoreleasePool中懒棉,當(dāng)該pool被釋放時草描,該pool中的所有Object會被調(diào)用Release。對于每一個Runloop策严, 系統(tǒng)會隱式創(chuàng)建一個AutoreleasePool穗慕,這樣所有的release pool會構(gòu)成一個象CallStack一樣的一個棧式結(jié)構(gòu),在每一個Runloop結(jié)束時妻导,當(dāng)前棧頂?shù)?AutoreleasePool會被銷毀逛绵,這樣這個pool里的每個Object會被release。
那怎樣判斷創(chuàng)建的對象是否Autorelease的呢倔韭?
使用alloc/new/copy/mutableCopy的對象都不是Autorelease的术浪,非以上方法創(chuàng)建的都是Autorelease的
具體可以使用以下方法測試:
__weak id reference = nil;
- (void)viewDidLoad {
[super viewDidLoad];
NSString *str = [NSString stringWithFormat:@"xhj"];
// str是一個autorelease對象,設(shè)置一個weak的引用來觀察它
reference = str;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"%@", reference);// Console: xhj
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"%@", reference);//Console:nil
}
如果reference在 viewWillApper:
方法還沒有被釋放寿酌,就代表就是Autorelease的對象
文章開頭問題的解決
for (int i = 0; i <= 1000; i ++) {
@autoreleasepool {
UIImage *image = [UIImage imageNamed:@"test"];
//Do something...
}
}
在每次循環(huán)都會自動釋放image對象胰苏。
或者:
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// 這里被一個局部@autoreleasepool包圍著
UIImage *image = [UIImage imageNamed:@"test"];
//Do something...
}];
在使用容器的block版本的枚舉器時,內(nèi)部會自動添加一個AutoreleasePool
參考文檔 :
《Objective-C 高級編程 iOS與OS X多線程和內(nèi)存管理》
黑幕背后的Autorelease
@autoreleasepool-內(nèi)存的分配與釋放