美文网首页iOS
内存管理autorelease

内存管理autorelease

作者: 越天高 | 来源:发表于2020-11-15 20:17 被阅读0次

    自动释放是在什么时候释放的
    在MRC环境下,释放对象有两种方式一种是创建完对象调用autorelease,或者是手动release。为了方便使用对象,一般情况下都是调用autorelease。
    要想查看到调用了autorelease的对象什么时候会被释放,就要先研究一下,autoreleasepool,表面上看是在autoreleasepool的大括号结束的时候,就释放了对象。将代码转成CPP文件查看autoreleasepool的结构,

    @autoreleasepool
        {
            Person *person = [[Person alloc] init];//正常情况出了大括号就会自动销毁
        }
    //转成cpp
    { __AtAutoreleasePool __autoreleasepool;
     //相当于在这里调用了构造函数
     atautoreleasepoolobj = objc_autoreleasePoolPush();
         Person *person = [[Person alloc] init];
     //在大括号结束的时候调用了析构函数
     objc_autoreleasePoolPop(atautoreleasepoolobj);
     }
     
     struct __AtAutoreleasePool {
       __AtAutoreleasePool() {//构造函数,在结构体创建的时候调用
     atautoreleasepoolobj = objc_autoreleasePoolPush();}
       ~__AtAutoreleasePool() {//析构函数,在结构体销毁的时候调用
     objc_autoreleasePoolPop(atautoreleasepoolobj);}
       void * atautoreleasepoolobj;
     };
    

    以此类推,没增加一个autoreleasepool就会多增加一次上面的结构

    02 objc_autoreleasePoolPush和objc_autoreleasePoolPop做的事情

    要清楚autorelease对象什么时候销毁,就要高清这两个函数做了什么事情
    通过源码可以看出他们通过方法调用来操作autoreleasepoolPage
    自动释放池的主要底层数据结构是:__AtAutoreleasePool、AutoreleasePoolPage
    调用了autorelease的对象,他的生命周期最终都是通过AutoreleasePoolPage对象来管理的
    源码分析
    clang重写@autoreleasepool
    objc4源码:NSObject.mm


    autoreleasepoolPage

    每个AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址
    所有的AutoreleasePoolPage对象通过双向链表的形式连接在一起
    双向链表就是两个对象产生联系,左边的对象有个指针指向右边的对象,右边的对象也有个指针指向左边的对象,
    因为内存是有限的,一个autoreleasepoolpage只有4096当不够的时候就会使用新的page
    AutoreleasePoolPage里面一个begin和end一点调用begin他就会返回存放autorelease对象的首个地址,其实就是用this指针+sizeof(this)


    page的双向链表

    03push和pop做的事情

    调用push方法会将一个POOL_BOUNDARY入栈,并且返回其存放的内存地址,然后将autorelease对象放在这个地址后面,如果这个autoreleasepoolPage空间放满了,那么会创建一个新的page来存放autorelease对象,
    调用pop方法时传入一个POOL_BOUNDARY的内存地址,会从最后一个入栈的对象开始发送release消息,直到遇到这个POOL_BOUNDARY
    id *next指向了下一个能存放autorelease对象地址的区域,
    当出现多个autoreleasepool嵌套的时候,没出现一个push都会加入一个POOL_BOUNDARY

    04

    poolPage的结构,
    如果是有多个autoreleasepool对象,如果page的内存够,他们会放到同一个page中,这一个page满了之后才会往下面存

    objc[1228]: ##############
    objc[1228]: AUTORELEASE POOLS for thread 0x1000d2dc0
    objc[1228]: 7 releases pending.
    objc[1228]: [0x101803000]  ................  PAGE  (hot) (cold)
    objc[1228]: [0x101803038]  ################  POOL 0x101803038
    objc[1228]: [0x101803040]       0x10051b200  Person
    objc[1228]: [0x101803048]       0x100519700  Person
    objc[1228]: [0x101803050]  ################  POOL 0x101803050
    objc[1228]: [0x101803058]       0x100519290  Person
    objc[1228]: [0x101803060]       0x1005188c0  Person
    objc[1228]: [0x101803068]       0x100516cf0  NSObject
    objc[1228]: ##############
    

    cold和hot
    hot是指当前正在使用的page,如果我们取对象是从当前page中开始,那么当前的page就可以称作hot

    05push和pop的具体调用过程

    autorelease和runloop01

    autorelease对象会在什么时机调用release
    如果这个对象是被一个autoreleasepool包裹住的话,他的release时机就在这个大括号的结束
    如果没有的话,

    autorelease和runloop02

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

    这个对象什么时候释放是有runloop控制的
    他可能是在某次循环中,runloop休眠之前调用了release

    方法中有局部对象,出了括号就马上释放么?
    如果这个对象是通过autorelease的方式的话,不是马上释放,要等那次runloop休眠之前释放,如果是普通的arc代码是会马上释放的

    相关文章

      网友评论

        本文标题:内存管理autorelease

        本文链接:https://www.haomeiwen.com/subject/zngdbktx.html