ios 自动释放池

什么是自动释放池

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 为止。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏牛肉圆粉不加葱

[Spark源码剖析] JobWaiter

来创建容纳job结果的数据,数组的每个元素对应与之下标相等的partition的计算结果;并将结果处理函数(index, res) => results(ind...

7620
来自专栏前端真相

前端编码规范

26360
来自专栏Petrichor的专栏

python: set函数

27520
来自专栏Java面试通关手册

Java多线程学习(三)volatile关键字

Java面试通关手册(Java学习指南,欢迎Star,会一直完善下去,欢迎建议和指导):https://github.com/Snailclimb/Java_G...

20430
来自专栏积累沉淀

CXF 发布 Web Service

使用CXF框架开发 ①.CXF : xfire–>xfire + celtrix 做web service开发的开源框架 ②.开发Server端: 加入...

25060
来自专栏跟着阿笨一起玩NET

开源实体映射框架EmitMapper介绍

EmitMapper是一个开源实体映射框架,地址:http://emitmapper.codeplex.com/。

15320
来自专栏牛肉圆粉不加葱

Spark的位置优先: TaskSetManager 的有效 Locality Levels

在Spark Application Web UI的 Stages tag 上,我们可以看到这个的表格,描述的是某个 stage 的 tasks 的一些信息,其...

14530
来自专栏difcareer的技术笔记

JNI实现源码分析【四 函数调用】正文0x01:dvmCallMethodV0x02:nativeFunc0x03: 何时赋值

有了前面的铺垫,终于可以说说虚拟机是如何调用JNI方法的了。JNI方法,对应Java中的native方法,所以我们跟踪对Native方法的处理即可。

10940
来自专栏Java编程技术

ClassLoader解惑

一个Java程序要想运行起来,首先需要经过编译生成 .class文件,然后创建一个运行环境(jvm)来加载字节码文件到内存运行,而.class 文件是怎样被加载...

16410
来自专栏大内老A

采用一个自创的"验证框架"实现对数据实体的验证[扩展篇]

关于“验证框架”,先后推出了《编程篇》、《设计篇》和《改进篇》,本不打算再写《XXX篇》的。但是今天收到两个园友的短消息,想了解一下如何定义自己的验证规则。这实...

22570

扫码关注云+社区

领取腾讯云代金券