首先,看两段代码执行之后,内存的变化:
1、
// 未使用@autoreleasepool
for (int i = 0; i < 100000; i ++) {
UIImage *img = [UIImage imageNamed:@"test.jpg"];
NSLog(@"%@", @(i));
}
分析:imageName 返回的是 autorelease 的对象,因为我们一直处在循环中,因此它们将一直没有机会被释放。如果数量太多而且数据太大的时候,很容易因为内存不足而崩溃。若 for 循环中为 非autorelease 的对象,不会出现内存问题。
如下图,我们可以看到内存占用情况:
2、
// 使用@autoreleasepool
for (int i = 0; i < 100000; i ++) {
@autoreleasepool {
UIImage *img = [UIImage imageNamed:@"test.jpg"];
NSLog(@"%@", @(i));
}
}
分析:这就涉及到 autorelease 的对象在什么时候释放的问题。
引用【在没有手加Autorelease Pool的情况下,Autorelease对象是在当前的runloop迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop】。对于我们手动添加的@autoreleasepol{},显然出了它的作用域之后对象就会被release。这里我们每一次循环都生成了一个自动释放池,虽然可以保证内存使用达到最小,但是释放过于频繁也会带来潜在的性能忧虑。一个折中的方法是将循环分隔开加入自动释放池,比如每10次循环对应一次自动释放,这样能减少带来的性能损失。
如下图,内存占用情况:
什么时候用@autoreleasepool
根据Apple的文档,使用场景如下:
- 写基于命令行的的程序时,就是没有UI框架,如AppKit等Cocoa框架时。
- 写循环,循环里面包含了大量临时创建的对象。(本文的例子)
- 创建了新的线程。(非Cocoa程序创建线程时才需要)
- 长时间在后台运行的任务。
注:
1、什么是autorelease 的对象?
alloc、new、copy、mutableCopy四个内存管理的关键字,自身使用了这些关键字产生的对象都不是 autorelease 的对象,自身持有对象,那么不再需要时对象就会调用release方法释放对象;
不是通过上面关键字产生的都是 autorelease 的对象,底层会将该对象注册到 autoreleasepool 中,不会立即释放,会在pool结束时,释放对象。
2、参考
iOS内存管理
黑幕背后的Autorelease
@autoreleasepool-内存的分配与释放