• Autorelease机制讲解


    Autorelease机制是在iOS内存管理中的一员。在MRC中,是通过调用[obj autorelease]来延迟内存释放;在ARC中,我们已经完全不需要知道Autorelease就能很好地管理好内存。而在这背后,Objective-C帮我们做了什么呢,又是如何正确的管理好内存呢,下面我们来讲解Autorelease机制,希望大家对Autorelease有所进一步的了解!!!

    Autorelease对象什么时候释放呢?

    我不知道大家在面试的时候,有没有遇到过这样的问题,本人在悦动天下面试遇到过。如果拿Autorelease对象什么时候释放拿来做面试题,可能正确回答上来的没有几个,大家可能都会回答,“当前作用域也就是大括号结束时释放”,如果这样回答,显然没有正确很好地理解Autorelease机制。

    在如果没有手动加Autorelease Pool情况下,Autorelease对象是在当前runloop迭代结束之后才会释放,释放的原因是因为系统在每个runloop迭代中都已经加入了自动释放池push和pop。

    (每个runloop都会创建一个autoreleasepool并在runloop迭代结束之后进行释放)

    下面是一段代码讲述ARC与MRC的autorelease的使用,如下:

    // MRC
    NSAutoreleasePool *pool = [NSAutoreleasePool alloc] init];
    id obj = [NSObject alloc] init];
    [obj autorelease];
    [pool drain];
    
    // ARC
    @autoreleasepool {
      id obj = [NSObject alloc] init];
    }

    Autorelease的苹果实现

    我们可以通过Objective-C库runtime/objc-arr.mm来看一下苹果Autorelease实现。

    class AutoreleasePoolPage 
        {
            static inline void *push() 
            {
                相当于生成或持有NSAutoreleasePool类对象
            }
            static inline void *pop(void *token)
            {
                相当于废弃NSAutoreleasePool类对象
                releaseAll();
            }
            static inline id autorelease(id obj)
            {
                相当于NSAutoreleasePool类的addObject类方法   
                AutoreleasePoolPage *autoreleasePoolPage = 取得正在使用的AutoreleasePoolPage实例; 
                autoreleasePoolPage->add(obj);
            }
            id *add(id obj) 
            {
                将对象追加到内部数组中
            }
            void releaseAll() 
            {
                调用内部数组中对象的release实例方法 
            }
        };
        void *objc_autoreleasePoolPush(void)
        {
            return AutoreleasePoolPage::push();
        }
        void objc_autoreleasePoolPop(void *ctxt)
        {
            AutoreleasePoolPage::pop(ctxt);
        }
        id *objc_autorelease(id obj) 
        {
            return AutoreleasePoolPage::autorelease(obj);
        }

    在iOS 程序启动之后,主线程会创建一个Runloop,也会创建两个Observer,回调工作都是在_wrapRunLoopWithAutoreleasePoolHandler()函数中。

    第一个Observer监听的是Entry(即将进入Loop),回调是在_objc_autoreleasePoolPush()中创建自动释放池的,优先级是最高的,保证创建释放池是在所有回调之前

    第二个Observer监听有两个事件:BeforeWaiting(进入休眠)时调用_objc_autoreleasePoolPop()和_objc_autoreleasePoolPush()释放旧的释放池以及创建新的释放池;Exit(退出Loop)调用_objc_autoreleasePoolPop()来释放自动释放池。这个优先级是最低的,保证释放池发生在所有回调之后调用。

    通过上面的代码发现AutoreleasePoolPage是核心类:函数主要是调用了pushpop方法。下面我们一起看一下AutoreleasePoolPage这个类。

    AutoreleasePoolPage

    下面是AutoreleasePoolPage类:

    #   define EMPTY_POOL_PLACEHOLDER ((id*)1)
    
    #   define POOL_BOUNDARY nil
        static pthread_key_t const key = AUTORELEASE_POOL_KEY;
        static uint8_t const SCRIBBLE = 0xA3;  // 0xA3A3A3A3 after releasing
        static size_t const SIZE = 
    #if PROTECT_AUTORELEASEPOOL
            PAGE_MAX_SIZE;  // must be multiple of vm page size
    #else
            PAGE_MAX_SIZE;  // size and alignment, power of 2
    #endif
        static size_t const COUNT = SIZE / sizeof(id);
    
        magic_t const magic;
        id *next;
        pthread_t const thread;
        AutoreleasePoolPage * const parent;
        AutoreleasePoolPage *child;
        uint32_t const depth;
        uint32_t hiwat;

    在ARC下,我们如果使用@autoreleasepool{}来创建一个AutoreleasePool,随后编译器将会改成下面:

    void *context = objc_autoreleasePoolPush();
    // {}中的代码
    objc_autoreleasePoolPop(context);

    AutoreleasePoolPage是依靠C++实现的类。

    • AutoreleasePool没有单独的结构,是由许多个AutoreleasePoolPage以双链表的方式组合而成的,parent指向是前一个page,而child指向的是下一个page。
    • AutoreleasePoolPage中的每个对象都会开辟出虚拟内存一页的大小(也就是4096个字节),除了实例变量占据空间,其他的空间都用来存储autorelease对象的地址。
    • id *next指向的是栈顶对象的下一个位置
    • 如果AutoreleasePoolPage空间被占满时,会创建一个AutoreleasePoolPage连接链表,后来的对象也会在新的page加入。

    假设当前线程只有一个AutoreleasePoolPage对象,对象的内存地址如下图:

    从上面可以看出,当一个对象发送了autorelease消息,就是将当前这个对象加入到AutoreleasePoolPage的栈顶next指向的位置。

    释放时刻

    每进行一次objc_autoreleasePoolPush调用时,runtime就会将当前的AutoreleasePoolPage加入一个哨兵对象,就会变成下面结构:

    objc_autoreleasePoolPush返回值也就是哨兵对象的地址,被objc_autoreleasePoolPop作为参数。于是:

    1. 根据传入的哨兵位置找到哨兵所对应的page
    2. 将晚于哨兵对象插入的autorelease对象都发送一个release消息,并移动next指针到正确的位置

    自动释放池是以一个个AutoreleasePoolPage组成,而AutoreleasePoolPage以双链表形成的自动释放池

    pop的时候传入边界的对象,然后再对page中的对象发送release消息。

    以上就是Autorelease机制个人的理解,希望对大家有所帮助!!!

  • 相关阅读:
    阿里风控大脑关于大数据应用的探索与实践
    MaxCompute
    Lyft 基于 Flink 的大规模准实时数据分析平台(附FFA大会视频)
    基于MaxCompute InformationSchema进行血缘关系分析
    阿里经济体大数据平台的建设与思考
    厉害了!阿里云首批通过边缘云标准符合性测试
    边缘计算和“寒武纪”有什么关系?阿里云资深专家刘强如是说
    阿里云荣获最佳智能边缘计算技术创新平台
    阿里云杨敬宇:四层技术构建基于城市场景的边缘计算
    阿里云移动研发平台 EMAS 助力银行业打造测试中台,提升发版效能
  • 原文地址:https://www.cnblogs.com/guohai-stronger/p/9496706.html
Copyright © 2020-2023  润新知