自动释放池autoreleasepool 底层原理

 @autoreleasepool {
            MJPerson *person = [[[MJPerson alloc] init] autorelease];
    }

转成C++代码

{
    __AtAutoreleasePool __autoreleasepool;
    MJPerson *person = ((MJPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((MJPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((MJPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("MJPerson"), sel_registerName("alloc")), sel_registerName("init")), sel_registerName("autorelease"));
 }

__AtAutoreleasePool结构


struct __AtAutoreleasePool {
    __AtAutoreleasePool() { // 构造函数,在创建结构体的时候调用
        atautoreleasepoolobj = objc_autoreleasePoolPush();
    }
 
    ~__AtAutoreleasePool() { // 析构函数,在结构体销毁的时候调用
        objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
 
    void * atautoreleasepoolobj;
 };

所以autoreleasepool底层就是

 @autoreleasepool {
// 创建时调用push
//        atautoreleasepoolobj = objc_autoreleasePoolPush();
            MJPerson *person = [[[MJPerson alloc] init] autorelease];

// 销毁时调用pop
//        objc_autoreleasePoolPop(atautoreleasepoolobj);
    }

push 和 pop 分别调用的方法

void *
objc_autoreleasePoolPush(void)
{
    return AutoreleasePoolPage::push();
}

void
objc_autoreleasePoolPop(void *ctxt)
{
    AutoreleasePoolPage::pop(ctxt);
}

所以调用了autorelease的对象最终都是通过AutoreleasePoolPage对象来管理的


image.png

每个AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址
所有的AutoreleasePoolPage对象通过双向链表的形式连接在一起

流程

调用push方法会将一个POOL_BOUNDARY入栈,并且返回其存放的内存地址
对象调用autorelease,会将其地址放入当前的AutoreleasePoolPage中
调用pop方法时传入一个POOL_BOUNDARY的内存地址,会从最后一个入栈的对象开始发送release消息,直到遇到这个POOL_BOUNDARY
id *next指向了下一个能存放autorelease对象地址的区域

嵌套的autoreleasepool释放

 @autoreleasepool { //  r1 = push()
        
        MJPerson *p1 = [[[MJPerson alloc] init] autorelease];
        MJPerson *p2 = [[[MJPerson alloc] init] autorelease];
        
        @autoreleasepool { // r2 = push()
            for (int i = 0; i < 600; i++) {
                MJPerson *p3 = [[[MJPerson alloc] init] autorelease];
            }
            
            @autoreleasepool { // r3 = push()
                MJPerson *p4 = [[[MJPerson alloc] init] autorelease];

            } // pop(r3)
            
        } // pop(r2)
        
        
    } // pop(r1)
Snip20200718_1.png

流程为执行到pop(r3)时先p4发送release 然后遇到对应的POOL_BOUNDARY结束,执行到pop(r2)时,p3发送release释放遇到对应的POOL_BOUNDARY结束,执行到pop(r1)时p2,p1调用release遇到POOL_BOUNDARY结束.

autorelease对象在什么时机会被调用release

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 这个Person什么时候调用release,是由RunLoop来控制的
    // 它可能是在某次RunLoop循环中,RunLoop休眠之前调用了release
    MJPerson *person = [[[MJPerson alloc] init] autorelease];
    
    NSLog(@"%s", __func__);
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    
    NSLog(@"%s", __func__);
}

iOS在主线程的Runloop中注册了2个Observer
第1个Observer监听了kCFRunLoopEntry事件,会调用objc_autoreleasePoolPush()
第2个Observer
监听了kCFRunLoopBeforeWaiting事件,会调用objc_autoreleasePoolPop()、objc_autoreleasePoolPush()
监听了kCFRunLoopBeforeExit事件,会调用objc_autoreleasePoolPop()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。