最近在梳理基础知识,前两天根据自己的理解,以及参考同行大家的文章,整理了一下autorelease 的实现原理,自己的东西没有,大家别见笑,具体内容如下:
AutoreleasePool(自动释放池)是OC中的一种内存自动回收机制,它可以延迟加入AutoreleasePool中的变量release的时机。在正常情况下,创建的变量会在超出其作用域的时候release,但是如果将变量加入AutoreleasePool,那么release将延迟执行。
分别有三种情况:
for (item in BigSet){
@autoreleasepool {
//create large mem objects
}
}
还有就是enumerateObjectsUsingBlock
NSDictionary,NSArray通过这个枚举方法遍历的时候,其实内部已经实现了autorelease操作。
https://blog.csdn.net/zcmuczx/article/details/80034923
ARC中release/retain等方法不能使用,但ARC 会帮你在合适的地方插入这些方法。这将导致内存的延迟释放。autoreleasepool是为了 autorelease 这个方法,在对象的创建者没法销毁对象的时候,可以使用autorelease让autoreleasepool每隔一段时间检查该对象的引用计数,如果为0则释放对象。那么多个autoreleasepool的作用就是增加这种间隔,比原本autorelease释放的时间更早释放。
也就是说AutoreleasePool创建是在一个RunLoop事件开始之前(push),AutoreleasePool释放是在一个RunLoop事件即将结束之前(pop)。
AutoreleasePool里的Autorelease对象的加入是在RunLoop事件中,AutoreleasePool里的Autorelease对象的释放是在AutoreleasePool释放时。
具体如下图:
转载地址:https://www.jianshu.com/p/50bdd8438857
ARC下,我们使用@autoreleasepool{}来使用一个AutoreleasePool,随后编译器将其改写成下面的样子:
void *context = objc_autoreleasePoolPush();// {}中的代码objc_autoreleasePoolPop(context);
而这两个函数都是对AutoreleasePoolPage的简单封装,所以自动释放机制的核心就在于这个类。
AutoreleasePoolPage
AutoreleasePool并没有单独的结构,而是由若干个AutoreleasePoolPage以双向链表的形式组合而成(分别对应结构中的parent指针和child指针)。
AutoreleasePool是按线程一一对应的(结构中的thread指针指向当前线程)。
AutoreleasePoolPage每个对象会开辟4096字节内存(也就是虚拟内存一页的大小),除了上面的实例变量所占空间,剩下的空间全部用来储存autorelease对象的地址。
上面的id *next指针作为游标指向栈顶最新add进来的autorelease对象的下一个位置。
一个AutoreleasePoolPage的空间被占满时,会新建一个AutoreleasePoolPage对象,连接链表,后来的autorelease对象在新的page加入。
所以,若当前线程中只有一个AutoreleasePoolPage对象,并记录了很多autorelease对象地址时,内存如下图:
内存情况
上图中的情况,这一页再加入一个autorelease对象就要满了(也就是next指针马上指向栈顶),这时就要执行上面说的操作,建立下一页page对象,与这一页链表连接完成后,新page的next指针被初始化在栈底(begin的位置),然后继续向栈顶添加新对象。
所以,向一个对象发送- autorelease消息,就是将这个对象加入到当前AutoreleasePoolPage的栈顶next指针指向的位置。
释放时刻
每当进行一次objc_autoreleasePoolPush调用时,runtime向当前的AutoreleasePoolPage中add进一个哨兵对象,值为0(也就是个nil),那么这一个page就变成了下面的样子:
objc_autoreleasePoolPush的返回值正是这个哨兵对象的地址,被objc_autoreleasePoolPop(哨兵对象)作为入参,于是:
1、根据传入的哨兵对象地址找到哨兵对象所处的page。
2、在当前page中,将晚于哨兵对象插入的所有autorelease对象都发送一次- release消息,并向回移动next指针到正确位置。
3、补充2:从最新加入的对象一直向前清理,可以向前跨越若干个page,直到哨兵所在的page,刚才的objc_autoreleasePoolPop执行后,最终变成下面的样子:
嵌套的AutoreleasePool
知道了上面的原理,嵌套的AutoreleasePool就非常简单了,pop的时候总会释放到上次push的位置为止,多层的pool就是多个哨兵对象而已,就像剥洋葱一样,每次一层,互不影响。
转载地址:https://blog.csdn.net/linfengwenyou/article/details/47054205
另外通过梳理知识,也产生了一些疑问:OC中变量延迟释放 的情形有哪些 后续会进一步的梳理。关于双链表的各种操作也要练习一下。
更多优质文章,可以微信扫码关注: