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是核心类:函数主要是调用了push和pop方法。下面我们一起看一下AutoreleasePoolPage这个类。

AutoreleasePoolPage

下面是AutoreleasePoolPage类:

#   define EMPTY_POOL_PLACEHOLDER ((id*))

#   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机制个人的理解,希望对大家有所帮助!!!

Autorelease机制讲解的更多相关文章

  1. 75. Autorelease机制及释放时机

    Autorelease机制是iOS开发人员管理对象内存的好伙伴.MRC中.调用[obj autorelease]来延迟内存的释放是一件简单自然的事:ARC下,我们甚至能够全然不知道Autoreleas ...

  2. 从自旋锁、睡眠锁、读写锁到 Linux RCU 机制讲解

    ​    同步自我的 csdn 博客 6.S081 从自旋锁.睡眠锁.读写锁到 Linux RCU 机制讲解_我说我谁呢 --CSDN博客 总结一下 O/S 课程里面和锁相关的内容. 本文是 6.S0 ...

  3. 最全面的Android Intent机制讲解

    对于大型软件开发经验较少的程序员来说,这可能是一个不太容易理解的抽象概念,因为它与我们平常使用的简单函数调用,或者通过库调用接口的方式不太一样.在 Intent 的使用中你看不到直接的函数调用,相对函 ...

  4. Elasticsearch系列---分布式架构机制讲解

    概要 本篇主要介绍Elasticsearch的数据索引时的分片机制,集群发现机制,primary shard与replica shard是如何分工合作的,如何对集群扩容,以及集群的容错机制. 分片机制 ...

  5. redis哨兵机制讲解

    原文链接:https://blog.csdn.net/yswKnight/article/details/78158540 一.什么是哨兵机制? 答:Redis的哨兵(sentinel) 系统用于管理 ...

  6. 品茗论道说广播(Broadcast内部机制讲解)(下)

    下面我们来看,递送广播动作中最重要的processNextBroadcast(). 3.2 最重要的processNextBroadcast() 从processNextBroadcast()的代码, ...

  7. 品茗论道说广播(Broadcast内部机制讲解)(上)

    1 概述 我们在编写Android程序时,常常会用到广播(Broadcast)机制.从易用性的角度来说,使用广播是非常简单的.不过,这个不是本文关心的重点,我们希望探索得再深入一点儿.我想,许多人也不 ...

  8. mybatis代理机制讲解

    问题描述 在使用Mybatis开发中,或者和Spring整合中,在Dao层中的Mapper接口与xml中的sql对应着,在service中直接调用Dao中的方法就可以直接访问sql.如下所示: /** ...

  9. soap消息机制 讲解

    SOAP(Simple Object Access Protocol,简单对象访问协议)作为一种信息交互协议在分布式应用中非常广泛,如WebService.在使用.Net开发WebService时候, ...

随机推荐

  1. mysql import error

    mysql导入文件一直出错,显示ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option s ...

  2. .Net Core 部署 CentOs7+Nginx

    先爆图 由于是初学者,部署出来这个界面也不容易,此前第一步弄了个这个出来 动态的没问题,然后静态资源死活就是不出来,弄了两个小时没有结果,带着遗憾睡了个觉 试验1: server { listen ; ...

  3. Mac上常用工具总结

    iOS开发辅助工具 Reveal :Xcode辅助界面调试工具 官网地址:https://revealapp.com/download/ 图标: SimPholders : 快速打开模拟器中的应用程序 ...

  4. http://www.layui.com/doc/modules/laydate.html实时通信\日期、==插件

    8520**ali chengyouli http://www.layui.com/doc/modules/laydate.html实时通信\日期.==插件

  5. HTML标题 段落 样式

    HTML 标题 标题(Heading)是通过 <h1> - <h6> 等标签进行定义的. <h1> 定义最大的标题.<h6> 定义最小的标题. 注释:默 ...

  6. WebAssembly让你的Javascript计算性能提升70%

    现在的JavaScript代码要进行性能优化,通常使用一些常规手段,如:延迟执行.预处理.setTimeout等异步方式避免处理主线程,高大上一点的会使用WebWorker.即使对于WebWorker ...

  7. React-Native的基本控件属性方法,对React-Native的学习,从熟悉基本控件开始。

    对React-Native的学习,从熟悉基本控件开始. View 属性方法 序号 名称 属性Or方法 类型 说明 1 accessibilityLabel 属性 string   2 accessib ...

  8. 阿里面试100%问到,JVM性能调优篇

    JVM 调优概述 性能定义 吞吐量 - 指不考虑 GC 引起的停顿时间或内存消耗,垃圾收集器能支撑应用达到的最高性能指标. 延迟 - 其度量标准是缩短由于垃圾啊收集引起的停顿时间或者完全消除因垃圾收集 ...

  9. 吴恩达机器学习笔记56-多元高斯分布及其在误差检测中的应用(Multivariate Gaussian Distribution & Anomaly Detection using the Multivariate Gaussian Distribution)

    一.多元高斯分布简介 假使我们有两个相关的特征,而且这两个特征的值域范围比较宽,这种情况下,一般的高斯分布模型可能不能很好地识别异常数据.其原因在于,一般的高斯分布模型尝试的是去同时抓住两个特征的偏差 ...

  10. [Swift]LeetCode623. 在二叉树中增加一行 | Add One Row to Tree

    Given the root of a binary tree, then value v and depth d, you need to add a row of nodes with value ...