ios 自动释放池

作者: 赵哥窟 | 来源:发表于2018-08-13 17:11 被阅读46次
    什么是自动释放池

    OC中的一种内存自动回收机制,它可以延迟加入AutoreleasePool中的变量release的时机,即当我们创建了一个对象,并把他加入到了自动释放池中时,他不会立即被释放,会等到一次runloop结束或者作用域超出{}或者超出[pool release]之后再被释放

    自动释放池的创建与销毁时机
    MRC:
    NSAutoreleasePool *pool = [[ NSAutoreleasePool alloc]init ];//创建一个自动释放池
    Person *person = [[Person alloc]init];
    //调autorelease方法将对象加入到自动释放池
    [person autorelease];
    //手动释放自动释放池执行完这行代码是,自动释放池会对加入他中的对象做一次release操作
    [pool release];
    ···
    自动释放池销毁时机:[pool release]代码执行完后.
    
    ARC
    @autoreleasepool {
        //在这个{}之内的变量默认被添加到自动释放池
        Person *p = [[Person alloc] init];
    }//除了这个括号,p被释放
    
    Autorelease实现原理

    下面看一段简单的代码

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // insert code here...
            NSLog(@"Hello, World!");
        }
        return 0;
    }
    

    然后在终端中使用clang -rewrite-objc main.m 命令将上述OC代码重写成C++的实现
    搜索main我们可以看到main函数的实现重写成了如下代码:

    int main(int argc, const char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
    
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_49_sdbnp0nd07q4m_sh4_gw52r40000gn_T_main_9e48ee_mi_0);
        }
        return 0;
    }
    

    通过对比可以发现,苹果通过声明一个__AtAutoreleasePool类型的局部变量
    @autoreleasepool被转转换成__AtAutoreleasePool 结构体类型

    struct __AtAutoreleasePool {
      __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
      ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
      void * atautoreleasepoolobj;
    };
    

    可以看到 __AtAutoreleasePool() 构造函数调用objc_autoreleasePoolPush(),~__AtAutoreleasePool() 析构函数调用 objc_autoreleasePoolPop()

    objc_autoreleasePoolPush 和 objc_autoreleasePoolPop 是什么呢?
    在 NSObject.mm 文件中:

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

    实际上是调用AutoreleasePoolPage的push和pop两个类方法

    首先来看一下AutoreleasePoolPage这个类

    class AutoreleasePoolPage {
            magic_t const magic;
            id *next;
            pthread_t const thread;
            AutoreleasePoolPage * const parent;
            AutoreleasePoolPage *child;
            uint32_t const depth;
            uint32_t hiwat;
        };
    

    magic:用来校验 AutoreleasePoolPage 的结构是否完整;
    next:指向栈顶,也就是最新入栈的autorelease对象的下一个位置;
    thread:指向当前线程;
    parent:指向父节点
    child:指向子节点
    depth:表示链表的深度,也就是链表节点的个数
    hiwat:表示high water mark(最高水位标记)

    每一个AutoreleasePoolPage都是以双链表的形式连接起来的


    1622234d47c6c602b193cb512f160cf4.png

    parent指向前一个page , child指向下一个page

    push
    5948f2dadd36e4ea2274e5f8ad798cca.png

    一个 push 操作其实就是创建一个新的 autoreleasepool ,对应 AutoreleasePoolPage 的具体实现就是往 AutoreleasePoolPage 中的 next 位置插入一个 POOL_SENTINEL ,并且返回插入的 POOL_SENTINEL 的内存地址。

    执行一个具体的插入操作时,分别对三种情况进行了不同的处理:

    1. 当前 page 存在且没有满时,直接将对象添加到当前 page 中,即 next 指向的位置;
    2. 当前 page 存在且已满时,创建一个新的 page ,并将对象添加到新创建的 page 中;
    3. 当前 page 不存在时,即还没有 page 时,创建第一个 page ,并将对象添加到新创建的 page 中。

    每调用一次 push 操作就会创建一个新的 AutoreleasePoolPage ,即往 AutoreleasePoolPage 中插入一个 POOL_SENTINEL ,并且返回插入的 POOL_SENTINEL 的内存地址。

    pop


    6667ce10c80e53c23eab2f59c98af50b.png

    pop 函数的入参就是 push 函数的返回值,也就是 POOL_SENTINEL 的内存地址即 pool token 。当执行 pop 操作时,内存地址在 pool token 之后的所有 autoreleased 对象都会被 release 。直到 pool token 所在 page 的 next 指向 pool token 为止。

    相关文章

      网友评论

        本文标题:ios 自动释放池

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