??好的!今天我們來練習(xí)IOS,研究一下@autoreleasepool塊到底做了哪些事情。
自動釋放池
?? 在使用MRC管理內(nèi)存的時候,我們可以這樣創(chuàng)建一個自動釋放池并添加對象到自動釋放池,自動釋放池被銷毀后會向所有添加到池內(nèi)的對象發(fā)送釋放消息,代碼中也就是向obj0和obj1兩個局部對象發(fā)送釋放消息。
- (void)cerateAutoreleasepool {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];//創(chuàng)建自動釋放池
id obj0 = [[NSObject alloc] init];
id obj1 = [[NSObject alloc] init];
[obj0 autorelease];
[obj1 autorelease];
[pool drain];
}
??在ARC中,我們使用@autoreleasepool代碼塊實(shí)現(xiàn)自動釋放池,我個人的理解是放入@autoreleasepool代碼塊的對象會被添加到自動釋放池,也就是隱性的調(diào)用了autorelease,所以下面的代碼跟上面的其實(shí)是一個意思,當(dāng)離開@autoreleasepool代碼塊的時候,obj0和obj1收到了釋放的消息。
- (void)cerateAutoreleasepool {
@autoreleasepool {
id obj0 = [[NSObject alloc] init];
id obj1 = [[NSObject alloc] init];
}
}
案例
??以下的例子是我在實(shí)際工作中所碰到的一個問題,之前有一個需求是對一個有12個分頁的UIScrollView進(jìn)行截圖,要求是從第一個分頁開始截取,截完第二個,把第二個拼接在第一個下面,以此類推,拼接完12個頁面,以下是截圖與拼接的代碼。
- (UIImage *)screenShot {
UIGraphicsBeginImageContextWithOptions(self.view.frame.size, YES, 0.0);
[self.view drawViewHierarchyInRect:self.view.frame afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
- (UIImage *)addSlaveImage:(UIImage *)slaveImage toMasterImage:(UIImage *)masterImage {
CGSize size;
size.width = masterImage.size.width;
size.height = masterImage.size.height + slaveImage.size.height;
UIGraphicsBeginImageContextWithOptions(size, YES, 0.0);
[masterImage drawInRect:CGRectMake(0, 0, masterImage.size.width, masterImage.size.height)];
[slaveImage drawInRect:CGRectMake(0, masterImage.size.height, masterImage.size.width, slaveImage.size.height)];
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultImage;
}
- (UIImage *)imageFromStitchingNotUseAutoreleasepool {
UIImage *resultImage = [self screenShot];
for(int i = 0; i <= 12; i++) {
UIImage *screenShotImage = [self screenShot];
resultImage = [self addSlaveImage:screenShotImage toMasterImage:resultImage];
}
return resultImage;
}
??但是問題出現(xiàn)了,在截圖與拼接圖片的時候,UIGraphicsBeginImageContextWithOptions需要申請內(nèi)存,特別是在拼接到12張圖片的時候,需要申請的內(nèi)存已經(jīng)變得很大,如果是在老一點(diǎn)的機(jī)器上面就直接奔潰了,這時就需要用到@autoreleasepool代碼塊來優(yōu)化。
- (UIImage *)imageFromStitchingUseAutoreleasepool {
UIImage *resultImage = [self screenShot];
for(int i = 0; i <= 12; i++) {
//在這里添加 autoreleasepool
@autoreleasepool {
UIImage *screenShotImage = [self screenShot];
resultImage = [self addSlaveImage:screenShotImage toMasterImage:resultImage];
}
}
return resultImage;
}
??我個人的理解是在未添加@autoreleasepool代碼塊前,每次截圖都會有一個screenShotImage局部對象留在內(nèi)存中,這些對象在imageFromStitching方法結(jié)束生命周期才會被釋放,之后我添加了@autoreleasepool 代碼塊,把screenShotImage都放進(jìn)autoreleasepool,每次循環(huán)創(chuàng)建的screenShotImage都會在離開@autoreleasepool{ }之后被釋放而不是等到函數(shù)結(jié)束再釋放,這樣可以將一些臨時對象盡早的釋放掉,大大減少內(nèi)存的峰值,以下是我用xcode的Leaks工具測試的結(jié)果。
未使用@autoreleasepool內(nèi)存峰值變化

使用@autoreleasepool后內(nèi)存峰值變化

??好,問題到這里算是勉強(qiáng)解決了,當(dāng)然以上對autoreleasepool的理解只是個人的見解,如有錯誤請指出,對于截圖拼接的方法,也肯定會有大神有更好的方法去優(yōu)化(如果有的話請?jiān)u論告訴我),最后謝謝大家觀看?。?!??
最后,完整代碼鏈接:https://github.com/arrosev/IOSTestProjectCollection