有人告诉我@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块
autoreleasepool的实现是否发生变化?还是编译器进行了一些优化?
谢谢!
答案 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];
}