IOS @autoreleasepool

  好的!今天我们来练习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内存峰值变化
NotUseAutoreleasepool.jpg
使用@autoreleasepool后内存峰值变化

UseAutoreleasepool.jpg

  好,问题到这里算是勉强解决了,当然以上对autoreleasepool的理解只是个人的见解,如有错误请指出,对于截图拼接的方法,也肯定会有大神有更好的方法去优化(如果有的话请评论告诉我),最后谢谢大家观看!!!
最后,完整代码链接:https://github.com/arrosev/IOSTestProjectCollection

你可能感兴趣的:(IOS @autoreleasepool)