对@autoreleasepool的一些简单理解

142 阅读3分钟

一:什么是autoreleasepool

首先放一段苹果官方对autoreleasepool的描述

If your application creates a lot of temporary autoreleased objects within the event loop, however, it may be beneficial to create “local” autorelease pools to help to minimize the peak memory footprint.

可以看到这里面有几个关键词,temporary(临时),autoreleased objects(autoreleas对象),event loop(事件循环),minimize the peak memory(最小内存峰值)

那我们把他连起来就是如果你在一个事件循环中创建大量的临时autoreleas对象,又怕造成内存峰值过高的话,就可以使用@autoreleasepool来避免

二:通过一个简单例子来认识autoreleasepool

- (void)viewDidLoad {
    [super viewDidLoad];
    for (int i = 0; i < 10000; i++) {
        UIImage *imgge = [UIImage imageWithData:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"in" ofType:@"png"]]];
    }
}

通过运行这段代码,我发现内存发生了暴涨,注意如果这里换成[UIImage imageNamed]方法不会发现内存暴涨,因为这个方法会通过缓存获取图片

截屏2021-12-30 上午10.26.59.png

那么尝试加一下autoreleasepool呢

- (void)viewDidLoad {
    [super viewDidLoad];
    for (int i = 0; i < 10000; i++) {
        @autoreleasepool {
            UIImage *imgge = [UIImage imageWithData:[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"in" ofType:@"png"]]];
        }
    }
}

经过运行上面的代码,发现内存一直维持在50M左右,接下来说一下我浅显的理解,如果有不对的请及时联系我进行修改

三:针对上面的情况自己的理解

什么是autorelease对象呢,看别的帖子说系统方法非alloc,new,copy创建的对象为autorelease对象,这里存疑后面如果有答案会进行补充

那autorelease对象和普通对象有什么区别的,普通的对象在离开作用域后会立即释放,但是autorelease对象会延迟释放,对于autorelease对象,系统会加入到最近的AutoreleasePool中,当这个pool释放的时候,pool中所有的对象才会执行release。那这个AutoreleasePool是怎么来的呢,对于每一个runloop,系统会隐式的创建一个AutoreleasePool,当runloop结束时,当前栈顶的 AutoreleasePool会被销毁,pool里面的对象才会执行release。

针对上面的代码进行分析,如果没有添加@autoreleasepool的话,当前线程会隐式的创建一个runloop,循环创建image对象的时候,因为这种方式创建出来的为autorelease对象,所以不会立即释放,等当前runloop进行休眠或结束的时候才会全部释放,所以会造成内存暴涨,那我们手动加入@autoreleasepool以后,创建的image对象会push到我们手动创建的这个pool中,当作用域结束的时候,会进行pop操作,从而执行release操作,达到立即释放的效果,所以不会造成内存暴涨

四:autoreleasepool的内存结构

autoreleasepool是一个双向链表,包含了很多的AutoreleasePoolPage,还有2个很重要的方法objc_autoreleasePoolPush()和objc_autoreleasePoolPop(atautoreleasepoolobj)

接下来看一下AutoreleasePoolPage的结构

class AutoreleasePoolPage { 

PAGE_MAX_SIZE;//最大size 4096字节 

magic_t const magic; //用来校验AutoreleasePoolPage的结构是否完整 

id *next;//指向下一个即将产生的autoreleased对象的存放位置

pthread_t const thread;//指向当前线程

AutoreleasePoolPage * const parent;//指向父结点,可以找到上一个AutoreleasePoolPage

AutoreleasePoolPage *child;//指向子结点,可以找到下一个AutoreleasePoolPage

uint32_t const depth;//代表深度

}

五:objc_autoreleasePoolPush和objc_autoreleasePoolPop什么时候执行的

这两个方法是跟随runloop的

当即将进入loop时,会调用_objc_autoreleasePoolPush()方法,runtime会向当前的AutoreleasePoolPage中添加一个POOL_BOUNDARY(哨兵对象)

当runloop即将休眠时,会调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 清空自动释放池

当即将退出runloop时,会调用_objc_autoreleasePoolPop()清空自动释放池