循环中的@autoreleasepool块不会减少内存峰值

时间:2018-10-08 03:54:05

标签: ios nsautoreleasepool

有人告诉我@autoreleasepool块在循环中可以减少内存使用量的峰值,直到执行测试为止。测试设备是装有iOS 11.4.1的iPhone 6s。

我的代码:

@implementation BigMemObj{
    NSMutableArray *_mutArr;
}

-(instancetype)init{
    if(self = [super init]){
        _mutArr = [[NSMutableArray alloc] initWithCapacity:1024*1024*30];
        for(int i = 0; i < 1024*1024*30; i++){
            [_mutArr addObject:@(i)];
        }
    }

    return self;
}


- (void)viewDidLoad {

    NSMutableArray *arr = [[NSMutableArray alloc] init];
    for(int i = 0 ; i < 10000; i++){
        @autoreleasepool {
            BigMemObj *mem = [[BigMemObj alloc] init];
        }
    }
}

- (void)viewDidLoad {

    NSMutableArray *arr = [[NSMutableArray alloc] init];
    for(int i = 0 ; i < 10000; i++){
           BigMemObj *mem = [[BigMemObj alloc] init];
    }
}

我在测试1中都运行了34秒,在测试1中,最高内存使用量为458M,但是在测试2中,最高内存使用量为362M。并且两个测试都具有三角形形状。

带有@autoreleaspool块

enter image description here

没有@autoreleaspool块 enter image description here

autoreleasepool的实现是否发生变化?还是编译器进行了一些优化?

谢谢!

1 个答案:

答案 0 :(得分:0)

实际上看起来一切正常。您所看到的增长就是这一部分:

_mutArr = [[NSMutableArray alloc] initWithCapacity:1024*1024*30];
for(int i = 0; i < 1024*1024*30; i++){
    [_mutArr addObject:@(i)];
}

因此,您在此处将数字添加到数组_mutArr中,并在其中添加1024*1024*30。该循环完成后,_mutArr有效且已满,它将保留所有这些数字。在此循环中添加另一个自动释放池甚至都不会更改此设置,因为您的阵列将不允许释放这些数字。

现在调用此构造函数后,您便拥有

@autoreleasepool {
    BigMemObj *mem = [[BigMemObj alloc] init];
}

因此,此时自动释放池将耗尽,释放BigMemObj实例mem中的所有数字,并且您的内存恢复正常。

您可能希望您的应用程序在不调用@autoreleasepool的情况下仍会不断增长。但是,如果删除该呼叫,则根本没有任何改变。这样做的原因是,您的代码都根本没有使用自动释放池。您的代码转换为(非ARC)的内容是:

NSMutableArray *arr = [[NSMutableArray alloc] init];
for(int i = 0 ; i < 10000; i++){
    @autoreleasepool {
        BigMemObj *mem = [[BigMemObj alloc] init];
        [mem release];
    }
}
[arr release];

但是您只有在autoreleasepool时才需要

NSMutableArray *arr = [[NSMutableArray alloc] init];
for(int i = 0 ; i < 10000; i++){
    @autoreleasepool {
        BigMemObj *mem = [[[BigMemObj alloc] init] autorelease];
    }
}
[arr release];

要遇到需要自动释放池的情况:

NSMutableArray *allBigValues = [[NSMutableArray alloc] init];

NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"profile.png"];

for(int i = 0; i<100000; i++){
    @autoreleasepool {
        [allBigValues addObject:[UIImage imageWithContentsOfFile:path]];
        [allBigValues removeAllObjects];
    }
}

如果在此代码中删除自动释放池,它将在内存中增长,直到循环结束。这样做的原因是因为imageWithContentsOfFile正在使用自动释放池,并且由该方法生成的所有图像仅在耗尽该池之后才释放。由于主池不会在循环内释放,因此我们需要创建另一个池。

最重要的是,此代码可以很好地工作,但是一旦删除@autoreleasepool部分,它将开始在内存中增长,并可能使应用程序崩溃。

注释1:您需要将图像profile.png添加到您的应用中,此代码才能起作用(只需将其拖到源文件中,而不是资产中)。

注释2:我们在池中使用“流失”,因为它曾经是您希望池删除其对象时需要调用的方法的名称。以前是这样的:

for(int i = 0; i<100000; i++){
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    [allBigValues addObject:[UIImage imageWithContentsOfFile:path]];
    [allBigValues removeAllObjects];
    [pool drain];
}