ios runloop的Autorelease对象释放的背后

前言:

Autorelease机制对于iOS开发人员对对象的内存管理省下不少心血,说白了就是你甭管内存的管理问题,我会在背后帮你处理,不需要你操碎了心去避雷,这就是ARC的最大的好处!正所谓任何事情都是一把双刃剑,还有一个block里面循环引用的雷池要注意的,如何注意其实很简单,在这就不说了。

runloop:

runloop是什么的东西,runloop就好比一个特务,接到任务就去处理,没任务就休假,等待任务,任务有很多类型的。相对于线程,做完一个任务就挂掉了,runloop还是有很大的优势的,先看看runloop的大概流程图:


图1

上图的2-9流程就是这个”特务“的处理任务,休假,接到任务作处理,处理完后再休假(等待任务)的一个循环,我说了这么多,跟标题的内容有什么关系呢?关系是有的,本篇重点就在第6点里面了。

图中第1条的 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其 order 是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。

这个用我的话称为“外围释放池”的创建!

图中第6 Observer 监视了两个事件: BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。

图中第10 Observer 监视事件是exit(即讲退出runloop),其回调内会调用 _objc_autoreleasePoolpop() 释放自动释放池。

这个用我的话称为“外围析放池”的释放!

那么第6步的优先级低的释放池无论被创建和释放多少次也对“外围释放池”没影响了,也符合了这个逻辑思维了!

好了,上面的内容是对标题的内容作一个铺垫,那么我今天要深入说的就是第6步监听的回调的实现原理,也就是Autorelease的原理

各位,先想想这个“Autorelease对象什么时候被切底释放?”

答:当前作用域大括号结束时释放。答案真的是这样吗?也许手动添加Autorelease pool是这样的!

真正的答案是:在非手动添加Autorelease pool下,Autorelease对象是在当前runloop进入休眠等待前被释放的,为何会这样,接下来一一探究。

Autorelease内部原理

Autoreleasepool{} 编译后是这样的:

void * context = objc_autoreleasePoolPush();

{}// 内部要做的事情

objc_autoreleasePoolPush(context);

这两个方法是对AutoreleasePoolPage的一个简单封装,其核心就在这个类里面,AutoreleasePoolPage是一个C++实现的类;看看里面的内容


图2


1.AutoreleasePool并没有单独的结构,而是由若干个AutoreleasePoolPage以双向链表的形式组合而成(分别对应结构中的parent指针和child指针)。

2.AutoreleasePool是按线程一一对应的(结构中的thread指针指向当前线程)。

3.AutoreleasePoolPage每个对象会开辟4096字节内存(也就是虚拟内存一页的大小),除了上面的实例变量所占空间,剩下的空间全部用来储存autorelease对象的地址。

4.上面的id *next指针作为游标指向栈顶最新注册进来的autorelease对象的下一个位置。

5.一个AutoreleasePoolPage的空间被占满时,会新建一个AutoreleasePoolPage对象,连接链表(用结构中的parent指针和child指针分别指向旧的AutoreleasePoolPage和新的AutoreleasePoolPage),后来的autorelease对象在新的page加入。

当我们创建好注册进来的对象就变成这样


图3


当next 指针在栈顶的低一度内存时,当再注册一个对象进来时,那么系统就会创建一个新的AutoreleasePoolPage来保存对象,新的parent指针指向旧的AutoreleasePoolPage,形成链表的结构一样,保证释放和新建AutoreleasePool能有序进行。每向一个对象发送 autorelease 消息,就是把对象加入到next 指针处,next 指针向高内存偏移一个位置(位置大小取决于新添加进来的对象内存的大小) 这是自动释放对象add进来的大概原理。

释放原理

好了,自动释放对象加进来的大概原理我们清楚了,那么反过来,对象释放的原理也差不多了。。。 慢着,释放时究竟我要释放到那个位置呢?凭借什么去标记呢?

内涵在于每当objc_autoreleasePoolPush被执行时,runtime会向当前AutoreleasePoolPage加入一个标记,称为“哨兵指针”,(我很喜欢这个名字)这个哨兵指针内容是nil,看图:


图4


AutoreleasePoolPop主要做一下事情:

1.AutoreleasePoolPop要传入的参数就是这个哨兵指针,首先找到这个哨兵指针所在的page。

2.在next指针和哨兵指针之间的对象都发送一次 release消息,并把next指针移动上一次设立哨兵指针的位置,(由于page结构类似于链表,所有这一步可以靠parent指针跨越多个page的对象作处理)

以上就是Autorelease内部的实现原理。

我形象点说就是page就是一个气泵,next指针就是那个活动的活塞,哨兵指针地址就是那个顶点,当我释放对象时就等于我往里面压缩,把活动的活塞推到顶点,里面的空气就是add进来的对象,经过活动的活塞的顶点(next指针)向活塞推到顶点(哨兵指针位置)靠近时,空气(对象被释放)被排出,当活动的活塞(next指针)被退回到活塞顶点(哨兵指针位置),两者之间的空气被排出去了(对象被释放了)这时候就不在推了,这时候当再有对象add进来时就像有空气吹进来,把活动的活塞的顶点(next指针)和活塞推到顶点(哨兵指针位置)一点一点隔开,一到AutoreleasePoolPop时又以“推活塞“的形式去释放对象了。

好了,总结上下问所说的内容:当一个runloop在不停的循环工作,那么runloop每一次循环必定会经过BeforeWaiting(准备进入休眠):而去BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池,那么这两个方法来销毁要释放的对象,所以我们根本不需要担心Autorelease的内存管理问题,这就是ARC背后的“高人”。


最后,文章一些内容引用到一些大神级别的博客的内容,如有侵权,请及时联系。

由于水平有限,仅供大家学习 (大神请无视)如有错误,请指正!

最后的最后贴上一些更深入了解的博客地址:

黑幕背后的Autorelease - CocoaChina_让移动开发更简单

深入理解RunLoop - CocoaChina_让移动开发更简单

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,783评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,360评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,942评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,507评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,324评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,299评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,685评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,358评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,652评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,704评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,465评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,318评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,711评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,991评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,265评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,661评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,864评论 2 335

推荐阅读更多精彩内容