好的!今天我们来练习IOS,研究一下@autoreleasepool块到底做了哪些事情。
自动释放池
在使用MRC管理内存的时候,我们可以这样创建一个自动释放池并添加对象到自动释放池,自动释放池被销毁后会向所有添加到池内的对象发送释放消息,代码中也就是向obj0和obj1两个局部对象发送释放消息。
- (void)cerateAutoreleasepool {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];//创建自动释放池
id obj0 = [[NSObject alloc] init];
id obj1 = [[NSObject alloc] init];
[obj0 autorelease];
[obj1 autorelease];
[pool drain];
}
在ARC中,我们使用@autoreleasepool代码块实现自动释放池,我个人的理解是放入@autoreleasepool代码块的对象会被添加到自动释放池,也就是隐性的调用了autorelease,所以下面的代码跟上面的其实是一个意思,当离开@autoreleasepool代码块的时候,obj0和obj1收到了释放的消息。
- (void)cerateAutoreleasepool {
@autoreleasepool {
id obj0 = [[NSObject alloc] init];
id obj1 = [[NSObject alloc] init];
}
}
案例
以下的例子是我在实际工作中所碰到的一个问题,之前有一个需求是对一个有12个分页的UIScrollView进行截图,要求是从第一个分页开始截取,截完第二个,把第二个拼接在第一个下面,以此类推,拼接完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;
}
但是问题出现了,在截图与拼接图片的时候,UIGraphicsBeginImageContextWithOptions需要申请内存,特别是在拼接到12张图片的时候,需要申请的内存已经变得很大,如果是在老一点的机器上面就直接奔溃了,这时就需要用到@autoreleasepool代码块来优化。
- (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局部对象留在内存中,这些对象在imageFromStitching方法结束生命周期才会被释放,之后我添加了@autoreleasepool 代码块,把screenShotImage都放进autoreleasepool,每次循环创建的screenShotImage都会在离开@autoreleasepool{ }之后被释放而不是等到函数结束再释放,这样可以将一些临时对象尽早的释放掉,大大减少内存的峰值,以下是我用xcode的Leaks工具测试的结果。
未使用@autoreleasepool内存峰值变化
使用@autoreleasepool后内存峰值变化
好,问题到这里算是勉强解决了,当然以上对autoreleasepool的理解只是个人的见解,如有错误请指出,对于截图拼接的方法,也肯定会有大神有更好的方法去优化(如果有的话请评论告诉我),最后谢谢大家观看!!!
最后,完整代码链接:https://github.com/arrosev/IOSTestProjectCollection